hf mf isen: add collect_fm11rf08s_without_backdoor option

This commit is contained in:
Philippe Teuwen 2024-11-20 09:33:21 +01:00
commit 830549b474
6 changed files with 239 additions and 104 deletions

View file

@ -1778,7 +1778,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true);
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]);
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"
//-----------------------------------------------------------------------------
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply) {
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
@ -1055,6 +1055,10 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
uint8_t buf[MIFARE_BLOCK_SIZE] = {0x00};
uint64_t ui64Key = bytes_to_num(key, 6);
bool with_data = flags & 1;
bool without_backdoor = (flags >> 1) & 1;
if (with_data && without_backdoor) {
return PM3_EINVARG;
}
uint32_t cuid = 0;
int16_t isOK = PM3_SUCCESS;
uint8_t cascade_levels = 0;
@ -1072,6 +1076,115 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
LED_C_ON();
if (without_backdoor) {
uint32_t nt1 = 0;
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)");
isOK = PM3_ERFTRANS;
goto out;
}
switch (card_info.uidlen) {
case 4 :
cascade_levels = 1;
break;
case 7 :
cascade_levels = 2;
break;
case 10:
cascade_levels = 3;
break;
default:
break;
}
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
isOK = PM3_ESOFT;
goto out;
};
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + first_key_type, first_block_no, receivedAnswer, sizeof(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);
// send some crap to fail auth
CHK_TIMEOUT();
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
isOK = PM3_ERFTRANS;
goto out;
}
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
isOK = PM3_ESOFT;
goto out;
};
// Recover clear nt
struct Crypto1State mpcs_tmp = {0, 0};
struct Crypto1State *pcs_tmp = &mpcs_tmp;
crypto1_init(pcs_tmp, ui64Key);
uint32_t nt = crypto1_word(pcs_tmp, nt_enc ^ cuid, 1) ^ nt_enc;
int dist = nonce_distance(nt, nt1);
// ref dist is not always stable. Adjust physical distance to maximise ref dist, and try values around estimated nonces...
Dbprintf("Block %2i key %i nested nT=%08x first nT=%08x dist=%i", first_block_no, first_key_type, nt, nt1, dist);
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()) {
isOK = PM3_EOPABORTED;
break;
}
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
if (len != 4) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
isOK = PM3_ESOFT;
goto out;
}
// store nt_enc
memcpy(buf + (keyType * 8) + 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);
// store nt_par_err
buf[(keyType * 8) + 2] = nt_par_err;
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
// send some crap to fail auth
CHK_TIMEOUT();
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
isOK = PM3_ERFTRANS;
goto out;
}
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
isOK = PM3_ESOFT;
goto out;
};
nt1 = rewind_nonce(nt1, dist);
num_to_bytes(nt1 >> 16, 2, buf + (keyType * 8));
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
}
}
} else {
for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) {
uint16_t sec_gap = sec;
if (sec >= MIFARE_1K_MAXSECTOR) {
@ -1152,8 +1265,7 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
// store nt (first half)
num_to_bytes(nt >> 16, 2, buf + (keyType * 8));
// send some crap to fail auth
uint8_t nack[] = {0x04};
ReaderTransmit(nack, sizeof(nack), NULL);
CHK_TIMEOUT();
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
@ -1186,7 +1298,8 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
// send some crap to fail auth
ReaderTransmit(nack, sizeof(nack), NULL);
CHK_TIMEOUT();
}
}
}
out:
@ -3127,7 +3240,8 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
goto OUT;
};
first_nt_counter++;
} else for (uint8_t i = 0; i < nr_nested; i++) {
} else {
for (uint8_t i = 0; i < nr_nested; i++) {
if (need_first_auth) {
cuid = 0;
@ -3204,6 +3318,7 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
}
oldntenc = ntenc;
}
}
data[1] = (cuid >> 24) & 0xFF;
data[2] = (cuid >> 16) & 0xFF;

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);
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply);
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type);
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

@ -983,3 +983,12 @@ int nonce_distance(uint32_t from, uint32_t to) {
int nonce16_index(uint16_t nt) {
return nonce16_distance(0x0100, nt) + 1;
}
uint32_t rewind_nonce(uint32_t from, uint16_t dist) {
uint16_t x = from >> 16;
for (uint16_t i = 0; i < dist; i++) {
x = ((x << 1 | x >> 15) & 0xffff) ^ ((x >> 1 ^ x >> 2 ^ x >> 4) & 0x100);
}
uint32_t nt = x;
return nt << 16 | prng_successor(nt, 16);
}

View file

@ -128,4 +128,5 @@ bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt);
int nonce_distance(uint32_t from, uint32_t to);
int nonce16_distance(uint16_t x, uint16_t y);
int nonce16_index(uint16_t nt);
uint32_t rewind_nonce(uint32_t from, uint16_t dist);
#endif

View file

@ -9883,6 +9883,7 @@ static int CmdHF14AMfISEN(const char *Cmd) {
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_lit0(NULL, "collect_fm11rf08s_without_backdoor", "collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block"),
arg_str0("f", "file", "<fn>", "Specify a filename for collected data"),
arg_param_end
};
@ -9954,9 +9955,18 @@ static int CmdHF14AMfISEN(const char *Cmd) {
if (collect_fm11rf08s_with_data) {
collect_fm11rf08s = 1;
}
bool collect_fm11rf08s_without_backdoor = arg_get_lit(ctx, 23);
if (collect_fm11rf08s_without_backdoor) {
collect_fm11rf08s = 1;
}
if (collect_fm11rf08s_with_data && collect_fm11rf08s_without_backdoor) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Don't mix with_data and without_backdoor options");
return PM3_EINVARG;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 23), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParamStrToBuf(arg_get_str(ctx, 24), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
@ -10005,8 +10015,8 @@ static int CmdHF14AMfISEN(const char *Cmd) {
if (collect_fm11rf08s) {
uint64_t t1 = msclock();
uint32_t flags = collect_fm11rf08s_with_data;
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key));
uint32_t flags = collect_fm11rf08s_with_data | (collect_fm11rf08s_without_backdoor << 1);
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, blockn, keytype, key, sizeof(key));
if (WaitForResponseTimeout(CMD_ACK, &resp, 1000)) {
if (resp.oldarg[0] != PM3_SUCCESS) {
return NONCE_FAIL;