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 !'
"""
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.
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
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