lundi 2 juillet 2012

NDH 2k12 - Break Me Like Your Sister - 1000 pts


Une épreuve de crypto en python maintenant.

1.       What
On a un fichier .pyc (python compilé) ainsi qu’une image chiffrée !
Le plus simple et rapide est de décompiler le fichier avec uncompyle2 ou votre tool préféré et de voir ce que ça donne.

On obtient le fichier suivant :

#Embedded file name: spaceball.py
"""
Spaceball hash algorithm
"""

import sys
from Crypto.Cipher import Blowfish
from zlib import crc32

class PasswordError:

   
def __init__(self):
       
pass


def decode(s):
   
return ''.join([ chr(ord(c) + 1) for c in s[::-1] ])


def getbf():
    a =
'nsoxqB'
    b =
'grhevnkA'
    c =
'qdgohB'
   
try:
       
return getattr(getattr(__import__(decode(a)), decode(c)), decode(b))
   
except:
       
print decode('fmhrrhl\x1fdq`\x1frdhbmdcmdodC\x1f\\ Z')
       
sys.exit(-3)


def hash(s):
    a =
'ahky'
    b =
'12bqb'
    x =
__import__(decode(a))
    y =
getattr(x, decode(b))
   
if len(s) > 8:
       
raise ValueError()
    s = s +
'_' * (8 - len(s))
    a, b = s
[0:4], s[4:8]
   
return '%08X%08X' % (y(a) & 4294967295L, y(b) & 4294967295L)


def encrypt(filename_in, filename_out, password):
    _hash =
hash(password)
   
try:
        content =
open(filename_in, 'rb').read()
        remains =
8 - len(content) % 8
        content +=
chr(remains) * remains
        c = getbf
().new(password, getbf().MODE_CBC)
        _content = c.
encrypt(content)
       
open(filename_out, 'wb').write(_hash + _content)
       
return True
   
except IOError:
       
return False


def decrypt(filename_in, filename_out, password):
   
try:
        content =
open(filename_in, 'rb').read()
       
if len(content) < 16:
           
return False
        _hash = content
[:16]
       
if hash(password) != _hash:
           
raise PasswordError()
        content = content
[16:]
        c = getbf
().new(password, getbf().MODE_CBC)
        _content = c.
decrypt(content)
        _content = _content
[:-ord(_content[-1])]
       
open(filename_out, 'wb').write(_content)
       
return True
   
except IOError:
       
return False


if __name__ == '__main__':
   
if len(sys.argv) != 4:
       
print 'Usage: %s [c|d] [FILE] [PASSWORD (8 chars.)]' % sys.argv[0]
   
else:
       
try:
            action =
sys.argv[1]
           
if action != 'c' and action != 'd':
               
print '[!] Unknown action'
               
sys.exit(-1)
           
else:
                infile =
sys.argv[2]
               
pwd = sys.argv[3]
               
if len(pwd) != 8:
                   
print '[!] Bad password length'
                   
sys.exit(-2)
               
elif action == 'c':
                   
print '[i] Encrypting %s ...' % infile
                   
if encrypt(infile, infile, pwd):
                       
print '[i] OK'
                   
else:
                       
print '[!] An error occured'
               
elif action == 'd':
                   
print '[i] Decrypting %s ...' % infile
                   
if decrypt(infile, infile, pwd):
                       
print '[i] OK'
                   
else:
                       
print '[!] An error occured'
       
except PasswordError:
           
print '[!] Bad password !'

On remarque que certaines strings sont obfusquées mais elles se décodent facilement. Celles dans « getbf » donnent : Crypto, Blowfish, Cipher. Et celles dans « hash » donnent : zlib, crc32.

2.       Chiffrement
Pour chiffrer, le programme va calculer le hash du mot de passe, chiffrer le fichier en blowfish, et il rajoutera le hash en début de fichier. Comme ça, au déchiffrement il a juste à comparer le hash dans le fichier et le hash du mot de passe qu’on lui donne pour savoir si c’est le bon ou pas !.
Le hash du mot de passe utilisé, on peut aller le prendre dans le fichier et
c’est : 1D4481E1A22C8C3B.

Reste à savoir comment remonter au mot de passe !

3.       Hash
On sait que le mot de passe fait 8 caractères (c’est vérifié avant de chiffrer) mais on ne peut pas brute forcer sur 8 chars …
Par contre comme c’est un crc32 (4 octets) qui est utilisé, le programme est obligé de calculer le hash en 2 fois. D’abord les 4 premiers caractères, puis les 4 derniers.
Ce qui nous ramène à trouver 2 mots de 4 lettres qui ont un certain CRC32 … brute force !

import sys
from zlib import crc32

easyRange =
[32,33,36,42,43] + range(48, 58) +
           
range(65, 91) + range(97, 123)
key =
""

def checkPassword(password):
   
global key
    crc = crc32
(password) & 4294967295L
   
if (crc) == 0x1D4481E1:
        key = password + key
   
elif (crc) == 0xA22C8C3B:  
        key = key + password

def recurse(width, position, baseString):
   
for char in easyRange:
       
if (position < width-1):
            recurse
(width, position + 1, baseString + "%c" % char)
       
else:
            checkPassword
(baseString + "%c" % char)
   
print "Bruteforcing the pass ..."
   
recurse(4, 0, "")

print key


4.       Résultat
On obtient le mot de passe suivant après quelques secondes : His4n00b



Aucun commentaire:

Enregistrer un commentaire