Annonce

Réduire
Aucune annonce.

Résolution d'un challenge CTF : Decrypter un GIF

Réduire
X
 
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • Tutoriel Résolution d'un challenge CTF : Decrypter un GIF

    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 :
    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()
    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 :
    1. Les caractères formant GIF, c'est à dire : 0x47 0x49 0x46
    2. Le numéro de versions sur 3 hexa : 0x38 0x39 0x61
    3. La longueur sur 2 hexa (en little indian) donc dans notre cas : 0x20, 0x3
    4. 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()
    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 :
    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()
    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.

  • #2
    Hello Anonyme77 !!!

    Merci meme si pas j'ai capté grand chose au niveau du code source, j’espère bientôt faire des CTF aussi. Cela a l'air cool d'en faire.

    Commentaire

    Chargement...
    X