authur1 functinality finished
This commit is contained in:
parent
31b1a7a10c
commit
601118b77e
|
@ -44,3 +44,31 @@ working extension attack against a basic mic:
|
||||||
>>> authur1.keyed_hash(bytearray(8), bytearray(16), False).hex()
|
>>> authur1.keyed_hash(bytearray(8), bytearray(16), False).hex()
|
||||||
'fd0ef003'
|
'fd0ef003'
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
|
|
||||||
|
===========
|
||||||
|
Reversing the exercise
|
||||||
|
===========
|
||||||
|
❯ bash authurl-values-attack.sh
|
||||||
|
bytearray(b'abcdef') has length 6
|
||||||
|
========================================================================================================================
|
||||||
|
extension_msg for bruteforce: bytearray(b'ef\xff\xff')
|
||||||
|
looking for result: 0f6b8802
|
||||||
|
Bruteforcing the internal state, this might take a while
|
||||||
|
========================================================================================================================
|
||||||
|
state 332e2000 | hash 0f6ba002 | search 0f6b88022
|
||||||
|
========================================================================================================================
|
||||||
|
FOUND THE THING
|
||||||
|
IT IS 332e2800
|
||||||
|
========================================================================================================================
|
||||||
|
Trying to forge a mic for an extended version with input:
|
||||||
|
00006566
|
||||||
|
(ef)
|
||||||
|
========================================================================================================================
|
||||||
|
Hacked MIC: 0f6b8802
|
||||||
|
Forged a valid delta:
|
||||||
|
====================BEGIN FORGED AUTHENTICATED TEXT====================
|
||||||
|
bytearray(b'ef')
|
||||||
|
====================END FORGED AUTHENTICATED TEXT======================
|
||||||
|
MIC: bytearray(b'\x0fk\x88\x02')
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ Since this (auth) hash did not have a name before, I gave it the name 'authur1'
|
||||||
import argparse
|
import argparse
|
||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
from types import NoneType
|
||||||
|
|
||||||
# FIXME make proper pyi Implementation for the rust part
|
# FIXME make proper pyi Implementation for the rust part
|
||||||
# only used for bit rotation
|
# only used for bit rotation
|
||||||
|
@ -22,7 +24,7 @@ from plexcryptool import binary
|
||||||
SHIFT_LENGTH = 17
|
SHIFT_LENGTH = 17
|
||||||
DEFINED_INITIAL = bytearray([0x52, 0x4f, 0x46, 0x4c])
|
DEFINED_INITIAL = bytearray([0x52, 0x4f, 0x46, 0x4c])
|
||||||
PADDING = 0xff
|
PADDING = 0xff
|
||||||
EXT_TEST_STR = "EXTENSION ATTACK WORKS"
|
EXT_TEST_STR: str = "ef"
|
||||||
|
|
||||||
# constants for Circular shifting
|
# constants for Circular shifting
|
||||||
# constant value defined in limits.h, it's 8 (bit) on my machine, on yours probably too.
|
# constant value defined in limits.h, it's 8 (bit) on my machine, on yours probably too.
|
||||||
|
@ -124,7 +126,7 @@ def keyed_hash(message: bytearray, key: bytearray, verbose: bool = False, return
|
||||||
mic: bytearray = authur1(input, verbose, return_last_state=return_last_state)
|
mic: bytearray = authur1(input, verbose, return_last_state=return_last_state)
|
||||||
return mic
|
return mic
|
||||||
|
|
||||||
def extension_attack(valid_pairs: list[tuple[bytearray, bytearray]]) -> tuple[bytearray,bytearray]:
|
def extension_attack(valid_pairs: list[tuple[bytearray, bytearray]], extending_message: bytearray, last_internal_state: int | NoneType = None) -> tuple[bytearray,bytearray, bytearray]:
|
||||||
"""
|
"""
|
||||||
Extension attack against keyed hash of authur1
|
Extension attack against keyed hash of authur1
|
||||||
|
|
||||||
|
@ -191,18 +193,26 @@ def extension_attack(valid_pairs: list[tuple[bytearray, bytearray]]) -> tuple[by
|
||||||
# only the last block
|
# only the last block
|
||||||
last_block = target_pair[0][-4:]
|
last_block = target_pair[0][-4:]
|
||||||
extension_msg = last_block
|
extension_msg = last_block
|
||||||
|
full_msg = target_pair[0][:-4]
|
||||||
# given mic for "abcdef" 0f6b8802
|
# given mic for "abcdef" 0f6b8802
|
||||||
looking_for = target_pair[1]
|
looking_for = target_pair[1]
|
||||||
last_internal_state = 0
|
|
||||||
print("=" * 120)
|
print("=" * 120)
|
||||||
print("extension_msg for bruteforce: %s" % extension_msg)
|
print("extension_msg for bruteforce: %s" % extension_msg)
|
||||||
print("looking for result: %s" % looking_for.hex())
|
print("looking for result: %s" % looking_for.hex())
|
||||||
print("Bruteforcing the internal state, this might take a while")
|
print("Bruteforcing the internal state, this might take a while")
|
||||||
print("=" * 120)
|
print("=" * 120)
|
||||||
|
starttime = time.time()
|
||||||
|
if last_internal_state is None:
|
||||||
for i in range(0, KEY_SPACE):
|
for i in range(0, KEY_SPACE):
|
||||||
mic = authur1_optimized(extension_msg, bytearray(i.to_bytes(4)))
|
mic = authur1_optimized(extension_msg, bytearray(i.to_bytes(4)))
|
||||||
if i % 0x1000 == 0:
|
if i % 0x1000 == 0:
|
||||||
msg = "state %08x | hash %s | search %s" % (i, mic.hex(), looking_for.hex())
|
msg = "state %08x | hash %s | search %s | status %f%% | time %s" % (
|
||||||
|
i,
|
||||||
|
mic.hex(),
|
||||||
|
looking_for.hex(),
|
||||||
|
i / KEY_SPACE,
|
||||||
|
time.time() - starttime
|
||||||
|
)
|
||||||
sys.stdout.write('%s\r' % msg)
|
sys.stdout.write('%s\r' % msg)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
if mic == looking_for:
|
if mic == looking_for:
|
||||||
|
@ -212,7 +222,7 @@ def extension_attack(valid_pairs: list[tuple[bytearray, bytearray]]) -> tuple[by
|
||||||
break
|
break
|
||||||
print("IT IS %08x" % last_internal_state)
|
print("IT IS %08x" % last_internal_state)
|
||||||
print("=" * 120)
|
print("=" * 120)
|
||||||
forged_input: bytearray = bytearray(b'HELLO')
|
forged_input: bytearray = extending_message
|
||||||
print("Trying to forge a mic for an extended version with input:\n%08x\n(%s)" % (
|
print("Trying to forge a mic for an extended version with input:\n%08x\n(%s)" % (
|
||||||
int.from_bytes(forged_input),
|
int.from_bytes(forged_input),
|
||||||
forged_input.decode(errors="replace")
|
forged_input.decode(errors="replace")
|
||||||
|
@ -221,7 +231,8 @@ def extension_attack(valid_pairs: list[tuple[bytearray, bytearray]]) -> tuple[by
|
||||||
|
|
||||||
hacked_mic = authur1_optimized(forged_input, first_state=bytearray(last_internal_state.to_bytes(4)))
|
hacked_mic = authur1_optimized(forged_input, first_state=bytearray(last_internal_state.to_bytes(4)))
|
||||||
print("Hacked MIC: %s" % hacked_mic.hex())
|
print("Hacked MIC: %s" % hacked_mic.hex())
|
||||||
return (forged_input, hacked_mic)
|
full_msg.extend(forged_input)
|
||||||
|
return (forged_input, hacked_mic, full_msg)
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
init: int = int.from_bytes(DEFINED_INITIAL)
|
init: int = int.from_bytes(DEFINED_INITIAL)
|
||||||
|
@ -260,21 +271,27 @@ def test_extension_attack():
|
||||||
"""
|
"""
|
||||||
# this key produces a mic that is fast to bruteforce with my code
|
# this key produces a mic that is fast to bruteforce with my code
|
||||||
# (in combination with "AAAAaa" as input ofc)
|
# (in combination with "AAAAaa" as input ofc)
|
||||||
|
# test with these values, they are some of the first the brute force algo tries
|
||||||
|
#key = bytearray(0x289488ae6d71c82da1502c0130ec04e0.to_bytes(16))
|
||||||
|
#message = bytearray(b'AAAAaa')
|
||||||
key = bytearray(0x289488ae6d71c82da1502c0130ec04e0.to_bytes(16))
|
key = bytearray(0x289488ae6d71c82da1502c0130ec04e0.to_bytes(16))
|
||||||
message = bytearray(b'AAAAaa')
|
message = bytearray(b'AAAAaa')
|
||||||
# we need to bruteforce this, skip for the test
|
# we need to bruteforce this, skip for the test
|
||||||
mic = keyed_hash(message, key, True)
|
mic = keyed_hash(message, key, False)
|
||||||
print("testing against mic: %s" % mic.hex())
|
print("testing against mic: %s" % mic.hex())
|
||||||
#last_internal_state = keyed_hash(message, key, return_last_state=True)
|
#last_internal_state = keyed_hash(message, key, return_last_state=True)
|
||||||
forged_data = extension_attack([(message, mic)])
|
forged_data = extension_attack([(message, mic)])
|
||||||
print("extension attack returned the following: %s,%s" % (forged_data[0].decode(errors="replace"), forged_data[1].hex()))
|
print("extension attack returned the following:\n%s\n%s" % (forged_data[0].decode(errors="replace"), forged_data[1].hex()))
|
||||||
print("=" * 120)
|
print("=" * 120)
|
||||||
message = message[:4]
|
message = message[:4]
|
||||||
message.extend(forged_data[0])
|
message.extend(forged_data[0])
|
||||||
print("generating real mic for %x (%s)" % (int.from_bytes(message), message.decode(errors="replace")))
|
print("generating real mic for\n%x\n(%s)" % (int.from_bytes(message), message.decode(errors="replace")))
|
||||||
validated_mic = keyed_hash(message, key, True)
|
validated_mic = keyed_hash(message, key, False)
|
||||||
assert validated_mic == forged_data[1], "forged mic %s is not valid. (%s)" % ( forged_data[1].hex(), validated_mic.hex() )
|
assert validated_mic == forged_data[1], "forged mic %s is not valid. (%s)" % ( forged_data[1].hex(), validated_mic.hex() )
|
||||||
print("Manual extension attack with known last internal state works (%s == %s)" % (forged_data[1].hex(), validated_mic.hex()))
|
print("Manual extension attack with known last internal state works (%s == %s)" % (forged_data[1].hex(), validated_mic.hex()))
|
||||||
|
print("=" * 120)
|
||||||
|
print("Forged a valid MIC for the following message:\n\n%s\n\nMIC: %s" % (message.decode(errors="replace"), validated_mic.hex()))
|
||||||
|
print("=" * 120)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(prog="authur1", description='Implementation and attack for the custom authur1 hash. Don\'t actually use this hash!')
|
parser = argparse.ArgumentParser(prog="authur1", description='Implementation and attack for the custom authur1 hash. Don\'t actually use this hash!')
|
||||||
|
@ -288,6 +305,10 @@ def main():
|
||||||
help='print many things')
|
help='print many things')
|
||||||
parser.add_argument('-e', '--extension-attack', metavar="ORIGINALS", type=str,
|
parser.add_argument('-e', '--extension-attack', metavar="ORIGINALS", type=str,
|
||||||
help='perform an extension attack, this option requires known mics in the form: "msg1:deadbeed,msg2:abababab,msg3:ecbadf,..."')
|
help='perform an extension attack, this option requires known mics in the form: "msg1:deadbeed,msg2:abababab,msg3:ecbadf,..."')
|
||||||
|
parser.add_argument('-s', '--last-state-attack', metavar="HEX", type=str,
|
||||||
|
help='don\'t bruteforce the internal state and use a given one.')
|
||||||
|
parser.add_argument('-m', '--ext-message', metavar="MSG", type=str,
|
||||||
|
help='the message that is appended to the valid one')
|
||||||
parser.add_argument('-a', '--auth', action="store_true",
|
parser.add_argument('-a', '--auth', action="store_true",
|
||||||
help='generate a message integrity code (mic), needs a value to be hashed. If no key is specified, a random key will be generated.')
|
help='generate a message integrity code (mic), needs a value to be hashed. If no key is specified, a random key will be generated.')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
@ -317,8 +338,8 @@ def main():
|
||||||
hashed: bytearray = authur1(my_bytes, args.verbose)
|
hashed: bytearray = authur1(my_bytes, args.verbose)
|
||||||
print("hash for \"%s\" is:\n%s" % (args.hash, hashed.hex()))
|
print("hash for \"%s\" is:\n%s" % (args.hash, hashed.hex()))
|
||||||
exit()
|
exit()
|
||||||
elif args.extension_attack:
|
elif args.extension_attack and args.ext_message:
|
||||||
# TODO
|
msg_bytes = bytearray(args.ext_message.encode())
|
||||||
original_strs: list = args.extension_attack.split(",")
|
original_strs: list = args.extension_attack.split(",")
|
||||||
# will store our processed given messages and mics as tuples of bytearrays
|
# will store our processed given messages and mics as tuples of bytearrays
|
||||||
valid_pairs: list = []
|
valid_pairs: list = []
|
||||||
|
@ -333,7 +354,17 @@ def main():
|
||||||
print("given pair '%s' formatted incorrectly" % pair)
|
print("given pair '%s' formatted incorrectly" % pair)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
extension_attack(valid_pairs)
|
if args.last_state_attack:
|
||||||
|
state = int(args.last_state_attack, 16)
|
||||||
|
forged_data = extension_attack(valid_pairs, msg_bytes, state)
|
||||||
|
|
||||||
|
else:
|
||||||
|
forged_data = extension_attack(valid_pairs, msg_bytes)
|
||||||
|
print("Forged a valid delta:")
|
||||||
|
print("=" * 20 + "BEGIN FORGED AUTHENTICATED TEXT" + "=" * 20)
|
||||||
|
print(forged_data[2].decode())
|
||||||
|
print("=" * 20 + "END FORGED AUTHENTICATED TEXT" + "=" * 22)
|
||||||
|
print("MIC: %s" % forged_data[1].hex())
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# values from the exercise
|
# values from the exercise
|
||||||
./authur1.py -e "abcd:632e4e5c,abcdef:0f6b8802,abcdefghijk:2638a819,foobar:782a826e,barfoo:885dc316"
|
#./authur1.py -e "abcd:632e4e5c,abcdef:0f6b8802,abcdefghijk:2638a819,foobar:782a826e,barfoo:885dc316"
|
||||||
|
./authur1.py -e "abcdef:0f6b8802"
|
||||||
|
|
Loading…
Reference in New Issue