Intro
Déjà à l’école, on me le disait, “prends ton temps, lis bien le problème, c’est avant tout une question de français”. J’ai perdu énormément de points car je n’ai vu le bouton comparator.py qu’au bout d’une semaine !
Révision, où comment gagner un repas au resto offert par ton sysadmin
Le principe est simple, on envoie deux documents, via le site web, et cela les archive. Jusque là tout va bien, mais très vite cela va devenir le drame.
comparator.py
# coding: utf-8
import hashlib
from web.services.database import Database
from web.services.mailer import Mailer
class ComparatorError(Exception):
"""Base class for all Comparator exceptions"""
pass
class DatabaseError(ComparatorError):
"""Exception raised for errors in database operations."""
class StoreError(ComparatorError):
"""Exception raised for errors in store function.
Attributes:
message -- explanation of the error
"""
def __init__(self, files, message):
self.files = files
self.message = message
class Comparator(object):
"""A class for Comparator"""
BLOCK_SIZE = 8*1024
def __init__(self, f1=None, f2=None):
"""
Set default parameters
Required parameters :
f1: open file handler
f2: open file handler
db: database
m : mailer
"""
self.f1 = f1
self.f2 = f2
self.db = Database()
self.m = Mailer()
def compare(self):
self._reset_cursor()
return self.f1.read() == self.f2.read()
def store(self):
self._reset_cursor()
f1_hash = self._compute_sha1(self.f1)
f2_hash = self._compute_sha1(self.f2)
if self.db.document_exists(f1_hash) or self.db.document_exists(f2_hash):
raise DatabaseError()
attachments = set([f1_hash, f2_hash])
# Debug debug...
if len(attachments) < 2:
raise StoreError([f1_hash, f2_hash], self._get_flag())
else:
self.m.send(attachments=attachments)
def _compute_sha1(self, f):
h = hashlib.sha1()
buf = f.read(self.BLOCK_SIZE)
while len(buf) > 0:
h.update(buf)
buf = f.read(self.BLOCK_SIZE)
return h.hexdigest()
def _reset_cursor(self):
self.f1.seek(0)
self.f2.seek(0)
def _get_flag(self):
with open('flag.txt', 'r') as f:
flag = f.read()
return flag
Si l’on analyse le code rapidement, il sert à comparer deux documents, et si tout va bien il les envoie par mail, si une grosse erreur arrive, il lève l’exception qui nous donne le flag :
def store(self):
self._reset_cursor()
f1_hash = self._compute_sha1(self.f1)
f2_hash = self._compute_sha1(self.f2)
if self.db.document_exists(f1_hash) or self.db.document_exists(f2_hash):
raise DatabaseError()
attachments = set([f1_hash, f2_hash])
# Debug debug...
if len(attachments) < 2:
raise StoreError([f1_hash, f2_hash], self._get_flag())
else:
self.m.send(attachments=attachments)
OK, mais comment obtenir cette exception alors ? La comparaison se fait par le Hash des fichiers, et le bloc juste après, nous voyons qu’il va utiliser une somme SHA1 :
def _compute_sha1(self, f):
h = hashlib.sha1()
buf = f.read(self.BLOCK_SIZE)
while len(buf) > 0:
h.update(buf)
buf = f.read(self.BLOCK_SIZE)
return h.hexdigest()
Et vous savez quoi mes chatons ? Il existe une collision SHA1, deux documents craftés peuvent avoir la même somme SHA1 (alors que théoriquement, cela était impossible).
Je vous laisse regarder ce site qui explique le souci, et ce qui est bien c’est qu’il fournit deux PDF crafté avec la collision, c’est parfait !
Let’s forge our file !
Le site a déjà ce hash en mémoire, ce n’est pas grave, cela nous retarde juste un petit peu. Nous avons deux PDF avec la même somme SHA1 :
Nous allons donc les modifier afin de changer la signature ! Et si nous le faisons bien, nous aurons la même propriété. Pour cela, nous allons ajouter un padding à la fin du document, des random bytes qui modifieront la somme SHA1 et qui nous validerons le défi. Pour ce faire, nous avons besoin de créer un fichier dummy avec notre padding dedans :
dd bs=64 if=/dev/urandom of=./padding-pseudo-random.tmp count=23
Pourquoi 23 ? Car c’est mon jour de naissance. Et l’on reste dans la limitation des 2 Mo par document (23 blocs de 64 Ko).
On réalise une copie des documents de Google, et nous allons les modifier comme cec i:
Nous vérifions que nous avons bien gardé notre propriété magique :
Nous les passons dans l’outil de révision, et nous obtenons notre flag !
FCSC{8f95b0fc1a793e102a65bae9c473e9a3c2893cf083a539636b082605c40c00c1}
Pourquoi ton sysadmin devrait te payer le resto
Va voir ton sysadmin, et demande lui s’il est possible qu’une somme SHA1 soit identique sur deux documents différents. S’il est cultivé, il saura te répondre que oui c’est possible, s’il n’est pas réellement intéressé, il te soutiendra que non, et là tu pourras mettre en place ton pari !