So! We got passed level1 and now have a basic shell. Whats next?! In the home directory of level1 we find two files: level2 and level2_readme.txt. The readme files tells us to run level2 with the command “socat TCP4-listen:53121,reuseaddr,fork EXEC:./level2” and connect to it using something like netcat. When connecting to it, we discover it’s some kind of note manager. We can store up to 10 notes and have some commands available to create/ write/ read and delete a note. We start fuzzing the program, creating multiple notes, setting, showing and freeing them and eventually we run into a crash!
Looks like we overwrote a pointer somewhere! We have control over EDX and ECX and can thus patch 4 bytes! I started reversing the binary and discovered that the program allocated 64 bytes for the note data but 128 bytes is read from the input and copied.. Bad!
As the notes are allocated right after each other we can assume that we are overwriting the data pointer of note 2, causing the crash, and giving us the change to patch 4 bytes.. Also note that the mprotect call in the note creation function makes the memory executable.. What a coincidence :). Time to get our hands dirty. As we can patch 4 bytes, and we can store stuff in executable memory what better to do a GOT overwrite and with the help of a single ROP gadget like pop, return we can execute our payload inside the note data. I started looking at used functions and came soon to the conclusion that free would be a nice candidate. So our simplified plan is:
- Create note 1 and 2
- Set note 1 content: size 80, with payload and the address of the free GOT entry to patch at the end
- Set note 2 with the value to patch the address we put at the end of note 1 (We set it to an address of a ROP gadget we found: pop ebp; return;)
- We free note 1 with the del command, triggering free(note->dataPtr), executing our gadget which returns into the payload. Boom!
Enough talking! Drop that shell already!
And beneath is the exploit I constructed. As you can see I made my own payload, msf payloads are so boring, build our own! I calculate the address of execve based on the address of puts and the delta between those functions (ASLR was on). I copy “/bin/sh” into the bss section, and eventually call execve(“/bin/sh”, NULL, NULL) 🙂