Added support for dumping FM11RF08S data at once

This commit is contained in:
Philippe Teuwen 2024-09-03 11:43:22 +02:00
parent 258e2892ec
commit de86cd85d1
6 changed files with 110 additions and 27 deletions

View file

@ -3,7 +3,8 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added support for collecting all fm11rf08s nT/{nT}/par_err at once (@doegox)
- Added support for dumping FM11RF08S data at once (@doegox)
- Added support for collecting all FM11RF08S nT/{nT}/par_err at once (@doegox)
- Fixed `hf mfu wrbl` - compatibility write only writes 4 bytes. Now handled correctly (@iceman1001)
- Changed `hf mfu info` - better magic tag detection (@iceman1001)
- Added ELECTRA pattern decoding in `lf search` (@CiRIP)

View file

@ -1751,7 +1751,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
MifareAcquireStaticEncryptedNonces(packet->data.asBytes);
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes);
break;
}
case CMD_HF_MIFARE_ACQ_NONCES: {

View file

@ -1036,7 +1036,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
// acquire static encrypted nonces in order to perform the attack described in
// Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant"
//-----------------------------------------------------------------------------
void MifareAcquireStaticEncryptedNonces(uint8_t *key) {
void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
@ -1048,6 +1048,7 @@ void MifareAcquireStaticEncryptedNonces(uint8_t *key) {
uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00};
uint64_t ui64Key = bytes_to_num(key, 6);
bool with_data = flags & 1;
uint32_t cuid = 0;
int16_t isOK = PM3_SUCCESS;
uint16_t num_nonces = 0;
@ -1108,6 +1109,20 @@ void MifareAcquireStaticEncryptedNonces(uint8_t *key) {
isOK = PM3_ESOFT;
goto out;
};
if (with_data) {
uint8_t data[16];
for (uint8_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);
}
}
// nested authentication
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, par_enc, NULL);
if (len != 4) {

View file

@ -37,7 +37,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key);
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
void MifareAcquireStaticEncryptedNonces(uint8_t *key);
void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key);
void MifareAcquireNonces(uint32_t arg0, uint32_t flags);
void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem);
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);

View file

