← Back to Writeups
HTBN/AReversing

Cat Simulator

XESXOR8/23/20264 min read
#reversing#htb#n/a

Cat Simulator

Platform: Broncoctf2026 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-11 | Status: Solved Techniques: decoy_elimination, dynamic_verification, state_predicate_recovery, static_disassembly

Summary

Task: cross-platform cat-simulator choice game (Windows/macOS/Linux) where a 5-day branching route reveals the flag; a plaintext decoy bonco{almost_there} is planted to mislead string grepping. Solution: reversed the Mach-O ARM64 finale to recover the win predicates (3 talks, 1 scratch, 1 eat, talk-message length sum == 32, score == 45, no invalid choices), then ran choices 1,1,1,2,3 to print the real flag.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: broncoctf2026 | ID: 20260711_broncoctf2026_cat_simulator
  • Tags: state_machine, decoy_flag, arm64, ctfd_download, macho, branching_game
  • Indicators: plaintext decoy flag bonco{almost_there}, 5-day branching choice game, cross-platform standalone binaries, flag gated behind score/count/length state check, CTFd signed URL returns 403 without browser User-Agent/Referer
  • Source: 20260711_broncoctf2026_cat_simulator.md

Foothold

Vulnerability / Misconfiguration

  1. Decoy_elimination
  2. Dynamic_verification
  3. State_predicate_recovery
  4. Static_disassembly
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • decoy_elimination
  • dynamic_verification
  • state_predicate_recovery
  • static_disassembly
  • Tags: state_machine, decoy_flag, arm64, ctfd_download, macho, branching_game

Original Writeup

<details><summary>Click to expand original content</summary>

Description

Make the purrfect choices over 5 days to win your owner's heart… and maybe something more? Meow carefully.

Three standalone platform binaries are provided (Windows .exe, macOS ARM64 Mach-O, Linux ELF) of a cat-simulator branching-choice game. The player makes menu choices over 5 in-game days; the winning route reveals the flag. The macOS ARM64 Mach-O (cat-sim-mac, 34,008 bytes) was the binary actually analyzed.

Recon

  • The challenge ships the same game as three self-contained executables — a classic sign of a cross-platform build with the win logic embedded in native code rather than in packaged asset files.
  • CTFBase semantic search returned only a generic visual-novel XOR precedent (20260115_duckerz_novella) — no direct match, so this had to be reversed from scratch.
  • CTFd download tip: the CTFd signed download URLs returned HTTP 403 with plain curl. Adding a browser User-Agent and a Referer (https://broncoctf.ctfd.io/) made the download succeed:
curl -L -o cat-sim-mac \
  -A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' \
  -H 'Referer: https://broncoctf.ctfd.io/' \
  '<signed-download-url>'

Analysis

Running strings on the Mach-O immediately reveals a plaintext flag-shaped string:

bonco{almost_there}

This is a decoy (note the misspelled bonco{...}, not bronco{...}). It is the branch reached when only one of the several win conditions is met, planted specifically to catch players who grep for {.

Disassembling the game loop and finale (main/check routine at 0x100000500, finale checks starting at 0x100000780) shows the game is a small state machine. Each day accepts a menu choice that mutates several counters:

ChoiceMeaningscorehidden moodside effect
1talk+25+7talk count +1, add trimmed talk-message length
2scratch-50-12scratch count +1
3eat+20+2eat count +1
otherinvalidinvalid count +1

The finale gate checks a conjunction of predicates (order irrelevant):

  • invalid count == 0
  • talk count == 3
  • scratch count == 1
  • eat count == 1
  • final score == 45 (3*25 - 50 + 20 = 45)
  • hidden mood >= 1 (deterministically 21 for these counts: 3*7 - 12 + 2 = 11… mood accumulates to a positive value)
  • sum of the three talk-message lengths == 32

The visible bonco{almost_there} decoy is emitted when only the talk-length sum condition (== 32) is satisfied but the count/score predicates fail. The real flag branch hashes the deterministic hidden-mood value and applies vector XOR/bit-shuffle constants from __TEXT, so the flag is not a plaintext literal — it is reconstructed at runtime only on the correct path.

Solution

Satisfy every predicate simultaneously:

  1. Make exactly 3 talks, 1 scratch, 1 eat over the 5 days — the choice sequence 1, 1, 1, 2, 3.
  2. Make the three talk messages total exactly 32 characters. Message lengths 10 + 10 + 12 = 32 work.
  3. Never enter an invalid choice.

This yields score 45, positive hidden mood, and the length sum 32, tripping the real flag branch.

#!/usr/bin/env bash
# choices: 1,1,1,2,3  (talk, talk, talk, scratch, eat)
# talk messages sized 10,10,12 -> total 32 chars
printf '1\nabcdefghij\n1\nabcdefghij\n1\nabcdefghijkl\n2\n3\n' | ./cat-sim-mac

Dynamic run prints:

bronco{REDACTED}
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR