Aujourd'hui on s'attaque au challenge "collision" de pwnable.kr.

Etape 1: Récupérer le code source et le comprendre

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;

// Creation d'un fonction de hachage 
unsigned long check_password(const char* p){
    int* ip = (int*)p; // Declare un tableau de 
    				   // pointeur de type entier sur la string 
                       // Chaque pointeur contient 4 octet de la string
                       // Il y a 5 morceaux car 
                       // 5 morceaux de 4 octets = 20 octets ou 20 caractères
    int i;
    int res=0;
    // Parcour la string 
    for(i=0; i<5; i++){
    	// Pour chaque octet la valeur de celui ci est ajouté à l'entier "res"
        res += ip[i];
    }
    return res;
}

int main(int argc, char* argv[]){
    if(argc<2){
        printf("usage : %s [passcode]\n", argv[0]);
        return 0;
    }
    if(strlen(argv[1]) != 20){
        printf("passcode length should be 20 bytes\n");
        return 0;
    }

    if(hashcode == check_password( argv[1] )){
        system("/bin/cat flag");
        return 0;
    }
    else
        printf("wrong passcode.\n");
    return 0;
}

Au cas où vous seriez confus, ce qui se passe, c'est que le programme prend le code d'accès donné qui fait 20 octets de long et le divise en 5 morceaux (chaque morceau fait 4 octets) puis il additionne la valeur décimale des 5 morceaux et renvoie cette valeur.

Etape 2: Exploitation

On peut se dire que la solution est simple il suffit de diviser le hashcode par 5 et de le passer en argument au programme. Le seul hic c'est que le hashcode en décimal vaut 568134124, qui n'est pas divisible par 5 (car il ne finit ni pas zéro ni par 5).

La solution consiste à passer en argument quelque chose comme ça

568134124 = 142033530 * 4 + 4
568134124 = 568134120 + 4 

NB: On peut tout fait passer 5 valeurs différentes les unes des autres mais j'essaye de rester simple dans mon explication.

C'est à dire 4 octets contenant la valeur 142033530 et un octet contenant la valeur 4. De cette façon on obtient bien 5 octets qui une fois additionner vaudront 568134124.

Sauf que ça ne peut pas marcher. Pourquoi ? Parce que la dernière valeur va être encodé comme suit: 0x4 soit une fois convertit en little endian : \x04\x00\x00\x00 Ce qui en soit ne pose pas de problème au programme mais par contre le shell ne nous laissera pas faire.

Il faut donc revoir notre stratégie, il faut faire en sorte que les valeurs ajoutés ne contiennent pas de null byte.

Il y a une multitude de chose qui peuvent valoir le hashcode (c'est d'ailleurs grâce à ça qu'on peut faire une hash collision)
Par exemple:

568134124  = 113626824 * 4 + 113626828

Ce qui une fois convertit en hexa donne

from pwn import *

print(p32(113626824) * 4 + p32(113626828))

\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06

Comme on le voit, plus de null bytes !

Testons ça !

Et bingo !
Il ne reste plus qu'a reproduire ça sur le serveur de pwnable.kr !

Bonus:

On peut automatiser cette exploitation via un script python et la librairie pwntools

#!/usr/bin/python

from pwn import *
from pprint import pprint

shell = ssh('col' ,'pwnable.kr' ,password='guest', port=2222)

payload = p32(113626824)*4 + p32(113626828)

process = shell.process(executable='./col', argv=['col',payload])

log.success(process.recv())

Social et Media

Comme toujours je suis disponible sur Twitter et cie, si vous avez des questions, des remarques, des suggestions etc. N’hésitez pas !

Twitter: @GhostAgs

Discord: hackraw