392 words
2 minutes
CodeVinci CTF 2026 - CivilWar - Binary Exploitation Writeup

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 CivilWar
CivilWar: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), for OpenBSD, statically linked, no section header

The 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

tableflip

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_writeup
from 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 0f84b0000000
chmod +x CivilWar_patched_for_writeup
printf 'Cr34t0r\n1581004908\n' | ./CivilWar_patched_for_writeup
WINNER! WINNER! WINNER!
SECRET UNLOCKED: CodeVinci{cosmopolitan_library_is_so_fun}

smug

Solution#

cp CivilWar CivilWar_patched_for_writeup
python patch_civilwar.py
chmod +x CivilWar_patched_for_writeup
printf 'Cr34t0r\n1581004908\n' | ./CivilWar_patched_for_writeup
orig6 0f85b0000000
new6 0f84b0000000
WINNER! WINNER! WINNER!
SECRET UNLOCKED: CodeVinci{cosmopolitan_library_is_so_fun}
CodeVinci CTF 2026 - CivilWar - Binary Exploitation Writeup
https://blog.rei.my.id/posts/86/codevinci-ctf-2026-civilwar-binary-exploitation-writeup/
Author
Reidho Satria
Published at
2026-03-10
License
CC BY-NC-SA 4.0