@ -103,14 +103,37 @@ if args.init_check:
print_key(sec, 1, found_keys[sec][1])
print("Getting nonces...")
cmd = f"hf mf isen --collect_fm11rf08s --key {BACKDOOR_RF08S}"
cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {BACKDOOR_RF08S}"
p.console(cmd)
try:
nt, nt_enc, par_err = json.loads(p.grabbed_output)
nt, nt_enc, par_err, data = json.loads(p.grabbed_output)
except json.decoder.JSONDecodeError:
print("Error getting nonces, abort.")
exit()
print("Generating first dump file")
dumpfile = f"hf-mf-{uid:08X}-dump.bin"
with (open(dumpfile, "wb")) as f:
for sec in range(NUM_SECTORS):
for b in range(4):
d = data[(sec * 4) + b]
if b == 3:
ka = found_keys[sec][0]
kb = found_keys[sec][1]
if ka == "":
ka = "FFFFFFFFFFFF"
if kb == "":
kb = "FFFFFFFFFFFF"
d = ka + d[12:20] + kb
f.write(bytes.fromhex(d))
print(f"Data have been dumped to `{dumpfile}`")
elapsed_time1 = time.time() - start_time
minutes = int(elapsed_time1 // 60)
seconds = int(elapsed_time1 % 60)
print("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
if os.path.isfile(DICT_DEF_PATH):
print(f"Loading {DICT_DEF}")
with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file:
@ -259,11 +282,11 @@ if args.debug:
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))
elapsed_time = time.time() - start_time
minutes1 = int(elapsed_time // 60)
seconds1 = int(elapsed_time % 60)
print("----Step 1: " + color(f"{minutes1:2}", fg="yellow") + " minutes " +
color(f"{seconds1:2}", fg="yellow") + " seconds -----------")
elapsed_time2 = time.time() - start_time - elapsed_time1
minutes = int(elapsed_time2 // 60)
seconds = int(elapsed_time2 % 60)
print("----Step 2: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
# fchk: 147 keys/s. Correct key found after 50% of candidates on average
FCHK_KEYS_S = 147
@ -272,7 +295,6 @@ minutes = int(foreseen_time // 60)
seconds = int(foreseen_time % 60)
print("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds to run...")
start_time = time.time()
abort = False
print("Brute-forcing keys... Press any key to interrupt")
@ -437,11 +459,31 @@ else:
if unknown:
print("[" + color("=", fg="yellow") + "] --[ " + color("FFFFFFFFFFFF", fg="yellow") +
" ]-- has been inserted for unknown keys")
print(plus + "Generating final dump file")
dumpfile = f"hf-mf-{uid:08X}-dump.bin"
with (open(dumpfile, "wb")) as f:
for sec in range(NUM_SECTORS):
for b in range(4):
d = data[(sec * 4) + b]
if b == 3:
ka = found_keys[sec][0]
kb = found_keys[sec][1]
if ka == "":
ka = "FFFFFFFFFFFF"
if kb == "":
kb = "FFFFFFFFFFFF"
d = ka + d[12:20] + kb
f.write(bytes.fromhex(d))
print(plus + "Data have been dumped to `" + color(dumpfile, fg="yellow")+"`")
elapsed_time3 = time.time() - start_time - elapsed_time1 - elapsed_time2
minutes = int(elapsed_time3 // 60)
seconds = int(elapsed_time3 % 60)
print("----Step 3: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
elapsed_time = time.time() - start_time
minutes2 = int(elapsed_time // 60)
seconds2 = int(elapsed_time % 60)
print("----Step 2: " + color(f"{minutes2:2}", fg="yellow") + " minutes " +
color(f"{seconds2:2}", fg="yellow") + " seconds -----------")
print("---- TOTAL: " + color(f"{minutes1+minutes2:2}", fg="yellow") + " minutes " +
color(f"{seconds1+seconds2:2}", fg="yellow") + " seconds -----------")
minutes = int(elapsed_time // 60)
seconds = int(elapsed_time % 60)
print("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds -----------")

View file

@ -9727,7 +9727,10 @@ static int CmdHF14AMfISEN(const char *Cmd) {
arg_lit0(NULL, "incblk2", "auth(blk)-auth(blk2)-auth(blk2+4)-..."),
arg_lit0(NULL, "corruptnrar", "corrupt {nR}{aR}, but with correct parity"),
arg_lit0(NULL, "corruptnrarparity", "correct {nR}{aR}, but with corrupted parity"),
arg_lit0(NULL, "collect_fm11rf08s", "correct all nT/{nT}/par_err of FM11RF08S in JSON. Option to be used only with -k."),
arg_rem("", ""),
arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"),
arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."),
arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
@ -9793,7 +9796,11 @@ static int CmdHF14AMfISEN(const char *Cmd) {
bool incblk2 = arg_get_lit(ctx, 16);
bool corruptnrar = arg_get_lit(ctx, 17);
bool corruptnrarparity = arg_get_lit(ctx, 18);
bool collect_fm11rf08s = arg_get_lit(ctx, 19);
bool collect_fm11rf08s = arg_get_lit(ctx, 21);
bool collect_fm11rf08s_with_data = arg_get_lit(ctx, 22);
if (collect_fm11rf08s_with_data) {
collect_fm11rf08s = 1;
}
CLIParserFree(ctx);
uint8_t dbg_curr = DBG_NONE;
@ -9840,14 +9847,14 @@ static int CmdHF14AMfISEN(const char *Cmd) {
}
if (collect_fm11rf08s) {
SendCommandNG(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, key, sizeof(key));
uint32_t flags = collect_fm11rf08s_with_data;
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key));
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) {
if (resp.status == PM3_ESOFT) {
return NONCE_FAIL;
}
}
uint8_t num_sectors = 16;
// TODO: get nonces and display
PrintAndLogEx(NORMAL, "[\n [");
for (uint8_t sec = 0; sec < num_sectors; sec++) {
PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s",
@ -9871,12 +9878,30 @@ static int CmdHF14AMfISEN(const char *Cmd) {
(p1 >> 3) & 1, (p1 >> 2) & 1, (p1 >> 1) & 1, p1 & 1,
sec < num_sectors - 1 ? "," : "");
}
PrintAndLogEx(NORMAL, " ]\n]");
if (collect_fm11rf08s_with_data) {
PrintAndLogEx(NORMAL, " ],\n [");
int bytes = num_sectors * 4 * 16;
// PrintAndLogEx(SUCCESS, " nT: " _GREEN_("%s"), sprint_hex(resp.data.asBytes + (((sec * 2) + keyType) * 9), 4));
// PrintAndLogEx(SUCCESS, " {nT}: " _GREEN_("%s"), sprint_hex(resp.data.asBytes + (((sec * 2) + keyType) * 9) + 4, 4));
// // TODO: wrong par:
// PrintAndLogEx(SUCCESS, " par: " _GREEN_("%02x"), resp.data.asBytes[(((sec * 2) + keyType) * 9) + 8]);
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EFAILED;
}
if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
free(dump);
return PM3_ETIMEOUT;
}
for (uint8_t sec = 0; sec < num_sectors; 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) ? "" : ",");
}
}
free(dump);
}
PrintAndLogEx(NORMAL, " ]\n]");
return PM3_SUCCESS;
}