def forge_sha1_mac(original_message, mac, append): A, B, C, D = struct.unpack('<IIII', mac) for prefix_length in range(1, 20): print(f'PREFIX LENGTH: {prefix_length}') to_pad = b'a' * prefix_length + original_message padded = get_md4_padding(to_pad) md4 = MD4(append, len(padded) + len(append), A, B, C, D) new_mac = md4.digest() new_plaintext = padded[prefix_length:] + append if authenticate(new_plaintext, new_mac): return new_plaintext, new_mac return b'', b''
def appendMessage(original, tag, extra): #assume secret is between 0 and 64 bytes in length for i in range(0, 65): oldpadding = generateMD4Padding(len(original)+i); newdata = extra; a = int.from_bytes(tag[0:4], byteorder='little'); b = int.from_bytes(tag[4:8], byteorder='little'); c = int.from_bytes(tag[8:12], byteorder='little'); d = int.from_bytes(tag[12:16], byteorder='little'); newtag = MD4(A=a, B=b, C=c, D=d, numbytes=i+len(original + oldpadding)).update(newdata).digest() if (checkDumbHashAuth(original + oldpadding + extra, newtag)): print(original + oldpadding + extra) return newtag print("Failure");
def hash_extend_attack(old_message, old_tag, new_message): # try key length for i in range(17): old_pad = gen_md4_padding(len(old_message) + i) new_data = new_message a = struct.unpack("<I", (old_tag[0:4]))[0] b = struct.unpack("<I", (old_tag[4:8]))[0] c = struct.unpack("<I", (old_tag[8:12]))[0] d = struct.unpack("<I", (old_tag[12:16]))[0] new_tag = MD4(A=a, B=b, C=c, D=d, numbytes=i + len(old_message + old_pad)).update(new_data).digest() if check(old_message + old_pad + new_message, new_tag): print("congratz!") return new_tag print("failed")
def challenge55(): md = MD4() # these are the two messages (given in a Wang's paper) that have the # same hash (written in little endian) #h = "4d7a9c8356cb927ab9d5a57857a7a5eede748a3cdcc366b3b683a0203b2a5d9fc69d71b3f9e99198d79f805ea63bb2e845dd8e3197e31fe52794bf08b9e8c3e9" #h = "4d7a9c83d6cb927a29d5a57857a7a5eede748a3cdcc366b3b683a0203b2a5d9fc69d71b3f9e99198d79f805ea63bb2e845dc8e3197e31fe52794bf08b9e8c3e9" #m = h.decode("hex") # convert to big endian before packing into string: # (split into 8 4-bytes blocks and then invert the order of bytes in each block) #b = struct.unpack("<16I", m) #m = struct.pack(">16I", *b) print "Wang's paper reports that 4 to 64 randomly selected messages are needed to find a collision;" print "with this implementation a few thousands randomly selected messages are needed (it takes some minutes)" print "to find a collision - could be because not all multi-step modifications have been applied" print "" charset = string.letters + string.digits + "%/+=" for i in range(10000): m = ''.join(random.choice(charset) for x in range(64)) #m = "PyxsBFALuSQW0AsCRKx7wXvRRyKIOIMg1v3hN+dlguuqGnKP1Y=BYo/gR1JHp86=" #m = "AaUjDhPG6n6acvWmRecqqQ5IDlI0+Klkw9oe6rzDQ=b1/7rtQB2rE=VrxEC2aRhK" # fails in i = 35 (even all conditions are met, the steps 32 and 33 might fail) #m = "wJKsvK3V1Ypyi7U5I=Wqbe2VJRne29l/Q9LADne=YJg65DBrwTuPj7WP=oLK6q/W" #m = "l13YLL0oC/rnykdK%i5q%5abUwLNvleCPv6hspmmDPoSG4kX0W9SI2qlsYpJZBb6" #m = "M/LxOqd9KpzNY4G%bh9dT%kTGLrpk9wCU4hi+4/K3n=9yufGrqqCD8ULG9rcBh91" #m = "+JiumjNHGtbVX886=0qnZSZYx/uX%MHUO8q0Aq0NWa%Zi8nQ=hwrh+5X5udJyNhy" #h = "4b67208753178a6617a95fb685448610209732e8abe97a8c5bc7d9a615136f314d475b027ff19f881befb76080eb382e169f30e1b6e9347edd17890badbfeae3" #m = h.decode("hex") #b = struct.unpack("<16I", m) #m = struct.pack(">16I", *b) #ok = md.modify_message(m) #print ok #md = MD4() #md.add(m) #d = md.finish() #hash1 = d.encode("hex") #h1 = "4b672087d3178a6687a95fb685448610209732e8abe97a8c5bc7d9a615136f314d475b027ff19f881befb76080eb382e169e30e1b6e9347edd17890badbfeae3" #m1 = h1.decode("hex") #b1 = struct.unpack("<16I", m1) #m1 = struct.pack(">16I", *b1) #md = MD4() #md.add(m1) #d = md.finish() #hash2 = d.encode("hex") #print hash1 #print hash2 # collisions are constructed for example for: #m = "hMJI75MIPD2ib4SjeJKrMiOrjWD59/CdhM8mUuWC81Fd1nQXrnaQs44P060kCbAh" #m = "4MuGCKvYPnlmdnhAab1JF=J4l7/YSMw30WJvY4izj%fgwXuTgADMLptYTBn9Gdww" #m = "\xff" * 8 * 8 #ms = struct.unpack("<16I", m) # m_0, m_1, ..., m_15 # m_0 - "1234", m_1 - "5678", m_3 - "1234", m_4 - "5678" ... # modify message to be of the format for which we can construct collision ok = md.modify_message(m) if not ok: continue x_mod = md.X_mod[:16] # x_mod has been constructed using little endian, so consider this when packing into string: l1 = map(lambda x : struct.pack("<I", x), x_mod) m_mod = "".join(l1) md = MD4() md.add(m_mod) d = md.finish() hash1 = d.encode("hex") # construct collision: success = md.construct_collision(m_mod) if success == False: # it could be None continue x_coll = md.X_mod[:16] # x_mod has been constructed using little endian, so consider this when packing into string: l1 = map(lambda x : struct.pack("<I", x), x_coll) x_coll = "".join(l1) md1 = MD4() md1.add(x_coll) d = md1.finish() hash2 = d.encode("hex") for i in range(25, 38): break print "======" # i = 35 ... b_9 # i = 36 ... a_10 # i = 37 ... d_10 t = md._calculate_h_until_break(md.X[:16], i) if i == 35: print "---" print t t1 = md._calculate_h_until_break(md.X_mod[:16], i) if t != t1: print i print t print t1 da, db, dd = None, None, None if i == 35: db = t1[1] - t[1] print db if i == 36: da = t1[0] - t[0] print da if i == 37: dd = t1[3] - t[3] print dd print hash1 print hash2 if hash1 == hash2: print "Hash collision!" print "The following two messages have the same hash:" print m_mod print x_coll break else: print "Not OK"
def secret_prefix_mac(message, key): md4 = MD4(key + message) return md4.digest()
def sign(self, msg): md4 = MD4() digest = md4.update(key+msg).digest() return digest
length = sha._message_byte_length # wrong! sha2 = Sha1Hash(d, length) print 'sha2._h', sha2._h assert sha2._h == sha._h # The reason we need to use the exact padding bytes is because the registers we # are going to inject contain the hash state after hashing key || message || padding. # So the final if __name__ == '__main__': #test2() #exit() key = 'yellow submarine' mac = MD4Mac(key) msg = "comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon" output_mac = mac.sign(msg) evil = ';admin=true' # my_mac is the sha1 hash of key || msg || glue_padding || evil # we need to try all the possible key lengths to figure out which amount of # glue padding is in there. for i in range(32): pad = MD4.get_padding(len(msg) + i) mymd4 = MD4(digest=output_mac, length=i + len(msg) + len(pad)) mymd4.update(evil) my_mac = mymd4.digest() full_msg = msg + pad + evil if mac.validate(full_msg, my_mac): print 'Forged message', my_mac.encode('hex'), 'key length = ', i
def hash_md4(data): #return hashlib.new('md4', data) md4 = MD4() md4.update(data) return md4
def md4(data): return MD4().update(data).digest()
def get_hash(orig): md4 = MD4() md4.update(KEY + orig) return md4.dgst()
from md4 import MD4, get_pad def get_hash(orig): md4 = MD4() md4.update(KEY + orig) return md4.dgst() def get_hash_check(orig): md4 = MD4() md4.update(KEY + orig) return md4.dgst() KEY = b'YELLOW SUBMARINE' message = b'comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon' faked = b';admin=true' mess = struct.unpack('<4I', get_hash(message)) for i in range(10000): padding = b'A' * i new_key = get_pad(padding + message) + faked length = len(new_key) * 8 fake_mess = new_key[i:] md4hash = MD4(mess) md4hash.update(faked, length) fake_mac = md4hash.dgst() test = get_hash_check(fake_mess) if test == fake_mac: print(fake_mess)
def dumbMD4HashAuth(key, message): return MD4().update(key + message).digest()