Category: Binary Exploitation
Flag: CodeVinci{cosmopolitan_library_is_so_fun}
Challenge Description
it’s your problem to understand what I mean by civil war
Analysis
The binary immediately felt like a trap because it didn’t behave like a plain, ordinary ELF in early inspection, and that mattered because wrapper-style runtimes can hide the real logic path behind mode flags.
file CivilWarCivilWar: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), for OpenBSD, statically linked, no section headerThe mitigation profile showed a pwn-friendly layout despite NX: no PIE and no canary, which often means branch control and code-path abuse are enough even without a classic shellcode route.
checksec --file=./CivilWar[*] './CivilWar'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)A quick strings sweep exposed two personalities in the same program: a debug checksum mode and an arcade-style validation mode with a visible winner message.
strings -tx CivilWar | rg "DEBUG_MODE|PLAYER 1 NAME|ENTER CHEAT CODE|WINNER|BAD CHECKSUM|Cr34t0r" 1c8ef Cr34t0r
46127 [DEBUG_MODE]: Enter Developer ID to extract Checksum:
46285 [0;35mPLAYER 1 NAME:
462a0 [0;35mENTER CHEAT CODE:
462d5 BAD CHECKSUM. SYSTEM HALTED.
46335 WINNER! WINNER! WINNER!Running it normally with the special-looking name Cr34t0r gave a deterministic hash and decimal cheat code, but then the program exited instead of asking for the player/cheat pair from the hidden branch. That was the exact rabbit-hole moment.
printf 'Cr34t0r\n' | ./CivilWar> SYSTEM BOOT... OK
> RAM CHECK... 64KB OK
> DEV_KIT_V1.0 DETECTED.
> WARNING: ROM INTEGRITY FAIL.
[DEBUG_MODE]: Enter Developer ID to extract Checksum:
> ALERT: ORIGINAL DEVELOPER SIGNATURE RECOGNIZED.
> UNLOCKING MASTER CHEAT CODE.
> CALCULATING HASH...
> 0x5E3C386C... DONE.
-----------------------------------------
> CHEAT CODE: 1581004908
The solving pivot was to force the hidden validation path by flipping one conditional branch in a copied binary. The byte pair 0f85 (JNE) at the mode gate was changed to 0f84 (JE), which inverted the branch decision and unlocked the player-name/cheat-code flow. I patched only the copy, then fed the valid pair (Cr34t0r, 1581004908) and got the secret line with the real flag.
cp CivilWar CivilWar_patched_for_writeupfrom pathlib import Path
target = Path("CivilWar_patched_for_writeup")
data = bytearray(target.read_bytes())
offset = 0x11978
print("orig6", data[offset:offset + 6].hex())
# Invert JNE (0F 85) -> JE (0F 84)
data[offset + 1] = 0x84
target.write_bytes(data)
print("new6", data[offset:offset + 6].hex())orig6 0f85b0000000
new6 0f84b0000000chmod +x CivilWar_patched_for_writeup
printf 'Cr34t0r\n1581004908\n' | ./CivilWar_patched_for_writeupWINNER! WINNER! WINNER!
SECRET UNLOCKED: CodeVinci{cosmopolitan_library_is_so_fun}
Solution
cp CivilWar CivilWar_patched_for_writeup
python patch_civilwar.py
chmod +x CivilWar_patched_for_writeup
printf 'Cr34t0r\n1581004908\n' | ./CivilWar_patched_for_writeuporig6 0f85b0000000
new6 0f84b0000000
WINNER! WINNER! WINNER!
SECRET UNLOCKED: CodeVinci{cosmopolitan_library_is_so_fun}