mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 13:53:55 -07:00
Merge pull request #2648 from csBlueChip/master
add KDF functionality to rf08s 'full' card recovery script
This commit is contained in:
commit
c3571f1035
2 changed files with 81 additions and 10 deletions
|
@ -142,7 +142,10 @@ globals:
|
||||||
lprint(f" Keys not loaded, use {s} to run recovery script [slow]", prompt="[" + color("!", fg="red") + "]")
|
lprint(f" Keys not loaded, use {s} to run recovery script [slow]", prompt="[" + color("!", fg="red") + "]")
|
||||||
else:
|
else:
|
||||||
# FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack
|
# FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack
|
||||||
keyfile = recoverKeys()
|
keyfile = recoverKeys(uid=uid, kdf=[["Bambu v1", kdfBambu1]])
|
||||||
|
if keyfile == False:
|
||||||
|
lprint("Script failed - aborting")
|
||||||
|
return
|
||||||
key = loadKeys(keyfile)
|
key = loadKeys(keyfile)
|
||||||
|
|
||||||
if key is not None:
|
if key is not None:
|
||||||
|
@ -396,22 +399,34 @@ If keys cannot be loaded AND --recover is specified, then run key recovery
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
||||||
def recoverKeys():
|
def recoverKeys(uid, kdf=[[]]):
|
||||||
"""Run key recovery script"""
|
"""Run key recovery script"""
|
||||||
badrk = 0 # 'bad recovered key' count (ie. not recovered)
|
badrk = 0 # 'bad recovered key' count (ie. not recovered)
|
||||||
|
|
||||||
|
keys = False
|
||||||
|
lprint(f"\nTrying KDFs:");
|
||||||
|
for fn in kdf:
|
||||||
|
lprint(f" {fn[0]:s}", end='')
|
||||||
|
keys = fn[1](uid)
|
||||||
|
if keys != False:
|
||||||
|
lprint(" .. Success", prompt='')
|
||||||
|
break
|
||||||
|
lprint(" .. Fail", prompt='')
|
||||||
|
|
||||||
lprint("\nRunning recovery script, ETA: Less than 30 minutes")
|
lprint("\nRunning recovery script, ETA: Less than 30 minutes")
|
||||||
|
|
||||||
lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
|
lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
|
||||||
|
r = recovery(quiet=False, keyset=keys)
|
||||||
|
lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
|
||||||
|
|
||||||
|
if r == False:
|
||||||
|
return False
|
||||||
|
|
||||||
r = recovery(quiet=False)
|
|
||||||
keyfile = r['keyfile']
|
keyfile = r['keyfile']
|
||||||
rkey = r['found_keys']
|
rkey = r['found_keys']
|
||||||
# fdump = r['dumpfile']
|
# fdump = r['dumpfile']
|
||||||
# rdata = r['data']
|
# rdata = r['data']
|
||||||
|
|
||||||
lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
|
|
||||||
|
|
||||||
for k in range(0, 16+1):
|
for k in range(0, 16+1):
|
||||||
for ab in [0, 1]:
|
for ab in [0, 1]:
|
||||||
if rkey[k][ab] == "":
|
if rkey[k][ab] == "":
|
||||||
|
@ -427,9 +442,57 @@ def recoverKeys():
|
||||||
lprint(f"[{kn}/", end='', prompt='')
|
lprint(f"[{kn}/", end='', prompt='')
|
||||||
lprint("A]" if ab == 0 else "B]", end='', prompt='')
|
lprint("A]" if ab == 0 else "B]", end='', prompt='')
|
||||||
if badrk > 0:
|
if badrk > 0:
|
||||||
lprint()
|
lprint("", prompt='')
|
||||||
return keyfile
|
return keyfile
|
||||||
|
|
||||||
|
def kdfBambu1(uid):
|
||||||
|
from Cryptodome.Protocol.KDF import HKDF
|
||||||
|
from Cryptodome.Hash import SHA256
|
||||||
|
|
||||||
|
# Generate all keys
|
||||||
|
try:
|
||||||
|
# extracted from Bambu firmware
|
||||||
|
salt = bytes([0x9a,0x75,0x9c,0xf2,0xc4,0xf7,0xca,0xff,0x22,0x2c,0xb9,0x76,0x9b,0x41,0xbc,0x96])
|
||||||
|
keyA = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-A\0")
|
||||||
|
keyB = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-B\0")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# --- Grab block 13 (in sector 3) ---
|
||||||
|
cmd = f"hf mf rdbl -c 0 --key {keyA[3].hex()} --blk 12"
|
||||||
|
#lprint(f" `{cmd}`", flush=True, log=False, end='')
|
||||||
|
for retry in range(5):
|
||||||
|
p.console(cmd)
|
||||||
|
|
||||||
|
found = False
|
||||||
|
for line in p.grabbed_output.split('\n'):
|
||||||
|
if " | " in line and "# | s" not in line:
|
||||||
|
lsub = line[4:76]
|
||||||
|
found = True
|
||||||
|
if found:
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# --- Try to decode it as a bambu date string ---
|
||||||
|
try:
|
||||||
|
dl = bytes.fromhex(lsub[6:53]).decode('ascii').rstrip('\x00')
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# dl 2024_03_22_16_29
|
||||||
|
# yy y y m m d d h h m m
|
||||||
|
exp = r"20[2-3][0-9]_[0-1][0-9]_[0-3][0-9]_[0-2][0-9]_[0-5][0-9]"
|
||||||
|
if not re.search(exp, dl):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# --- valid date string, we are confident this is a bambu card ---
|
||||||
|
keys = []
|
||||||
|
for i in range(0, 15+1):
|
||||||
|
keys.append([keyA[i].hex(), keyB[i].hex()])
|
||||||
|
|
||||||
|
return keys
|
||||||
|
|
||||||
def verifyKeys(key):
|
def verifyKeys(key):
|
||||||
"""Verify keys
|
"""Verify keys
|
||||||
|
|
|
@ -76,7 +76,7 @@ for tool, bin in tools.items():
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
def recovery(init_check=False, final_check=False, keep=False, debug=False, supply_chain=False, quiet=True):
|
def recovery(init_check=False, final_check=False, keep=False, debug=False, supply_chain=False, quiet=True, keyset=False):
|
||||||
def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs):
|
def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs):
|
||||||
if not quiet:
|
if not quiet:
|
||||||
s = f"{prompt}" + f"\n{prompt}".join(s.split('\n'))
|
s = f"{prompt}" + f"\n{prompt}".join(s.split('\n'))
|
||||||
|
@ -94,7 +94,7 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
|
||||||
|
|
||||||
if uid is None:
|
if uid is None:
|
||||||
show("Card not found")
|
show("Card not found")
|
||||||
return
|
return False
|
||||||
show("UID: " + color(f"{uid:08X}", fg="green"))
|
show("UID: " + color(f"{uid:08X}", fg="green"))
|
||||||
|
|
||||||
def show_key(sec, key_type, key):
|
def show_key(sec, key_type, key):
|
||||||
|
@ -106,6 +106,14 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
|
||||||
save_path = prefs['file.default.dumppath'] + os.path.sep
|
save_path = prefs['file.default.dumppath'] + os.path.sep
|
||||||
|
|
||||||
found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
|
found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
|
||||||
|
|
||||||
|
if keyset != False:
|
||||||
|
n = min(len(found_keys),len(keyset))
|
||||||
|
show(f"{n} Key pairs supplied: ")
|
||||||
|
for i in range(0, n):
|
||||||
|
found_keys[i] = keyset[i]
|
||||||
|
show(f" Sector {i:2d} : A = {found_keys[i][0]:12s} B = {found_keys[i][1]:12s}")
|
||||||
|
|
||||||
if init_check:
|
if init_check:
|
||||||
show("Checking default keys...")
|
show("Checking default keys...")
|
||||||
p.console("hf mf fchk")
|
p.console("hf mf fchk")
|
||||||
|
@ -135,7 +143,7 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
|
||||||
|
|
||||||
if (nonces_with_data == ""):
|
if (nonces_with_data == ""):
|
||||||
show("Error getting nonces, abort.")
|
show("Error getting nonces, abort.")
|
||||||
return
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(nonces_with_data, 'r') as file:
|
with open(nonces_with_data, 'r') as file:
|
||||||
|
@ -143,7 +151,7 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
|
||||||
dict_nwd = json.load(file)
|
dict_nwd = json.load(file)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
show(f"Error parsing {nonces_with_data}, abort.")
|
show(f"Error parsing {nonces_with_data}, abort.")
|
||||||
return
|
return False
|
||||||
|
|
||||||
nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
|
nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
|
||||||
nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
|
nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue