Vnote
off one byte overflow to stack pivot gaining ret2execve
Problem
Description
cpP enjoyer
nc 103.181.183.216 17002
Proof of Concept
discovering foothold
given compiled binary, we'll check its basic executable information and security perimeter
next, after interacting with the binary a little bit, I immediately jump into ghidra.
judging from the syntax, I suspect this binary is written with C++. The decompiled main suggest what the binary does on a normal routine, prompting the user with a single input. Let's take a look how the program is accepting our input
aha!, here lies a single byte overflow, this happens because of the incorrect check of size < 0
. But, what can we do with this? let's break on get_input() before it returns on a normal execution.
as we can observe from the stack, beneath our input exist bunch of instruction pointers. This has been made easy to notice since PIE is not enabled thankfully. Now let's us observe the stack if malicious input was given instead.
notice the one byte overflow?
before:
0x7fffffffdc60 -> 0x4c3690 (__preinit_array_start+32) -> 0x401948 (Note::print_public())
after:
0x7fffffffdc60 -> 0x4c3641
since we screamed at the program using bunch of 'A's, the last of byte of that pointer is now overwritten with 0x41 (ascii for A). We can use this to alter the program execution. However since we can only write one byte, our option is quite limited. So where should we return?
gaining foothold
Let's take a look how Note is initialized (double click on the constructor function).
It takes one parameter and set to a variable pointer to a function of print_public(). Notice we also discover another function called get_private() that's never called on a normal routine. Let's take a look what it does.
seems like it prompt the user with quite a lot of input size, this is good news since it is probable to buffer overflows. Its taking the object relative address as the destination of its buffer. Specifically the object's structure at index 2 (offset 0x10) for the larger buffer and the object's structure at index one (offset 0x8) for the smaller buffer. We can figure out what these addresses shall be if we were going to call it by taking a look back at the main function.
so the get_private() will prompt an input of size 0x200 to a private_buffer (a global variable), and an input of size 0x60 to input (local variable) thus voila, a buffer overflow and since the address of this function has only one byte difference with the existing one on the stack, this is the perfect candidate.
and we successfully alter the program execution
Stack pivoting and ret2execve
Since the program is compiled statically, we have a lot of gadgets at our disposal and it will be our goal to gain remote code execution. However, our buffer won't be large enough to accommodate our large payload, so we need to do a stack pivoting, essentially we'll move the stack pointer to an area where we can put a larger buffer.
Thankfully, the author has already provided this on get_private(). What we'll have to do is to put our ropchain on private_buffer and move the stack pointer there. To do this we'll use the pop rsp gadget. this works because each the rip depends on the rsp to feed instructions from. and since we alter the rsp to our controlled input, the rip will then be fed those ropchain.
Flag
hacktoday{m1Nt4_R3k0m3nD4s1_fL4g_saM4_ch4tgPt_d14_m4Lah_b1ngunG_y4_Sud4h_L4h_1ni_4ja}
Appendix
Last updated