Bloom Bloom
1 file available
Analysis
The first thing to notice is the add_user method.
users = 0b0
def add_user(username):
global users
cipher = AES.new(key, AES.MODE_ECB)
enc_username = cipher.encrypt(pad(username.encode(), AES.block_size))
for i in range(hash_functions_count):
digest = mmh3.hash(enc_username, i) % size
users = users | (0x1 << digest)
Every time a new user is registered to the system, the users variable is updated by setting some of its bits.
On the other hand, by analyzing the check_user method:
def check_user(username):
global users
cipher = AES.new(key, AES.MODE_ECB)
enc_username = cipher.encrypt(pad(username.encode(), AES.block_size))
for i in range(hash_functions_count):
digest = mmh3.hash(enc_username, i) % size
if users & (0x1 << digest) == 0:
return False
return True
we can notice that a user exists within the users variable if and only if the bits are correctly set.
Moreover, it does not check that the other bits are set to 0, then a user could be found even if it was not registered yet. For example, if the users variable is 011, it would recognize as registered users 001, 011. 010.
Solver
To exploit this issue, we can simply run a script that registers random users until the Administrator user is recognized as a valid user by the system. At that point, we can get the flag.
r = remote(HOST, PORT)
r.recvuntil(b">")
attempts_counter = 0
while True:
attempts_counter += 1
username = "".join([random.choice(alphabet) for _ in range(20)])
r.sendline(b"3")
r.sendlineafter(b"Username: ", username.encode())
r.sendlineafter(b">", b"2")
response = r.recvuntil(b">")
if b"Here is your flag:" in response:
print(response)
print(f"Flag found in {attempts_counter} attempts")
break
The flag
snakeCTF{w3lc0me_to_cryp70_ch4ll3ng35}