import urllib2 import re import time #SERVEUR="epreuves2.insomni.hack:8080" SERVEUR="127.0.0.1:8080" URL_ENC="http://"+SERVEUR+"/enc/?t=" URL_DEC="http://"+SERVEUR+"/dec/?t=" BLOCK_SIZE=8 def crypt(t): u = urllib2.urlopen(URL_ENC+str(t)) r = u.readlines()[-1].split()[-1] u.close() print r return tobin(r) def split_ciphertext(c): return [c[BLOCK_SIZE*i : BLOCK_SIZE*(i+1)] for i in range(len(c)/BLOCK_SIZE)] def oracle_dec(t): u = urllib2.urlopen(URL_DEC+str(t)) r = u.read() u.close() return re.search("ussi", r) != None def xor_list(l1,l2): l = [] for i in range(len(l1)): l.append(l1[i] ^ l2[i]) return l def hexdump(r): return "".join([("%02x" % i) for i in r]) def tobin(r): return [int(r[i*2:i*2+2],16) for i in range(len(r)/2)] # Trouve le dernier octet du plaintext (avant d'appliquer CBC), voire meme quelques precedents def find_last_bytes(cipher_block): # Genera un bloc aleatoire previous_block = [0] * BLOCK_SIZE # Bruteforce le dernier caractere du bloc precedent for i in range(256): random_block = previous_block[:-1] + [i] cipher_random = random_block + cipher_block candidat = hexdump(cipher_random) if(oracle_dec(candidat)): break # On vient de trouver un bloc avec un padding valide new_block = previous_block[:-1] + [i] # Si on a de la chance, le dernier octet du plaintext est 01. # Mais on gere quand meme les autres cas. # Pour cela, on modifie les octets precedents en commencant par le premier, dans le but de voir si le padding devient incorrect. Si c'est le cas, c'est que ledit octet en fait partie for i in range(BLOCK_SIZE, 1, -1): indice = BLOCK_SIZE - i random_block = new_block[:indice] + [new_block[indice] ^ 1] + new_block[indice+1:] candidat = hexdump(random_block + cipher_block) if(not oracle_dec(candidat)): return [l ^ i for l in new_block[indice:]] return new_block[-1] ^ 1 # Dechiffre le caractere precedent en fonction des caracteres deja dechiffres def decrypt_previous_byte(cipher_block, already_decrypted): # Indice de l'octet a dechiffrer indice = BLOCK_SIZE - len(already_decrypted) - 1 # Genere un bloc aleatoire et fixe sa fin de telle sorte a obtenir un padding valide padding_byte = len(already_decrypted) + 1 pattern_block = [0] * (indice + 1) + [a ^ padding_byte for a in already_decrypted] # Bruteforce le caractere precedent les octets deja dechiffres for i in range(256): block_candidat = pattern_block[:indice] + [pattern_block[indice] ^ i] + pattern_block[indice+1:] candidat = hexdump(block_candidat + cipher_block) if(oracle_dec(candidat)): return pattern_block[indice] ^ i ^ padding_byte # Dechiffre la totalite d'un bloc def decrypt_block(cipher_block): decrypted_bytes = [find_last_bytes(cipher_block)] print decrypted_bytes[0], while(len(decrypted_bytes) != BLOCK_SIZE): b = decrypt_previous_byte(cipher_block, decrypted_bytes) print b, decrypted_bytes.insert(0, b) print return decrypted_bytes # Dechiffre l'integralite du ciphertext, en supposant que l'IV est le 1er bloc def decrypt_cbc(cipher_blocks): plaintext = [] # Effectue le mode CBC for i in range(1, len(cipher_blocks)): d = decrypt_block(cipher_blocks[i]) plaintext += xor_list(d, cipher_blocks[i-1]) return plaintext to_decrypt = "534EE451C9510604279896DEB97ACE72EE9FA6DCA08FF09B86A37D6BBA3FE272B3CAB385AB0F31D1" to_decrypt_ints = tobin(to_decrypt) blocks = split_ciphertext(to_decrypt_ints) # print "blocs : ", [hexdump(b) for b in blocks] print "".join([chr(i) for i in decrypt_cbc(blocks)])