PWN9 : CPP binary ROP chain

Hello guys, this is another write-up for a pwn challenge in CSAW 2020, this time it's a C++ compiled binary, let's go !


We were provided with the libc, libstdc and the binary, if you don't have them downloaded you can find them here : https://github.com/MaherAzzouzi/PWNing/tree/master/CSAW


File :

We're dealing with x64 binary, which is stripped as you can see (functions takes gibberish names which makes the binary hard to reverse)


Checksec :



* NO PIE : this is helpful to get some gadgets at fixed addresses


Binary :

This is what the binary is doing :

From this I guess that it's just a WWW (write what where) 1 char at a time.

And to be honest I didn't found the bug by reverse engineering, I just found it by testing some values until it crashes.

It looks that if we provide "d" we get some leaks that can be usefull to break ASLR here and there.

And here is the crash :

It looks like we overwrote the return pointer, that's interesting.


Reverse engineering : 

After loading the binary to Ghidra and see the available functions, I found a function that can be the "main":

The binary simply iterate 100 times, asking for 1 char and 2 integers and it puts the char you provided in the stack using those two integers, and if the char is "d" you can leak some addresses.

Let's press "d" and see what we got as leaks : 


It looks like we have addresses from libc let's take one and see if it's really a libc address from gdb:



Uh they are not libc addresses but libstdc (we were provided by libstdc.so) addresses which is cool too we have interesting gadgets for libstdc too.

Now we can calculate libstdc base address !


Analyzing the crash :

I got the crash by providing a char and then giving 0 and 121 as the indexes, and segmentation fault happened is the "d" option which leaks addresses, let's redo all of that inside gdb, but this now putting a breakpoint just before "d" returns :

Double click the last function :




Got the address of the return I want to put breakpoint in.






Stopped by breakpoint and the function is ready to return !

Let's see the stack and exactly the 8 bytes of rsp, (the return value)


Wow our char "M" 0x4d is written as the second bytes of the return value, which triggers the SISEGV


I will repeat the exact same steps, but now I'm changing 121 with 120 I'm expecting to overwrite the first byte with "M"

Yep as expected, and I have 100 iteration of the same steps I already done, so I can write whole ROP chain just like that and jump to it somehow.


Plan :

Now that I have libstdc leak, I can use it's gadgets, and I have 1 byte write everytime, so I can write those gadgets somewhere, and jump to them, for that I will write those gadgets just after the return pointer we were trying to overwrite earlier (with "M"), and just make that return pointer as "ret" so when executed it will go to our ROP chain which is exactly beyond it !

Exploit :

I defined those functions just to automate the task :

Here I'm just extracting a libstdc leak and calculating it's base address :


To extract gadgets I just used ropper like this : 


I searched to the other gadgets with the same manner just to execute execve("/bin/sh", 0, 0)

and I'm gonna use this gadget just to write "/bin/sh" in the bss :


Those are all the gadgets I'm gonna use : 


I'm using my function write_qword() to write these gadgets in the order I want just beyond the return pointer.

Writing these gadgets 1 byte at a time starting from 128 which is 120+8 to evade the return pointer, and I'm gonna overwrite the first byte of the return pointer with 0x52 which makes it just return.

This is the result of running our exploit :


The full exploit in my github repo.

If you have any question : Maher#7775

Comments

  1. Very cool! I wouldn't have been able to figure this out. I couldn't even figure out what the binary does.

    ReplyDelete
    Replies
    1. Here where the Fuzzer can help :)

      Delete
    2. I like how methodical you are in your explanation. I think you strike the right balance between over-complicating things, explaining every and each step, letting aside a whole part of the process that lets the reader alone in figuring out what you did(when you read a write-up, a lot of people seems to forget that you are mostly interesting in learning and understanding).
      You really manage to keep the right level of details IMO, even the way you name your variables.
      Great write-up, thanks for sharing !

      Delete
    3. Thanks a lot for your kind reply, I try to be as clear as possible, and I'm not making write-ups to show off my skills or something, all I want is to make the reader understand from 90% to 100% of what I'm saying and not just skip important things to save time or effort. Thanks again for appreciating my work.

      Delete

Post a Comment