diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 38c8a5a6f..96bba263f 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1045,8 +1045,8 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { uint8_t uid[10] = {0x00}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t par_enc[1] = {0x00}; - uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; - + // ((MIFARE_1K_MAXSECTOR + 1) * 2) * 9 < PM3_CMD_DATA_SIZE + uint8_t buf[((MIFARE_1K_MAXSECTOR + 1) * 2) * 9] = {0x00}; uint64_t ui64Key = bytes_to_num(key, 6); bool with_data = flags & 1; uint32_t cuid = 0; @@ -1054,7 +1054,6 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { uint16_t num_nonces = 0; uint8_t cascade_levels = 0; bool have_uid = false; - uint8_t num_sectors = 16; LED_A_ON(); LED_C_OFF(); @@ -1068,8 +1067,13 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { LED_C_ON(); - for (uint16_t sec = 0; sec < num_sectors; sec++) { - uint8_t blockNo = sec * 4; + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint16_t sec_gap = sec; + if (sec >= MIFARE_1K_MAXSECTOR) { + // gap between user blocks and advanced verification method blocks + sec_gap += 16; + } + uint16_t blockNo = sec_gap * 4; for (uint8_t keyType = 0; keyType < 2; keyType++) { // Test if the action was cancelled if (BUTTON_PRESS()) { @@ -1110,19 +1114,20 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { goto out; }; if (with_data) { - uint8_t data[16]; - for (uint16_t tb = blockNo; tb < blockNo + 4; tb++) { - memset(data, 0x00, sizeof(data)); - int res = mifare_classic_readblock(pcs, tb, data); - if (res == 1) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error"); - isOK = PM3_ESOFT; - goto out; + if (blockNo < MIFARE_1K_MAXSECTOR * 4) { + uint8_t data[16]; + for (uint16_t tb = blockNo; tb < blockNo + 4; tb++) { + memset(data, 0x00, sizeof(data)); + int res = mifare_classic_readblock(pcs, tb, data); + if (res == 1) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error"); + isOK = PM3_ESOFT; + goto out; + } + emlSetMem_xt(data, tb, 1, 16); } - emlSetMem_xt(data, tb, 1, 16); } } - // nested authentication uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); if (len != 4) { diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 0b6586221..8f5ca65a1 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -31,6 +31,7 @@ except ModuleNotFoundError: BACKDOOR_RF08S = "A396EFA4E24F" NUM_SECTORS = 16 +NUM_EXTRA_SECTORS = 1 DICT_DEF = "mfc_default_keys.dic" DEFAULT_KEYS = set() if os.path.basename(os.path.dirname(os.path.dirname(sys.argv[0]))) == 'client': @@ -87,7 +88,7 @@ def print_key(sec, key_type, key): print(f"Sector {sec:2} key{kt} = " + color(key, fg="green")) -found_keys = [["", ""] for _ in range(NUM_SECTORS)] +found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] if args.init_check: print("Checking default keys...") p.console("hf mf fchk") @@ -144,31 +145,34 @@ else: print(f"Warning, {DICT_DEF} not found.") print("Running staticnested_1nt & 2x1nt when doable...") -keys = [[set(), set()] for _ in range(NUM_SECTORS)] +keys = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] all_keys = set() duplicates = set() # Availability of filtered dicts -filtered_dicts = [[False, False] for _ in range(NUM_SECTORS)] -found_default = [[False, False] for _ in range(NUM_SECTORS)] -for sec in range(NUM_SECTORS): +filtered_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] +found_default = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] +for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 if found_keys[sec][0] != "" and found_keys[sec][1] != "": continue if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: for key_type in [0, 1]: - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{sec}", + cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] if args.debug: print(' '.join(cmd)) subprocess.run(cmd, capture_output=True) cmd = [tools["staticnested_2x1nt"], - f"keys_{uid:08x}_{sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{sec:02}_{nt[sec][1]}.dic"] + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"] if args.debug: print(' '.join(cmd)) subprocess.run(cmd, capture_output=True) filtered_dicts[sec][key_type] = True for key_type in [0, 1]: keys_set = set() - with (open(f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_filtered.dic")) as f: + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic")) as f: while line := f.readline().rstrip(): keys_set.add(line) keys[sec][key_type] = keys_set.copy() @@ -177,9 +181,14 @@ for sec in range(NUM_SECTORS): # Prioritize default keys keys_def_set = DEFAULT_KEYS.intersection(keys_set) keys_set.difference_update(DEFAULT_KEYS) + # Prioritize sector 32 keyB starting with 0000 + if real_sec == 32: + keyb32cands = set(x for x in keys_set if x.startswith("0000")) + keys_def_set.update(keyb32cands) + keys_set.difference_update(keyb32cands) if len(keys_def_set) > 0: found_default[sec][key_type] = True - with (open(f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_filtered.dic", "w")) as f: + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic", "w")) as f: for k in keys_def_set: f.write(f"{k}\n") for k in keys_set: @@ -189,13 +198,13 @@ for sec in range(NUM_SECTORS): key_type = 0 else: key_type = 1 - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{sec}", + cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] if args.debug: print(' '.join(cmd)) subprocess.run(cmd, capture_output=True) keys_set = set() - with (open(f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}.dic")) as f: + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic")) as f: while line := f.readline().rstrip(): keys_set.add(line) keys[sec][key_type] = keys_set.copy() @@ -206,30 +215,33 @@ for sec in range(NUM_SECTORS): keys_set.difference_update(DEFAULT_KEYS) if len(keys_def_set) > 0: found_default[sec][key_type] = True - with (open(f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}.dic", "w")) as f: + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic", "w")) as f: for k in keys_def_set: f.write(f"{k}\n") for k in keys_set: f.write(f"{k}\n") print("Looking for common keys across sectors...") -keys_filtered = [[set(), set()] for _ in range(NUM_SECTORS)] +keys_filtered = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] for dup in duplicates: - for sec in range(NUM_SECTORS): + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): for key_type in [0, 1]: if dup in keys[sec][key_type]: keys_filtered[sec][key_type].add(dup) # Availability of duplicates dicts -duplicates_dicts = [[False, False] for _ in range(NUM_SECTORS)] +duplicates_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] first = True -for sec in range(NUM_SECTORS): +for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 for key_type in [0, 1]: if len(keys_filtered[sec][key_type]) > 0: if first: print("Saving duplicates dicts...") first = False - with (open(f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_duplicates.dic", "w")) as f: + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic", "w")) as f: keys_set = keys_filtered[sec][key_type].copy() keys_def_set = DEFAULT_KEYS.intersection(keys_set) keys_set.difference_update(DEFAULT_KEYS) @@ -240,12 +252,15 @@ for sec in range(NUM_SECTORS): duplicates_dicts[sec][key_type] = True print("Computing needed time for attack...") -candidates = [[0, 0] for _ in range(NUM_SECTORS)] -for sec in range(NUM_SECTORS): +candidates = [[0, 0] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] +for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 for key_type in [0, 1]: if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_duplicates.dic" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" with open(dic, 'r') as file: count = sum(1 for _ in file) # print(f"dic {dic} size {count}") @@ -259,7 +274,7 @@ for sec in range(NUM_SECTORS): candidates[sec][key_type] = 1 else: kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_filtered.dic" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" with open(dic, 'r') as file: count = sum(1 for _ in file) # print(f"dic {dic} size {count}") @@ -272,7 +287,7 @@ for sec in range(NUM_SECTORS): else: key_type = 0 kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}.dic" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" with open(dic, 'r') as file: count = sum(1 for _ in file) # print(f"dic {dic} size {count}") @@ -280,9 +295,12 @@ for sec in range(NUM_SECTORS): candidates[sec][1] = 1 if args.debug: - for sec in range(NUM_SECTORS): - print(f" {sec:03} | {sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ") -total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS)) + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + print(f" {real_sec:03} | {real_sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ") +total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS)) elapsed_time2 = time.time() - start_time - elapsed_time1 minutes = int(elapsed_time2 // 60) @@ -300,15 +318,18 @@ print("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " + abort = False print("Brute-forcing keys... Press any key to interrupt") -for sec in range(NUM_SECTORS): +for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 for key_type in [0, 1]: # If we have a duplicates dict # note: we skip if we already know one key # as using 2x1nt1key later will be faster if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_duplicates.dic" - cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) p.console(cmd) @@ -316,11 +337,11 @@ for sec in range(NUM_SECTORS): if "aborted via keyboard" in line: abort = True if "found:" in line: - found_keys[sec][key_type] = line[30:] - print_key(sec, key_type, found_keys[sec][key_type]) + found_keys[sec][key_type] = line[30:].strip() + print_key(real_sec, key_type, found_keys[sec][key_type]) if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "": found_keys[sec][key_type ^ 1] = found_keys[sec][key_type] - print_key(sec, key_type ^ 1, found_keys[sec][key_type ^ 1]) + print_key(real_sec, key_type ^ 1, found_keys[sec][key_type ^ 1]) if abort: break if abort: @@ -333,8 +354,8 @@ for sec in range(NUM_SECTORS): if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type]: # Use filtered dict kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}_filtered.dic" - cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) p.console(cmd) @@ -342,8 +363,8 @@ for sec in range(NUM_SECTORS): if "aborted via keyboard" in line: abort = True if "found:" in line: - found_keys[sec][key_type] = line[30:] - print_key(sec, key_type, found_keys[sec][key_type]) + found_keys[sec][key_type] = line[30:].strip() + print_key(real_sec, key_type, found_keys[sec][key_type]) if abort: break if abort: @@ -354,8 +375,8 @@ for sec in range(NUM_SECTORS): key_type = 0 # Use regular dict kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type]}.dic" - cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) p.console(cmd) @@ -363,10 +384,10 @@ for sec in range(NUM_SECTORS): if "aborted via keyboard" in line: abort = True if "found:" in line: - found_keys[sec][0] = line[30:] - found_keys[sec][1] = line[30:] - print_key(sec, 0, found_keys[sec][key_type]) - print_key(sec, 1, found_keys[sec][key_type]) + found_keys[sec][0] = line[30:].strip() + found_keys[sec][1] = line[30:].strip() + print_key(real_sec, 0, found_keys[sec][key_type]) + print_key(real_sec, 1, found_keys[sec][key_type]) if abort: break @@ -379,11 +400,11 @@ for sec in range(NUM_SECTORS): key_type_source = 0 key_type_target = 1 if duplicates_dicts[sec][key_type_target]: - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type_target]}_duplicates.dic" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_duplicates.dic" elif filtered_dicts[sec][key_type_target]: - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type_target]}_filtered.dic" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic" else: - dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type_target]}.dic" + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic" cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic] if args.debug: print(' '.join(cmd)) @@ -394,7 +415,7 @@ for sec in range(NUM_SECTORS): keys.add(line[12:]) if len(keys) > 1: kt = ['a', 'b'][key_type_target] - cmd = f"hf mf fchk --blk {sec * 4} -{kt} --no-default" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default" for k in keys: cmd += f" -k {k}" if args.debug: @@ -404,11 +425,11 @@ for sec in range(NUM_SECTORS): if "aborted via keyboard" in line: abort = True if "found:" in line: - found_keys[sec][key_type_target] = line[30:] + found_keys[sec][key_type_target] = line[30:].strip() elif len(keys) == 1: found_keys[sec][key_type_target] = keys.pop() if found_keys[sec][key_type_target] != "": - print_key(sec, key_type_target, found_keys[sec][key_type_target]) + print_key(real_sec, key_type_target, found_keys[sec][key_type_target]) if abort: break @@ -434,14 +455,17 @@ else: print(plus + "-----+-----+--------------+---+--------------+----") print(plus + " Sec | Blk | key A |res| key B |res") print(plus + "-----+-----+--------------+---+--------------+----") - for sec in range(NUM_SECTORS): + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 keys = [["", 0], ["", 0]] for key_type in [0, 1]: if found_keys[sec][key_type] == "": keys[key_type] = [color("------------", fg="red"), color("0", fg="red")] else: keys[key_type] = [color(found_keys[sec][key_type], fg="green"), color("1", fg="green")] - print(plus + f" {sec:03} | {sec*4+3:03} | {keys[0][0]} | {keys[0][1]} | {keys[1][0]} | {keys[1][1]} ") + print(plus + f" {real_sec:03} | {real_sec*4+3:03} | {keys[0][0]} | {keys[0][1]} | {keys[1][0]} | {keys[1][1]} ") print(plus + "-----+-----+--------------+---+--------------+----") print(plus + "( " + color("0", fg="red") + ":Failed / " + color("1", fg="green") + ":Success )") @@ -451,7 +475,7 @@ else: unknown = False with (open(keyfile, "wb")) as f: for key_type in [0, 1]: - for sec in range(NUM_SECTORS): + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): k = found_keys[sec][key_type] if k == "": k = "FFFFFFFFFFFF" diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index d96b2596a..0e9a41846 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9863,7 +9863,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { return NONCE_FAIL; } } - uint8_t num_sectors = 16; + uint8_t num_sectors = MIFARE_1K_MAXSECTOR + 1; PrintAndLogEx(NORMAL, "[\n ["); for (uint8_t sec = 0; sec < num_sectors; sec++) { PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s", @@ -9889,7 +9889,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { } if (collect_fm11rf08s_with_data) { PrintAndLogEx(NORMAL, " ],\n ["); - int bytes = num_sectors * 4 * 16; + int bytes = MIFARE_1K_MAXSECTOR * 4 * 16; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { @@ -9901,11 +9901,11 @@ static int CmdHF14AMfISEN(const char *Cmd) { free(dump); return PM3_ETIMEOUT; } - for (uint8_t sec = 0; sec < num_sectors; sec++) { + for (uint8_t sec = 0; sec < MIFARE_1K_MAXSECTOR; sec++) { for (uint8_t b = 0; b < 4; b++) { PrintAndLogEx(NORMAL, " \"%s\"%s", sprint_hex_inrow(dump + ((sec * 4) + b) * 16, 16), - (sec == num_sectors - 1) && (b == 3) ? "" : ","); + (sec == MIFARE_1K_MAXSECTOR - 1) && (b == 3) ? "" : ","); } } free(dump);