A good write-up is already available on sleepya's blog. He made an exploit bypassing any ASLR/NX using ROP.
However, NX was not enabled on the wargame machine... Organizers thought they did, but it was not effective :( Good for us it means only ASLR, and the binary was not even PIE. One could exploit it quickly by writing a shellcode in the GOT, let's see that.
Context
22 - hashcalc1 - 300 pts Description Category: pwnables Download nc a9.amalgamated.biz 30001download file
Analysis
Quickly reverse it and see that it's a network server using regular sockets.In the main function that handles clients connections, we see that the server opens a log file, and writes the user buffer into it using fprintf(fp, buf), which is an obvious format string vulnerability.
Using the format string vulnerability which gives us an arbitrary write, we would like to control the program flow to our shellcode. We can do that by overwriting the pointer of the next dynamic function being called: strlen. This pointer is located in the Global Offset Table (GOT).
strlen's GOT entry is at 0x0804A41C.
Using the format string with two write2 (%hn format specifier) we can overwrite the value at this address to point to our shellcode. You do not want to use a single write4 (%n) because it would require as many characters to be written to the log file.
However, our code is received in a buffer on the stack, which has ASLR, so we cannot easily return to it. No problem, we can just use a bunch of write2 to copy a shellcode in the GOT, for instance right after strlen.
Choosing a shellcode, socket reuse
Since it is a remote exploitation, we can either use a classic connect-back shellcode or reuse the socket and spawn a shell from there. Since the first solution can fail if there is an outbound firewall (but there wasn't), I chose the second one.In order to do a socket reuse, we need to get the socket file descriptor. Easy, it's incremental: 0 (stdin), 1 (stdout), 2 (stderr), 3 (log file), 4 (server socket) and 5 our client accept socket. You can also simply guess it by trying numbers incrementally.
Then, we need a shellcode to close 0/1/2 and reopen them as duplicates of the client socket file descriptor. Syscall dup2 does that, and a public shellcode doing it for fd 0 already exists. We just adapt it to get the following 17 bytes:
$ echo -ne '\x31\xc9\x31\xdb\xb3\x05\x6a\x3f\x58\xcd\x80\x41\x80\xf9\x03\x75\xf5' |ndisasm -u - 00000000 31C9 xor ecx,ecx # ecx (new fd) => 0 00000002 31DB xor ebx,ebx # ebx (old fd) => 0 00000004 B305 mov bl,0x5 # ebx = 5, our socket fd 00000006 6A3F push byte +0x3f 00000008 58 pop eax # eax (syscall number) = sys_dup2 00000009 CD80 int 0x80 # syscall! dup2(5,ecx) 0000000B 41 inc ecx # increment new fd so 0000000C 80F903 cmp cl,0x3 # we do this 0000000F 75F5 jnz 0x6 # for fd 0/1/2Finally, just append a regular /bin/sh shellcode (23 bytes).
Exploit the format string
In order to have an arbitrary write, we need the offset in the stack of our buffer. And since the format string happens in the log file, we don't get this information remotely. Just run it locally and view the log. Good thing here is that this offset will not move (fixed stack, recv into the buffer) and we don't need any padding unlike shell exploitation of format strings, where your arguments influence the stack size and thus the offset and padding.Then we just have to build our format string with 2 write2 to update strlen's GOT entry and multiple write2 to copy the shellcode in the GOT, right after strlen's GOT entry.
Full exploit here: colored syntax or plain .py
Just run it with nc for the network part, and cat to keep stdin opened:
$ { python exploit.py; cat; } | nc a9.amalgamated.biz 30001 ** Welcome to the online hash calculator ** $ id uid=1009(hashcalc1) gid=1010(hashcalc1) groups=1010(hashcalc1)Quick & reliable, but remember that this exploit would not have worked if NX had been present, unlike sleepya's and surely others.
No comments:
Post a Comment