alphascii clashing
4 minutes to read
We are given the Python source code of the server:
from hashlib import md5
import json
'''
Data format:
{
username: [md5(username).hexdigest(), password],
.
.
.
}
'''
users = {
'HTBUser132' : [md5(b'HTBUser132').hexdigest(), 'secure123!'],
'JohnMarcus' : [md5(b'JohnMarcus').hexdigest(), '0123456789']
}
def get_option():
return input('''
Welcome to my login application scaredy cat ! I am using MD5 to save the passwords in the database.
I am more than certain that this is secure.
You can't prove me wrong!
[1] Login
[2] Register
[3] Exit
Option (json format) :: ''')
def main():
while True:
option = json.loads(get_option())
if 'option' not in option:
print('[-] please, enter a valid option!')
continue
option = option['option']
if option == 'login':
creds = json.loads(input('enter credentials (json format) :: '))
usr, pwd = creds['username'], creds['password']
usr_hash = md5(usr.encode()).hexdigest()
for db_user, v in users.items():
if [usr_hash, pwd] == v:
if usr == db_user:
print(f'[+] welcome, {usr} 🤖!')
else:
print(f"[+] what?! this was unexpected. shutting down the system :: {open('flag.txt').read()} 👽")
exit()
break
else:
print('[-] invalid username and/or password!')
elif option == 'register':
creds = json.loads(input('enter credentials (json format) :: '))
usr, pwd = creds['username'], creds['password']
if usr.isalnum() and pwd.isalnum():
usr_hash = md5(usr.encode()).hexdigest()
if usr not in users.keys():
users[usr] = [md5(usr.encode()).hexdigest(), pwd]
else:
print('[-] this user already exists!')
else:
print('[-] your credentials must contain only ascii letters and digits.')
elif option == 'exit':
print('byeee.')
break
if __name__ == '__main__':
main()
Source code analysis
Basically it implements a database to store user credentials. They use the username as a key and the value is a tuple containing the MD5 hash of the username, and the plaintext password:
'''
Data format:
{
username: [md5(username).hexdigest(), password],
.
.
.
}
'''
users = {
'HTBUser132' : [md5(b'HTBUser132').hexdigest(), 'secure123!'],
'JohnMarcus' : [md5(b'JohnMarcus').hexdigest(), '0123456789']
}
Login option
The flag appears at the login option:
if option == 'login':
creds = json.loads(input('enter credentials (json format) :: '))
usr, pwd = creds['username'], creds['password']
usr_hash = md5(usr.encode()).hexdigest()
for db_user, v in users.items():
if [usr_hash, pwd] == v:
if usr == db_user:
print(f'[+] welcome, {usr} 🤖!')
else:
print(f"[+] what?! this was unexpected. shutting down the system :: {open('flag.txt').read()} 👽")
exit()
break
else:
print('[-] invalid username and/or password!')
elif option == 'register':
creds = json.loads(input('enter credentials (json format) :: '))
In order to get the flag, we will need to find a username whose MD5 hash collides with a different username that already exists on the database. In other words, we need $u_1$ and $u_2$ such that $u_1 \ne u_2$ and $\mathrm{MD5}(u_1) = \mathrm{MD5}(u_2)$.
Register option
For this, we will need to register at least a new user:
elif option == 'register':
creds = json.loads(input('enter credentials (json format) :: '))
usr, pwd = creds['username'], creds['password']
if usr.isalnum() and pwd.isalnum():
usr_hash = md5(usr.encode()).hexdigest()
if usr not in users.keys():
users[usr] = [md5(usr.encode()).hexdigest(), pwd]
else:
print('[-] this user already exists!')
else:
print('[-] your credentials must contain only ascii letters and digits.')
The requirement here is that the username and the password are alphanumeric strings, as expected for this kind of fields.
Solution
So, the objective is to find two different alphanumeric strings whose MD5 hash collide. A quick search on the Internet shows a X (Twitter) post that contains what we need:
$ python3 -q
>>> from hashlib import md5
>>>
>>> u1 = b'TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak'
>>> u2 = b'TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak'
>>>
>>> u1 != u2 and md5(u1).digest() == md5(u2).digest()
True
Flag
So, we only need to register one of the usernames and use the second one to get the flag:
$ nc 94.237.53.112 51607
Welcome to my login application scaredy cat ! I am using MD5 to save the passwords in the database.
I am more than certain that this is secure.
You can't prove me wrong!
[1] Login
[2] Register
[3] Exit
Option (json format) :: {"option":"register"}
enter credentials (json format) :: {"username":"TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak","password":"x"}
Welcome to my login application scaredy cat ! I am using MD5 to save the passwords in the database.
I am more than certain that this is secure.
You can't prove me wrong!
[1] Login
[2] Register
[3] Exit
Option (json format) :: {"option":"login"}
enter credentials (json format) :: {"username":"TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak","password":"x"}
[+] what?! this was unexpected. shutting down the system :: HTB{f33ls_g00d_f1nd1ng_4lph4num3r1c_c0ll1s10ns_fr0m_tw1tt3r_p0sts} 👽