Try the challenge thoroughly before reading the write-up. Otherwise, it's your loss.
We ssh to [email protected] and authenticate using the password we had obtained after solving the previous level.
Recap of previous level.
The recap has no explaination, just the solution.
I’m writing this write-up the night before my exam, because I started solving gracker level13 as an excuse for not having to study, and still making my messed up brain think that I’m doing productive work. I’m a bit wierd that way. Now that I’ve solved the level after much much effort, I can’t resist jotting all my thoughts down right now. Exams be damned!
So, the challenge source code looks like:
There are sooo many vulnerabilities that I could spot on the first glance!
strcpy without length check
printf without format specifier
use after free
At least the binary was NX/DEP enabled. But, such over the top protections can’t do much if the underlying logic of the program is vulnerable.
Let’s just walk through what’s happening in the program.
First a, b, c chunks are allocated sequentially on the heap.
b chunk is freed
argv is copied to a without bound checks
The msg string is copied over to the c chunk
d chunk is allocated
argv is copied over to d
the b chunk’s contents are copied to a copy variable
printf prints c, without format specifiers
_exit(0) is called
Some good resources to know more about how heap stuff works:
When argv is copied over to the a chunk, I’ll attempt to corrupt the heap metadata, such that the processor thinks that the wilderness (the unoccupied part of the heap) is right after the a chunk.
This way, when d is allocated, it will be allocated over the same memory of the already allocated chunks b(freed) and c(not freed!)
This is what the heap looks like after d is allocated and argv is copied over to it:
If I don’t corrupt the heap metadata:
2.If I do corrupt the heap metadata:
Now, since we control the value pointed to by c, we control what printf outputs!
Here’s the PoC:
What??? Why didn’t it print my string?
It’s because it wasn’t terminated by a newline character! I thought it might be a wierd form of a blind format string attack. I spent hours on end trying different payloads to pass onto printf, guessing the offset of where my input string might be! But nothing worked.
So, I decided to eliminate this blindness, by passing a newline character. This is a tricky thing, passing newline character into the argv. But, stackoverflow to the rescue!
So, now it was no longer a blind attack.
This makes things a lot easier!
So, I started trying to figure out the location where my string was located.
And I made a stupid mistake! The parameter to printf was the pointer to the string that I controlled, not the string itself! So, when %40$s gave me my string, I started working without thinking on a payload to overwrite the GOT entry of the _exit() function which was called later. Of course it didn’t work, and when I debugged the issue by going into the intricacies of printf function, and trying to understand the instructions where it was segfaulting, it became clear to me.
I figured that I could actually have the address which I want to overwrite in the payload before my format string.
Btw, the address 0x80488b0 is the GOT entry of the _exit(0) function which I intended to overwrite.
Now, the question was, what to overwrite it with?
A few days ago, I’d watched this live stream of the 33c3 CTF challenge: babyfengshui
Near the end liveoverflow had used a Oneshot /bin/sh. So, I figured I too could overwrite the GOT entry of _exit(0) with one such oneshot gadget, and be done with it. Well, it didn’t work out at all!
First of all, writing the format string each time while debugging, for different addresses on the gracker machine’s libc took a long time. Even when I successfully did redirect code execution to one such gadget, it segfaulted!
I came across this blog which mentioned the preconditions which must be met for these gadgets to work, and they weren’t being met in this case. I’d independently also noticed this to be the reason why it was segfaulting . I spent hours on this!
So, where else could I redirect code execution to? Some shellcode! So, I hurriedly placed some shellcode into one of the args and carefully wrote out the format string payload. Then, when it too segfaulted, it dawned on me, that the heap is not executable!!! Silly me!
What could I do…what could I do?…
And then it hit me! I could redirect code execution to some other function in the code itself, whoose argument I could control! So, I would need to overwrite the GOT entry for _exit() to that function (I chose free(b) being called earlier), overwrite the GOT entry for that function to point to system(), and place ‘/bin/sh’ in the b heap chunk.
0x080499b0 -> 0x0804861e
0x080499b4 -> 0xf7e66360
/bin/sh (Terminated by newline) -> B chunk on the heap
So, I did this and it worked!!!
Heaps of Trouble! indeed!
So, the gracker wargame is finally over!!!
Overall it has been a great experience hacking on this wargame. I was a complete n00b when I started, but I have improved by a LOT by the end. I would definitely recommend others to attempt this wargame.