← Back to Writeups
CUSTOMEasyReversing

Cat Simulator

XESXOR9/10/20263 min read
#reversing#custom#easy#decoy_elimination#dynamic_verification#state_predicate_recovery#static_disassembly#windows

Cat Simulator

Platform: broncoctf2026 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: Windows | Author: XESXOR | 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.

Cat Simulator — broncoctf2026

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.
  • CTF Writeup 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:
bash
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
otherinvalid——invalid 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.

bash
#!/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}