Orange Deportation Simulator (pwn)
MAKE FLAGS GREAT AGAIN! HELP ME DEPORT THE ORANGES!
checksec:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3ff000)
RUNPATH: b'.'
Stripped: NoHere's the code of the program:
Scanning through the program, we realise that it allows us to:
Malloc a chunk of any size we want only once
Check any bit of our malloc'd chunk to see if it is set
Perform an OR operation on any bit of the malloc'd chunk
Essentially, the seat number we provide is represented as:
(byte offset * 8) + bit_index
Where the byte offset is the byte we want to read/OR relative to the start address of the chunk, and bit_index is the bit within that byte that we want to read/OR.
The key vulnerability lies in the fact that we can provide an arbitrarily large size value, so large that when we do malloc(size), it returns a null pointer (0) due to it being unable to fulfill the request. This results in us having bus = 0. Inputting a negative number like -8 will achieve this.
When bus = 0 , and size is extremely large, notice that the read and OR functionality provided by the program essentially allows us to read and OR any bit we want in the program.
Now the only address we know is the binary's address since PIE is disabled. Therefore our goal is to call win using the binary as a starting point. Remember that we do not have an arbitrary write primitive, but we have arbitrary read and arbitrary OR.
We can use our arbitrary read to read any entry in the binary's .got.plt section in order to get a libc leak.
How can we execute the win function?
I initially considered overwriting the exit functions, but this won't work since the program calls _exit() instead of exit(), so the exit functions aren't called.
Upon reading this article https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md it seems that printf custom conversion specifiers are the way to go, because the two things it relies on, namely:
__printf_function_tableand__printf_arginfo_table
initially have all their bits set to 0, so an arbitrary OR primitive will allow us to redirect execution to the win function.
How do we do this?
On reading the article, we learn that __printf_function_table must have a non-null value, and __printf_arginfo_table must have our function's address written in one of its slots corresponding to the conversion specifier we are using. (in this case we are using %lld . The l type modifier doesn't matter, and d has a hex value of 0x64. So we want to write the address of win to __printf_arginfo_table + 0x64 * 8 ). The 8 is because each address is 64 bits / 8 bytes.
But how do we find the offsets of __printf_function_table and __printf_arginfo_table in libc? When I tried to find them by doing p & __printf_function_table in GDB it cannot find the symbol. To find the offset, we have to look at the code and assembly of the register_printf_specifier function in libc:
And note that we actually have the symbol for the register_printf_specifier function:
With this we can look at the assembly of register_printf_specifier, and try to infer the offsets of __printf_function_table and __printf_arginfo_table.
Here's a small snippet of it:
With this we can find that the offset of __printf_function_table is 0x1d4980 and the offset of __printf_arginfo_table is 0x1d3890.
Now let's summarise the exploit:
Enter a negative number as size to obtain
bus = 0Obtain a libc leak by using the arbitrary read primitive on the binary's
.got.pltentryCalculate the addresses of
__printf_function_tableand__printf_arginfo_tableUse arbitrary write to make
__printf_function_tablea non-null value and set the content in__printf_arginfo_table + 0x64 * 8to be the address of thewinfunction.Trigger a printf again and execute the
winfunction
Below is my script:
Last updated