Bonsoir à tous.
Alors, aujourd'hui, je vous reviens avec la résolution d'un challenge tout droit issu d'un CTF auquel j'ai participé durant ce bon mois de Mars.
Voici son énoncé :
Someone used this NSA-approved-backdoor-free encryption program to encrypt the following file. Can you decrypt the file and prove it’s not so secure after all?
Et voici le programme fourni :
Et voici un lien vers le fichier crypté fourni :
https://mega.nz/#!44NnVBJC!jlloUHEh1...N67EtVekP3UPBk
L'analyse
Si on lit le code de cryptage, on remarque que celui ci génère un tableau de 10 bytes aléatoires qui sont ensuite répétés pour que ce tableau ai la même taille que le fichier donné en entrée.
Ensuite, le cryptage en lui même est fait via un simple xor.
Retrouver la clef
Notre problème, ce que nous n'avons pas la clef.
Par contre, nous savons via le nom du fichier (flag_800x400px.gif.enc) qu'auparavant celui ci devait être un gif mesurant 800 par 400 pixels.
Il faut savoir qu'un fichier GIF, comme tout autre type de fichier contient une entête relativement fixe.
On peut trouver une description de celle-ci ici :
http://giflib.sourceforge.net/whatsi...and_bytes.html
On y apprend que les 10 premiers hexas d'un GIF sont :
Nous pouvons donc refaire un xor entre ces informations et le début du fichier encrypté afin de retrouver la clef de chiffrement.
C'est ce que je fais dans le code suivant :
J'ai donc pu récupérer la clef.
Elle est : '\xb2j\x88\xb7SH\x1a\xb7K\xf0'
Decrypter le fichier
Grâce à cette clef, on peut modifier le code de chiffrement afin d'en faire un code de déchiffrement.
Voici un code d'exemple :
Si vous l'exécutez en lui fournissant le fichier crypté en entrée, il vous regénérera le gif suivant :
https://mega.nz/#!E4cU2bwD!iXaDbHDMX...1L7FUG5guozCIA
Et ce dernier contient le flag (le mot de passe) pour gagner le challenge.
Pourquoi ce chiffrement n'est-il pas sécurisé ?
Le problème de cette technique de "chiffrement" est qu'un xor est inversible si l'on connait le texte en clair ou une partie de celui-ci.
C'est un problème connu pour d'autres algorithmes dans certaines configurations (des paramètres de RSA mal choisis).
Comment corriger ?
Une méthode pour corriger ce problème est d'utiliser un autre système que le xor, les modulos par exemple.
Une seconde technique serait d'augmenter la taille de la clef. En effet, si celle-ci est plus grande que la taille du header du fichier, nous ne pourrons pas refaire complètement le xor qui nous permettra de récupérer la clef via le fichier encrypté.
Conclusion
J'espère que ce petit exemple de résolution de challenge vous aura plus.
N'hésitez pas à me poser des questions ou a me faire des remarques si vous en avez. Je serai heureux d'y répondre.
Alors, aujourd'hui, je vous reviens avec la résolution d'un challenge tout droit issu d'un CTF auquel j'ai participé durant ce bon mois de Mars.
Voici son énoncé :
Someone used this NSA-approved-backdoor-free encryption program to encrypt the following file. Can you decrypt the file and prove it’s not so secure after all?
Et voici le programme fourni :
Code:
import os import sys KEY_SIZE = 10 def expand_key(key, length): return (length / len(key)) * key + key[0:(length % len(key))] def xor(s1, s2): assert len(s1) == len(s2) return ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(s1, s2)]) def main(): if len(sys.argv) == 2: key = os.urandom(KEY_SIZE) filename = sys.argv[1] f = open(filename) data = f.read() f.close() expanded_key = expand_key(key, len(data)) data_encrypted = xor(expanded_key, data) f = open(filename + ".enc", "w") f.write(data_encrypted) f.close() print "File %s encrypted with key: %s" % (filename, key.encode("hex")) else: print "Usage: %s <filename>" % (sys.argv[0]) if __name__ == "__main__": main()
https://mega.nz/#!44NnVBJC!jlloUHEh1...N67EtVekP3UPBk
L'analyse
Si on lit le code de cryptage, on remarque que celui ci génère un tableau de 10 bytes aléatoires qui sont ensuite répétés pour que ce tableau ai la même taille que le fichier donné en entrée.
Ensuite, le cryptage en lui même est fait via un simple xor.
Retrouver la clef
Notre problème, ce que nous n'avons pas la clef.
Par contre, nous savons via le nom du fichier (flag_800x400px.gif.enc) qu'auparavant celui ci devait être un gif mesurant 800 par 400 pixels.
Il faut savoir qu'un fichier GIF, comme tout autre type de fichier contient une entête relativement fixe.
On peut trouver une description de celle-ci ici :
http://giflib.sourceforge.net/whatsi...and_bytes.html
On y apprend que les 10 premiers hexas d'un GIF sont :
- Les caractères formant GIF, c'est à dire : 0x47 0x49 0x46
- Le numéro de versions sur 3 hexa : 0x38 0x39 0x61
- La longueur sur 2 hexa (en little indian) donc dans notre cas : 0x20, 0x3
- La largeur sur 2 hexa (en little indian toujours) : 0x90, 0x1
Nous pouvons donc refaire un xor entre ces informations et le début du fichier encrypté afin de retrouver la clef de chiffrement.
C'est ce que je fais dans le code suivant :
Code:
>>> def xor(s1,s2): ... return ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(s1, s2)]) >>> f = open("flag_800x400px.gif.enc") >>> data = f.read()[0:10] >>> data '\xf5#\xce\x8fj):\xb4\xdb\xf1' >>> len(data) 10 >>> header = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x20, 0x3 ,0x90, 0x1 ] >>> headerHexString = "" >>> for i in header: ... headerHexString += chr(i) >>> headerHexString 'GIF89a \x03\x90\x01' >>> xor(headerHexString,data) '\xb2j\x88\xb7SH\x1a\xb7K\xf0' >>> exit()
Elle est : '\xb2j\x88\xb7SH\x1a\xb7K\xf0'
Decrypter le fichier
Grâce à cette clef, on peut modifier le code de chiffrement afin d'en faire un code de déchiffrement.
Voici un code d'exemple :
Code:
import os import sys KEY_SIZE = 10 def expand_key(key, length): return (length / len(key)) * key + key[0:(length % len(key))] def xor(s1, s2): assert len(s1) == len(s2) return ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(s1, s2)]) def main(): if len(sys.argv) == 2: key = '\xb2j\x88\xb7SH\x1a\xb7K\xf0' filename = sys.argv[1] f = open(filename) data = f.read() f.close() expanded_key = expand_key(key, len(data)) data_encrypted = xor(expanded_key, data) f = open(filename + ".gif", "w") f.write(data_encrypted) f.close() print "File %s encrypted with key: %s" % (filename, key.encode("hex")) else: print "Usage: %s <filename>" % (sys.argv[0]) if __name__ == "__main__": main()
https://mega.nz/#!E4cU2bwD!iXaDbHDMX...1L7FUG5guozCIA
Et ce dernier contient le flag (le mot de passe) pour gagner le challenge.
Pourquoi ce chiffrement n'est-il pas sécurisé ?
Le problème de cette technique de "chiffrement" est qu'un xor est inversible si l'on connait le texte en clair ou une partie de celui-ci.
C'est un problème connu pour d'autres algorithmes dans certaines configurations (des paramètres de RSA mal choisis).
Comment corriger ?
Une méthode pour corriger ce problème est d'utiliser un autre système que le xor, les modulos par exemple.
Une seconde technique serait d'augmenter la taille de la clef. En effet, si celle-ci est plus grande que la taille du header du fichier, nous ne pourrons pas refaire complètement le xor qui nous permettra de récupérer la clef via le fichier encrypté.
Conclusion
J'espère que ce petit exemple de résolution de challenge vous aura plus.
N'hésitez pas à me poser des questions ou a me faire des remarques si vous en avez. Je serai heureux d'y répondre.
Commentaire