hunting-field
tjctf
Task: text-based game with array pointer that decrements without bounds checking, allowing writes below the buffer into adjacent stack variable killCt. Solution: send 32 invalid inputs to exhaust the buffer, then overwrite killCt with the magic value 0x68756E74 ('hunt') via carefully ordered byte writes, then trigger game_over.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
hunting-field — TJCTF 2026
Description
Take up your arms, and slay your enemies!
A text-based combat game on a 9×9 grid. The player (@) moves and attacks enemies (E) that spawn and chase the player. The binary is a dynamically linked, not stripped ELF 64-bit x86-64 executable with source code provided. The game_over() function prints the flag if the kill count equals the magic value 1752526452.
Analysis
The game loop reads 2-character inputs (action + direction). Invalid inputs are logged into input_log[64] via a pointer array_ptr that starts at &input_log[63] and decrements by 1 for each character written, with no bounds checking:
char input_log[64]; int killCt = 0; int *kills = &killCt; char *array_ptr = &input_log[63]; while ((!strchr("MA", player_input[0])) || (!strchr("NESW", player_input[1]))) { scanf("%c", &player_input[0]); scanf("%c", &player_input[1]); int c; while ((c = getchar()) != '\n' && c != EOF); *array_ptr = player_input[0]; array_ptr -= sizeof(player_input[0]); // decrement, no bounds check! *array_ptr = player_input[1]; array_ptr -= sizeof(player_input[1]); }
Each invalid input writes 2 bytes and decrements array_ptr by 2. After 32 invalid inputs (64 bytes), the pointer has moved past the beginning of input_log and into adjacent stack variables.
Stack Layout (from disassembly)
rbp-0x41 to rbp-0x80: input_log[64] (array_ptr starts at rbp-0x41 = input_log[63])
rbp-0x81 to rbp-0x84: killCt (4-byte int, little-endian)
rbp-0x85 to rbp-0x86: player_input[2]
Win Condition
void game_over(int *kills) { if (*kills == 1752526452) { // reads and prints flag.txt } }
The magic value 1752526452 = 0x68756E74 is ASCII for "hunt" — a wordplay hint from the challenge name "hunting-field".
In little-endian byte order on the stack:
rbp-0x84(LSB) =0x74='t'rbp-0x83=0x6E='n'rbp-0x82=0x75='u'rbp-0x81(MSB) =0x68='h'
Solution
...
$ grep --similar
Similar writeups
- [pwn][free]Getting Started— hackthebox
- [pwn][Pro]0xLEET— hackerlab
- [pwn][Pro]hxp 39C3 CTF - brainfast— hxp_39c3
- [pwn][Pro]Taste— grodno_new_year_2026
- [pwn][Pro]New year party— grodno_new_year_2026