Impossible web challenge

Category: Web Points: 225 pts Solves: 28

Impossible web challenge consiste en une page d’enregistrement  et une page de login, le but  est d’arriver à  se loguer.

impossible1

On commence par récupérer les fichiers sources à l’aide du fichier robots.txt qui nous permet de découvrir un dossier backup.
On y trouve une archive qui contient les fichiers sources ainsi qu’un fichier users.dat.

Index.php

    $login = false;

    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
        if (!(isset($_POST['username']) && isset($_POST['password']))) {
            exit();
        }

        if(username_exist($_POST['username'])) {
            $user_info = get_user($_POST['username']);
            if ($user_info[1] == $_POST['password']) {
                $login = true;
            }
        }

    }

Register.php

    $check = true;

    $result = 0;
    $title = 'Forum - Registration';
    $user_info = array();

    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
        if (!(isset($_POST['username']) && isset($_POST['email']) && isset($_POST['password']))) {
            exit();
        }
        $check = preg_match('/^[a-zA-Z0-9]+$/', $_POST['username']);
        if (!$check) {
        } elseif (username_exist($_POST['username'])) {
            $result = 1;
            $title = 'Registration Failed';
        } else {
            add_user($_POST['username'], $_POST['email'], $_POST['password']);
            $user_info = get_user($_POST['username']);
            $result = 2;
            $title = 'Registration Complete';
        }
    }

Functions.php

    function username_exist($username) {
        $data = file_get_contents('../users.dat');
        $users = explode("\n", $data);
        foreach ($users as $key => $value) {
            $user_data = explode(",", $value);
            if ($user_data[2] == '1' && base64_encode($username) == $user_data[0]) {
                return true;
            }
        }
        return false;
    }

    function add_user($username, $email, $password) {
        file_put_contents('../users.dat', base64_encode($username) . ',' . base64_encode($email) . ',0\n', $flag = FILE_APPEND);
        file_put_contents('../passwords.dat', md5($username) . ',' . base64_encode($password) . '\n', $flag = FILE_APPEND);
    }

    function get_user($username) {
        $data = file_get_contents('../passwords.dat');
        $passwords = explode('\n', $data);
        foreach ($passwords as $key => $value) {
            $user_data = explode(',', $value);
            if (md5($username) == $user_data[0]) {
                return array($username, base64_decode($user_data[1]));
            }
        }
        return array('', '');
    }

Une analyse du code permet de voir que le fichier user contient la liste des noms d’ulisateur en base64, leur email en base64 et un flag d’activation,
le fichier password.dat contient les noms d’utilisateur en md5 ainsi que leur mot de passe en base64.
Lors de l’enregistrement, on vérifie la présence d’un utilisateur dans le fichier users.dat . Si il n’existe pas, on ajoute les champs dans les deux fichiers.
Pour le login, on regarde si l’utilisateur est présent et possède bien le flag a ‘1’ , et si c’est le cas, on charge son mot de passe qu’on compare avec la saisie de l’utilisateur.

On devine donc le but du challenge: créer une « collision » avec un autre utilisateur pour se loguer à sa place ou pour récupérer son mot de passe .
Une chose intéressante, toutes les comparaisons sont faites avec l’operateur ==.  Aurait-on un « Type juggling » ?

On commence par décoder les utilisateurs présent et à calculer leur md5.

enjoilife 8fddd0ddf2ce8c32417a1ba82915f5d6
b1gm1ke c91fbcedac658e46e9955b73faafc693
captain10106 83e350d4e020470566cf1ed664235568
Asadov 82905725386ab815a047200d4bd68e87
actusreyus 1b93b4344d31fb41d623213ebf8ceccc
safewaybill e2beba3105f2dd1383038d8346d57e6a
litojw11 69b7995852602018dd481b4106c59c24
joshuac11 55ada3c252f103f0183bd98c5865bb15
bobdole72 6acf3d115d39f31af8fc6e169437fd6d
deanposs d577d8bb2efdd70f72a1e25fa13b9c09
gndark 46926fdcd6e9ca47ba6287f471cdd9ea
adm2salwg 0e004561083131340065739640281486
ratch3 9e3e6dc1e10d32fb5b9c9c1826d2837c
bobsan c45b2be0b08d51b5b6e95fb53ef4eb80
brazz9685 cb3a7a388a7fc86f034ce9a7c60ae912
dave829 591f777942a50b702eb52a416cec606d
pointflick f44ce1c5a162886b9d0761b9eabfed54
bettyp69 ce365af3bfc85c12dc8f823f3b1a437f
Earlblack 6b017165ae58fcf6c971f402491eceb4
robinsonjr2 432876a924e2e3583bedb9fef8d2b08f
teamkatman b769140022dcd4eef468132f8eed5be3
golf2008 b902106ac4ecdae3723877607215747b
eazy779 0ee438353d97d29c8a48aeecc5342af2
mcarr01 25700233b45fc582ab6b2b745f9cec8e
tmoino38 2ba8f6eceeccf055e0c4d79023b69311
hygdigs├©ren cd2fef0f3b153d7cf52ebdb24ed0335a
bpackard 28f13353b0f7a7b163249603d09ade0e
evilklown b91385cae3325ffc40c61a235424b6dc

On voit un utilisateur très intéressant :  adm2salwg dont le md5 donne 0e004561083131340065739640281486.
En php, une telle chaine est convertie en entier et vaut 0.
La comparaison avec une chaine de la même forme ( 0[eE][0-9]*  ) sera toujours vraie.

Dans la fonction getuser, on trouve la comparaison suivante :

if (md5($username) == $user_data[0]) {
                return array($username, base64_decode($user_data[1]));
            }

On contrôle $username, donc on peut forcer la comparaison a true au moment où il va comparer l’utilisateur adm2salwg.

La fonction register affiche le mot de passe de l’utilisateur que l’on vient de créer .

On peut donc récupérer le mot de passe de adm2salwg avec un utilisateur dont le md5 sera de la bonne forme.

Un brute force permet de trouver un utilisateur correct :


Jm3u5Q9qOLzdiDkF6tRO 0e236442696614450577411236293116

On s’enregistre avec le nouvel utilisateur , et on récupère le mot de passe de adm2salwg

impossible2

Plus qu’a se loguer en tant qu’adm2salwg et on obtient le flag.

Sebbb.

Leave a Comment

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *