from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import json
FLAG = ?
KEY = ?
def admin():
print("welcome admin")
print(f"Flag = {FLAG}")
def guest():
print("welcome guest")
def auth():
user = dict()
user["username"] = input("username : ")
user["password"] = input("password : ")
user["uid"] = int(user["username"] == "admin" and user["password"] == FLAG)
plaintext = json.dumps(user).encode()
padded = pad(plaintext, 16)
cipher = AES.new(KEY, AES.MODE_ECB)
try:
encrypted = cipher.encrypt(padded)
except ValueError as e:
return {"error": str(e)}
print(f"Here is your token : {encrypted.hex()}")
return user
def authToken():
token = input("Token : ")
token = bytes.fromhex(token)
cipher = AES.new(KEY, AES.MODE_ECB)
try:
plaintext = cipher.decrypt(token)
unpadded = unpad(plaintext, 16)
user = json.loads(unpadded)
except ValueError as e:
return {"error": str(e)}
return user
print("Welcome !")
print("Please login first.")
choice = input("How would you login ?\n1) Token\n2) Credentials\n\nChoice :")
if choice == "1":
user = authToken()
elif choice == "2":
user = auth()
else:
print("error... goodbye")
exit(0)
if "error" in user:
print(f"ERROR : {user['error']}")
exit(0)
elif user["uid"] == 1:
admin()
else:
guest()
The challenge objectif is to log as admin user.
Exploitation
When the user authenticates using their credentials, the application will send a token that can be reuse to directly access the app.
The token contain :
username : provided by the user
password : provided by the user
uid : set by the application depending if the provided credentials are admin or not.
Can the user forge an arbitrary block ?
$ python3 -c 'print("2\n" + "A"*2 + "A"*16*2 + "\na\n")' | python3 chall.py
# Sending choice 2 to authenticate using credentials
# Sending as username : 2 bytes to complete the first block + 16 bytes * 2 to check if the block are correctly forged ( second and third block may be the same )
# Sending anything (a) as password... this pars isn't needed to exploit.
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials
Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea05eb159765773a70532da4789b0305a592eb159765773a70532da4789b0305a59265c235f39040a271d8e612fcb3cf0e3863e9de070bb91f9a2714f8b733371020
welcome guest
# Checking Token :
>>> result[32:64]
'eb159765773a70532da4789b0305a592'
>>> result[64:96]
'eb159765773a70532da4789b0305a592'
The user can effectively forge arbitrary blocks.
In order to obtain admin access the user need to replace the block containing 'uid': 0
Here we will manipulate the input to have this at the end of the penultimate block :
$ python3 -c 'print("2\n" + "A"*2 + "\n" + "A"*6 + "\n")' | python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials
Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f7e8c582ca3535bc548f256f69832be4f
welcome guest
We now need to forge the block 1
$ python3 -c 'print("2\n" + "A"*2 + " "*15 + "1" + "\n" + "A"*6 + "\n")' | python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials
Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea050ebd6b5fd3363bc885c982fd44c1e7f3c5f31a2e59a38743077acd22e4512d69eb6c532e83118ae9a89f176933e2cd0f7e8c582ca3535bc548f256f69832be4f
welcome guest
We need a last block :
# } + padding
$ python3 -c 'print("2\n" + "A"*2 + "\n" + "A"*5 + "\n")' | python3 chall.py
Welcome !
Please login first.
How would you login ?
1) Token
2) Credentials
Choice :username : password : Here is your token : 0f0db6ff7eb32259e2ab26faad5bea05c5f31a2e59a38743077acd22e4512d6907687b0f26a0485b8e70f508c3c2902b6e1373a93bd585792817170366e57f43
welcome guest
Now to obtain the admin access we need to replace the last block of the first token (containing 0} + padding) by the second block of the second token ( containing 1) and concat the last block of the last token ( containing } + padding )