The task is simple: We have an input which as the name suggests is standard input for the given native program. CTF challenge also provides to download binary with the name “vulnerable”. Tried several inputs with wildcards as well but nothing has happened. Let’s dive in.
The first thing is obviously to search for the hardcoded strings. Running command
strings vulnerable |grep flagin the terminal show the only string “print_flags”.
After checking the file I found that its 64-bit ELF format, dynamically linked and fortunately not stripped. The key part here its LSB type. It defines endianness — big-endian vs little-endian. So the target computer for which this binary is intended is little-endian. This will be helpful later.
“LSB” here stands for “least-significant byte” (first), as opposed to “MSB”, “most-significant byte”. It means that the binary is little-endian.
A big-endian system stores the most significant byte of a word at the smallest memory address and the least significant byte at the largest. A little-endian system, in contrast, stores the least-significant byte at the smallest address. Endianness may also be used to describe the order in which the bits are transmitted over a communication channel.
Locating the main function was easy. I change variable names and retyped them provides readability. Fairly easy program but the interesting parts are, obvious “error”, memory buffer initializes with fixed size and input is restricted size, besides this, there is the additional custom function
read_all_stdin instead of
gets . This is the indication we can do a buffer overflow.
Immediately after realizing this, I tried long string more that resulted in SegFault 😈
read_all_stdin reads the input step-by-step and the
print_flags function expects FLAGS from the environment.
But the interesting fact is print_flags function has no references and is never called. Hmm?
It means we should find a way using a buffer overflow to somehow jump to the memory address of print_flags function which is located at
But because it’s a 64-bit architecture, memory addresses are 64 bits long which is 8 octets
0x004006ee should be 0x00000000004006ee as a full address. We need to craft payload and inject this as an input to corrupt the memory.
After testing appears that after 40 bytes it fails with a segmentation fault. Therefore first 40 bytes need to be some dummy string. To craft the correct length string this is an awesome tool: https://mothereff.in/byte-counter and to pass it as an URL the following reference sheet is useful https://www.eso.org/~ndelmott/url_encode.html
Basically idea is to overflow buffer with the dummy string and the memory address which overwrites the return address. Keep in mind that it’s little-endian so we have to reverse the bytes and the final payload looks like this:
// hence % signs, because we pass it via URL it should be encoded
You may wonder why the program uses a custom function for input scanning instead of standard build-in one? Well, the standard function terminates on null (\0) value immediately. https://www.tutorialspoint.com/cprogramming/c_strings.htm
What did I learn?
Almost everything. I had a very small experience with a native code environment and everything was new and amazing for me. Starting from the c program architecture ending with ELF and LSB/MSB.
super useful resource on binary exploits: https://bitvijays.github.io/LFC-BinaryExploitation.html
I write those blogs for future me to estimate the progress and share the knowledge for the newcomers.