cheater_cheater
Challenge
Imported from local notes.md.
Solution
Original Notes
cheater_cheater
Challenge Summary
- Given: A downloaded Java archive,
starting_files/PacManForCTF.jar, for a Pac-Man clone called Hac-Man. - Goal: Beat the impossible high score and recover the flag.
- Constraints: Normal gameplay appears capped well below the displayed high score, so the intended path is to cheat or reverse the game logic.
Initial Recon / Triage
- Observations: The JAR only contains
SimplePacMan.classandJTextBasket.class, which keeps the reversing surface small. - File identification:
META-INF/MANIFEST.MFsetsMain-Class: SimplePacMan. - Entry points:
SimplePacMan.actionPerformedcontrols score progression, andSimplePacMan.paintComponenthandles the win/lose screens.
Hypotheses & Approach
- Hypothesis 1: The game hardcodes a losing condition that prevents the player from legitimately reaching the displayed high score.
- Hypothesis 2: The flag is embedded in the JAR and revealed through a hidden UI path once the score is forced into the winning state.
Execution Steps (Reproducible)
Stage 1
Commands:
cd /root/dawg2026CTF/cheater_cheater/starting_files
jar tf PacManForCTF.jar
unzip -p PacManForCTF.jar META-INF/MANIFEST.MF
javap -classpath PacManForCTF.jar -p -c SimplePacMan
javap -classpath PacManForCTF.jar -p -c JTextBasket
Results:
SimplePacMan.actionPerformedincrements score by 10 for each pellet.- If score reaches
64000, the game setsloser = true, so normal play is intentionally capped. - If score is
>= 6942069, the game setswinner = trueand clamps score to6942069. - In the win screen,
paintComponentsets the panel name to the score string and callsgetComponents()[0].revalidate(). JTextBasket.revalidate()uses the parent component name as input to derive an AES key and IV, decrypts a hardcoded Base64 ciphertext, and stores the plaintext in the hidden component name.
Stage 2
Commands:
cd /root/dawg2026CTF/cheater_cheater/artifacts
jshell -q recover_flag.jsh
Results:
- The winning score is the exact string
6942069. JTextBasket.revalidate()derives the decrypt material as(6942069 * 10 + 1)^4.- The decimal result is interpreted as hex bytes for the AES key, and its reversed decimal string is interpreted as hex bytes for the IV.
- Decrypting the embedded ciphertext reveals the flag:
DawgCTF{ch3at3R_ch34t3r_pumk1n_34t3r!}.
Artifacts Produced
artifacts/recover_flag.jsh: JShell script that reproduces the game's decryption logic and prints the flag.
Flag
DawgCTF{ch3at3R_ch34t3r_pumk1n_34t3r!}