def test_md4_length_extension(self): """Challenge 30""" orig_message = 'comment1=cooking%20MCs;userdata=foo;comment2=%20like' \ '%20a%20pound%20of%20bacon' # this is not known to attacker. key = Crypto.gen_random_key(43) suffix = ';admin=true;' orig_hash = md4(key + orig_message) forged_message = MD4Hash.pad(key + orig_message) + suffix forged_hash = md4(forged_message) validate = lambda h: h == forged_hash self.assertTrue(extend_md4(orig_hash, orig_message, suffix, validate))
def forgeHash(keylen, message, digest, suffix): paddedForgedMessageWithKey = padMD4(key + message) + suffix forgedMessage = paddedForgedMessageWithKey[keylen:] h = struct.unpack("<4I", digest) md4obj = md4.md4(h[0], h[1], h[2], h[3]) md4obj.update(suffix) forgedDigest = md4obj.digest(len(paddedForgedMessageWithKey) * 8) return (forgedMessage, forgedDigest)
def forgeHash(keylen, message, digest, suffix): paddedForgedMessageWithKey = padMD4(key + message) + suffix forgedMessage = paddedForgedMessageWithKey[keylen:] h = struct.unpack('<4I', digest) md4obj = md4.md4(h) md4obj.update(suffix) forgedDigest = md4obj.digest(len(paddedForgedMessageWithKey) * 8) return (forgedMessage, forgedDigest)
def assert_md4_state(b, expected_state_str, expected_hash_str): md4obj = md4.md4() md4obj.update(b) expected_s = expected_state_str.replace(' ', '') s = binascii.hexlify(md4obj.state_be()).decode('ascii') if s != expected_s: raise Exception('expected {}, got {}'.format(expected_s, s)) expected_h = expected_hash_str.replace(' ', '') h = md4obj.hexdigest() if h != expected_h: raise Exception('expected {}, got {}'.format(expected_h, h))
def md4_length_attack(message, mac, extension): registers = get_registers(mac) for i in range(0, 40): padded_text = md4_pad('A' * i + message) registers = struct.unpack("<4I", mac) extended_message = padded_text[i:] + extension forged_mac = md4.md4(registers[0], registers[1], registers[2], registers[3]).update(extension).digest( len(padded_text + extension) * 8) if validate_mac(extended_message, key, forged_mac) is True: return i, forged_mac return -1, ""
def main(): # create mac instance global key global key_min global key_max mac_gen = secret_prefix_mac(key, md4.md4) msg = """comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon""" mac = mac_gen.tag(msg) wanted_extension = ";admin=true" assert mac_gen.verify(msg, mac) print "The mac for the msg: \n%r\nis:" % (msg,) print "\t%r" % (mac.hexdigest(),) print "Trying to extend with %r" % (wanted_extension,) print print "Starting search from key lengths %d to %d" % (key_min, key_max) for i in range(key_min, key_max+1): # for each possible key length # generate a valid padding block for the original msg, truncating it to remove the key padded = md_pad(("A"*i) + msg, little_endian = True)[i:] # create a new sha1 generator and splice in the original tag fake_tag = md4.md4() fake_tag._a, fake_tag._b, fake_tag._c, fake_tag._d = struct.unpack("<LLLL", mac.hexdigest().decode("hex")) # extend the hash by hasing the extension, with a fake length value in the padding # the fake length value should reflect the length of the original block (512 bits) # plus the length of the extension (in bits) fake_length = (i + len(padded) + len(wanted_extension)) * 8 fake_tag._md4(wanted_extension, fake_length = fake_length) # attempt to verify it if mac_gen.verify(padded + wanted_extension, fake_tag): print "Success!" print "Key length is %d, we succesfully verified the following msg:" % (i,) print repr(padded + wanted_extension) break else: print "Could not create a fake tag!"
def check_weak_message(message): m = md4() m.update(message) a, b, c, d = m.intermediates assert get_bit(a[1], 7) == get_bit(b[0], 7) assert get_bit(d[1], 7) == 0 return assert get_bit(d[1], 8) == get_bit(a[1], 8) assert get_bit(d[1], 11) == get_bit(a[1], 11) assert get_bit(c[1], 7) == 1 assert get_bit(c[1], 8) == 1 assert get_bit(c[1], 11) == 0 assert get_bit(c[1], 26) == get_bit(d[1], 26) assert get_bit(b[1], 7) == 1 assert get_bit(b[1], 8) == 0 assert get_bit(b[1], 11) == 0 assert get_bit(b[1], 26) == 0
def make_weak_message(message): md = md4() md.update(message) a, b, c, d = md.intermediates m = message_numbers(message) # a17 = b07 a[1] ^= xor_bit(a[1], b[0], 7) assert get_bit(a[1], 7) == get_bit(b[0], 7) m[0] = ror(a[1], 3) - a[0] - F(b[0], c[0], d[0]) m[0] = m[0] & 0xffffffff assert a[1] == rol(a[0] + F(b[0], c[0], d[0]) + m[0], 3) assert get_bit(a[1], 7) == get_bit(b[0], 7) # d17 = 0 d[1] ^= get_bit(d[1], 7) << 6 return numbers_to_message(m)
def validate_mac(message, key, mac): output_mac = md4.md4().update(key + message).digest() if output_mac == mac: return True else: return False
def smd4(msg): m = md4() m.update(msg) return m.hexdigest()
from md4 import md4 md4_tests = [ ('', '31d6cfe0d16ae931b73c59d7e0c089c0'), ("a", 'bde52cb31de33e46245e05fbdbd6fb24'), ("abc", 'a448017aaf21d8525fc10ae87aa6729d'), ("message digest", 'd9130a8164549fe818874806e1c7014b'), ("abcdefghijklmnopqrstuvwxyz", 'd79e1c308aa5bbcdeea8ed63df412da9'), ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", '043f8582f241db351ce627e153e7f0e4'), ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 'e33b4ddc9c38f2199c3e7b164fcc0536'), ] for test in md4_tests: if md4(test[0]) == test[1]: print "[+] {0}".format(test[0]) else: print "[-] {0}".format(test[0])
from __future__ import print_function from md4 import md4 import sys md4_tests = [ ('', '31d6cfe0d16ae931b73c59d7e0c089c0'), ("a", 'bde52cb31de33e46245e05fbdbd6fb24'), ("abc", 'a448017aaf21d8525fc10ae87aa6729d'), ("message digest", 'd9130a8164549fe818874806e1c7014b'), ("abcdefghijklmnopqrstuvwxyz", 'd79e1c308aa5bbcdeea8ed63df412da9'), ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", '043f8582f241db351ce627e153e7f0e4'), ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 'e33b4ddc9c38f2199c3e7b164fcc0536'), ] for test in md4_tests: if md4(test[0]) == test[1]: print("[+] {0}".format(test[0])) else: print("[-] {0}".format(test[0])) assert 0 print("Passed assertions: " + __file__, file=sys.stderr)
assert get_bit(1, 1) == 1 assert get_bit(2, 1) == 0 assert get_bit(2, 2) == 1 assert xor_bit(1, 1, 1) == 0 assert xor_bit(2, 2, 2) == 0 assert xor_bit(1, 2, 2) == 2 assert ror(0xdeadbeef, 4) == 0xfdeadbee assert rol(0xdeadbeef, 4) == 0xeadbeefd assert numbers_to_message(message_numbers(b'test' * 16)) == b'test' * 16 message = os.urandom(64) m = md4() m.update(message) a, b, c, d = m.intermediates assert a[0] == 0x67452301 assert b[0] == 0xefcdab89 assert c[0] == 0x98badcfe assert d[0] == 0x10325476 assert len(message) == 64 weakened = make_weak_message(message) print(hexlify(message)) print(hexlify(weakened)) check_weak_message(weakened)
def authMD4(key, message): md4obj = md4.md4() md4obj.update(key + message) return md4obj.digest()
def test_md4(self): text = "naresh" m = MD4.new() m.update(text) self.assertEquals(m.digest(), md4(text))
def get_registers(mac): return struct.unpack("<4I", mac) def md4_length_attack(message, mac, extension): registers = get_registers(mac) for i in range(0, 40): padded_text = md4_pad('A' * i + message) registers = struct.unpack("<4I", mac) extended_message = padded_text[i:] + extension forged_mac = md4.md4(registers[0], registers[1], registers[2], registers[3]).update(extension).digest( len(padded_text + extension) * 8) if validate_mac(extended_message, key, forged_mac) is True: return i, forged_mac return -1, "" if __name__ == "__main__": message = b'comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon' mac = md4.md4().update(key + message).digest() key_len, forged_mac = md4_length_attack(message, mac, ';admin=true') print("The key length is {} with a forged MAC of {}".format( key_len, forged_mac.encode('hex')))
def md4hex(message): md = md4() md.update(message) return md.hexdigest()
def init_state(hexmac, blocks): binary = unhexlify(hexmac) int_parts = struct.unpack("<4I", binary) obj = md4(*int_parts) obj._count = blocks return obj
import md4 import os def MAC(key: bytes, mess: bytes) -> bytes: return md4.md4(key + mess) MESS = b'comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon' KSIZ = 16 _KEY = os.urandom(KSIZ) # SECRET MAC0 = MAC(_KEY, MESS) SUFF = b';admin=true' if __name__ == '__main__': M = bytearray(KSIZ) # 0-block for the unknown key M.extend(MESS) md4.pad(M) lold = len(M) # new message M.extend(SUFF) MESS1 = bytes(M[KSIZ:]) print(MESS1) # forge a MAC md4.pad(M) MAC1 = md4.md4(M[lold:], MAC0, False) print(MAC1.hex()) # check MAC validity assert MAC1 == MAC(_KEY, MESS1)
M[4] = (rrot(d, 5) - d0 - G(a, b, c) - 0x5a827999) & msk32 # this one might interfere with the 4 conditions on a2, # so overall we get at most +4 new conditions # (+4 on d5 + -0 to -4 on a2) A[2] = lrot((A[1] + F(B[1], C[1], D[1]) + M[4]) & msk32, 3) M[5] = (rrot(D[2], 7) - D[1] - F(A[2], B[1], C[1])) & msk32 M[6] = (rrot(C[2], 11) - C[1] - F(D[2], A[2], B[1])) & msk32 M[7] = (rrot(B[2], 19) - B[1] - F(C[2], D[2], A[2])) & msk32 M[8] = (rrot(A[3], 3) - A[2] - F(B[2], C[2], D[2])) & msk32 if __name__ == '__main__': a0, b0, c0, d0 = md4.H0 cnt = 1 while True: M = md4.bytes_to_ints32(os.urandom(64)) modification(M) Md = differential(M) if md4.compress(a0, b0, c0, d0, M) == md4.compress(a0, b0, c0, d0, Md): break cnt += 1 M = md4.ints32_to_bytes(M) Md = md4.ints32_to_bytes(Md) print(f'Collision found ({cnt} tries):') print(f'M = {M.hex()}') print(f"M' = {Md.hex()}") H = md4.md4(M) Hd = md4.md4(Md) assert H == Hd print(f'H = {H.hex()}')
def MAC(key: bytes, mess: bytes) -> bytes: return md4.md4(key + mess)
def md4hash(msg): obj = md4() obj.update(msg) return obj.hexdigest()
def mac_tac(key, message): return md4.md4(key + message)
def md4_hexdigest(m, msglen=None): md4obj = md4.md4() md4obj.update(m) return md4obj.hexdigest(msglen)