Article Index


Now that we have arbitrary read and write, there's one major thing left. We would like code execution. Taking into account the following things:


  1. We have a stack address leak
  2. Relative offsets on the stack will be deterministic
  3. Arbitrary Read
  4. Arbitrary Write


The game plan for execution will be to overwrite a return pointer. This can be accomplished in the following way:


  1. Leak stack address (as mentioned in previous page)
  2. Use debugger to determine correct address of stored return pointer
  3. Calculate relative offset from leaked address to stored return address


It turns out that, in my example, this was the stack address + 88. If you used different values you can directly calculate your own offset. After overwriting that, simply exiting from the program will trigger our code execution.


So finally, we have an arbitrary read, write, and ability to jump execution to wherever we want. The choice of what to do at this point is pretty open. We were given the libc that they are using. This is always handy and means that if we can leak a resolved address, we will immediately know the address of any function in libc.


Remember that this binary is FULL RELRO. What that means is that lazy linking of the functions is disabled, and all the addresses are linked up front. Further, the Global Offset Table (which stores these pointers) is made read-only. This is why we aren't just overwriting a GOT entry. However, we can absolutely still read the GOT entry to leak the address of libc. From there, we use the known libc binary to calculate the offset to what function we want. We have now fully derandomized libc as well.


I chose to use "system()" as my function call of choice to execute. However, given the prctl stuff up front, I was not able to call /bin/sh directly. Not a huge deal, because I can still call "ls", "cat" and others. To do this, I need to use a pointer to the command I want to run. Given we have a large globally read/write area available, and an arbitrary write, this is strait forward.


My end exploit worked something like this:


  1. leak the stack address by leaving a generic message
  2. reset the global index using -8 to allow arbitrary read/write
  3. calculate address of the return pointer on the stack using the leaked address
  4. leak the address of printf using the GOT (it could have been any function, i just chose this one)
  5. calculate offset of system() given what we now know about printf and their compiled binary
  6. write the command i want to run to a globally (static) read write address (i chose 0x602100)
  7. create my ROP chain to load this address into rdi then call the system() function I calculated
  8. use arbitrary write to write this rop chain over the return pointer on the stack
  9. "exit" the program to execute my code and get the results


Flag: bkp{you are a talented and ambitious hacker}


The full script can be found here: