neven@dimitri-dev:~$
cd ../hacknite-2021

$cat secret-location.mdPwnHacknite 2.0

Secret Location

The program reads input with no length limit, so a long enough answer overruns its buffer and overwrites the secret code it checks against.

  • #buffer-overflow
  • #memory-safety
  • #c
  • #little-endian

$man secret-location

What this one teaches:

  • How local variables sit next to each other in memory, and what a buffer overflow is.
  • Why the C function gets() is unsafe, since it never checks how much it reads.
  • Writing a number into memory in little-endian byte order.

$hint --one-at-a-time

Stuck? Open one hint, then go back and try again before reading the next.

Hint 1 of 2show

Look at how the input is read. Is there anything stopping you from typing more than fits?

Hint 2 of 2show

Right after the input buffer sits the value the program compares. What if your input reaches it?

Full solution below. Spoilers ahead.

Task description

Ante is looking for a venue for his birthday, for over 30 guests, but the location is hidden. Can you help him find it so he can book it?

The original was in Croatian; this is my translation.

The program

We’re given the C source. The important parts: it keeps the answer in a structure that holds a 56-character text field followed by a 4-byte number, reads your input, and then checks that number against a secret code.

#define TOCAN_KOD 0x476b2336        // the correct code

struct odgovor {                    // "answer"
    char odgovor[56];               // the text you type
    uint32_t tajni_kod;             // "secret code", sits right after the text
};
// ...
gets(krivi_odgovor.odgovor);        // read input into the 56-char field
if (krivi_odgovor.tajni_kod == TOCAN_KOD) {   // compare the number after it
    ispisi_tajne_podatke();         // print the secret (the flag)
}

The program sets this structure’s tajni_kod to a wrong value on purpose, so the check normally fails.

Why this is breakable

Two facts combine. First, the 56-character field and the 4-byte number sit right next to each other in memory, in that order. Second, the input is read with gets, a C function that reads a line of text with no limit on how much it takes. It keeps writing wherever it’s pointed, even past the end of the 56-character field.

So if you type more than 56 characters, the extra characters spill past the text field and land on the tajni_kod number just after it. This is a buffer overflow: writing past the end of a buffer and into whatever follows. gets is so unsafe for exactly this reason that it was later removed from the C standard.

Building the input

The plan is 56 characters to fill the text field exactly, then four more bytes that overwrite tajni_kod with the correct code, 0x476b2336.

Computers store a multi-byte number with its lowest byte first, an order called little-endian. So 0x476b2336 is written as the bytes 36 23 6b 47, which happen to be the characters 6#kG. In Python:

print("A" * 56 + "\x36\x23\x6b\x47")

That’s 56 As followed by 6#kG. Fed to the program, the As fill the text field and the last four bytes overwrite the secret code with the value the program wants to see. The check passes and it prints the flag.

$cat flag.txt revealCTF2021[323137855005]

Written by Neven Marić. This writeup was written with the help of Claude Code (Anthropic).