hf mf sim: add nested reader attack (needs data & rf08s nonces)

This commit is contained in:
Philippe Teuwen 2024-10-16 19:54:03 +02:00
commit 079689628b
9 changed files with 166 additions and 38 deletions

View file

@ -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]
- Changed `hf mf sim` - support data-first and nested reader attacks (@doegox)
- Fixed em4x50_read() - `lf search` and `lf em 4x50 rdbl -b <blk>` does not coredump reading EM4450 tag (@ANTodorov)
- Fixed flashing - client doesnt fail every other flash attempt (@iceman1001)
- Changed `pref show` - add option to dump as JSON (@doegox)

View file

@ -1580,7 +1580,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
}
}
switch (ar_nr_nonces[index].state) {
switch ((nonce_state)ar_nr_nonces[index].state) {
case EMPTY: {
// first nonce collect
ar_nr_nonces[index].cuid = cuid;

View file

@ -1183,7 +1183,7 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
// 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; // flag to tell we don't know the key yet
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);

View file

@ -45,6 +45,7 @@
#include "crc16.h"
#include "dbprint.h"
#include "ticks.h"
#include "parity.h"
static bool IsKeyBReadable(uint8_t blockNo) {
uint8_t sector_trailer[16];
@ -459,8 +460,9 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_
* FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that
* FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished
* FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later
* FLAG_NESTED_AUTH_ATTACK - means that we support nested authentication attack
*@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ...
* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted)
* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attempted)
*/
void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) {
tag_response_info_t *responses;
@ -539,6 +541,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
int counter = 0;
bool finished = false;
bool running_nested_auth_attack = false;
bool button_pushed = BUTTON_PRESS();
while ((button_pushed == false) && (finished == false)) {
@ -788,7 +791,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
// Load key into crypto
crypto1_init(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY));
running_nested_auth_attack = false;
if (!encrypted_data) {
// Receive Cmd in clear txt
// Update crypto state (UID ^ NONCE)
@ -812,9 +815,38 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0);
num_to_bytes(ans, 4, rAUTH_AT);
*/
// if key not known and FLAG_NESTED_AUTH_ATTACK and we have nt/nt_enc/parity, send recorded nt_enc and parity
if ((flags & FLAG_NESTED_AUTH_ATTACK) == FLAG_NESTED_AUTH_ATTACK) {
if (emlGetKey(cardAUTHSC, cardAUTHKEY) == 0) {
uint8_t buf[16] = {0};
emlGetMem(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + cardAUTHSC, 1);
if (buf[(cardAUTHKEY * 8) + 3] == 0xAA) { // extra check to tell we have nt/nt_enc/par_err
running_nested_auth_attack = true;
// nt
nonce = bytes_to_num(buf + (cardAUTHKEY * 8), 2);
nonce = nonce << 16 | prng_successor(nonce, 16);
// nt_enc
memcpy(response, buf + (cardAUTHKEY * 8) + 4, 4);
uint8_t nt_par_err = buf[(cardAUTHKEY * 8) + 2];
uint32_t nt_enc = bytes_to_num(response, 4);
response_par[0] = ((((nt_par_err >> 3) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 7 |
(((nt_par_err >> 2) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 6 |
(((nt_par_err >> 1) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 5 |
(((nt_par_err >> 0) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)) << 4);
ar_nr_resp[0].cuid = cuid;
ar_nr_resp[0].sector = cardAUTHSC;
ar_nr_resp[0].keytype = cardAUTHKEY;
ar_nr_resp[0].nonce = nonce;
ar_nr_resp[0].nonce2 = nt_enc;
};
}
}
if (running_nested_auth_attack == false) {
// rAUTH_NT, rAUTH_NT_keystream contains prepared nonce and keystream for nested authentication
// we need calculate parity bits for non-encrypted sequence
mf_crypto1_encryptEx(pcs, rAUTH_NT, rAUTH_NT_keystream, response, 4, response_par);
}
EmSendCmdPar(response, 4, response_par);
FpgaDisableTracing();
@ -1145,6 +1177,12 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
// test if auth KO
if (cardRr != prng_successor(nonce, 64)) {
// Collect AR/NR per keytype & sector
if (running_nested_auth_attack) {
ar_nr_resp[0].nr = nr;
ar_nr_resp[0].ar = ar;
ar_nr_resp[0].state = NESTED;
finished = true;
}
if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) {
for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
@ -1319,11 +1357,29 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
FpgaDisableTracing();
// NR AR ATTACK
uint8_t index = 0;
if (running_nested_auth_attack) {
if ((nonce_state)ar_nr_resp[0].state == NESTED) {
running_nested_auth_attack = false;
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Collected nested AR/NR which can be used to extract sector %d " _YELLOW_("%s")
, ar_nr_resp[0].sector
, (ar_nr_resp[0].keytype == AUTHKEYA) ? "key A" : "key B"
);
Dbprintf("../tools/mfc/card_reader/mfkey32nested %08x %08x %08x %08x %08x",
ar_nr_resp[0].cuid, //UID
ar_nr_resp[0].nonce, //NT
ar_nr_resp[0].nonce2,//NT_ENC
ar_nr_resp[0].nr, //NR1
ar_nr_resp[0].ar //AR1
);
}
}
} else {
// NR AR ATTACK
if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) {
for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
if (ar_nr_resp[i].state == SECOND) {
if ((nonce_state)ar_nr_resp[i].state == SECOND) {
index = i;
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Collected two pairs of AR/NR which can be used to extract sector %d " _YELLOW_("%s")
@ -1343,6 +1399,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
}
}
}
}
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen());
}

View file

@ -4044,7 +4044,13 @@ void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool
}
uint64_t key = 0;
if (mfkey32_moebius(&data, &key)) {
bool found = false;
if ((nonce_state)data.state == SECOND) {
found = mfkey32_moebius(&data, &key);
} else if ((nonce_state)data.state == NESTED) {
found = mfkey32_nested(&data, &key);
}
if (found) {
uint8_t sector = data.sector;
uint8_t keytype = data.keytype;
@ -4094,6 +4100,7 @@ static int CmdHF14AMfSim(const char *Cmd) {
"hf mf sim --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n"
"hf mf sim --2k --> MIFARE 2k\n"
"hf mf sim --4k --> MIFARE 4k"
"hf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys\n"
);
void *argtable[] = {
@ -4107,8 +4114,9 @@ static int CmdHF14AMfSim(const char *Cmd) {
arg_str0(NULL, "sak", "<hex>", "Provide explicit SAK (1 bytes, overrides option t)"),
arg_int0("n", "num", "<dec> ", "Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite"),
arg_lit0("i", "interactive", "Console will not be returned until simulation finishes or is aborted"),
arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"),
arg_lit0("e", "emukeys", "Fill simulator keys from found keys"),
arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader."),
arg_lit0("y", NULL, "Performs the nested 'reader attack'. This requires preloading nt & nt_enc in emulator memory. Implies -x."),
arg_lit0("e", "emukeys", "Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically."),
arg_lit0("v", "verbose", "verbose output"),
arg_lit0(NULL, "cve", "trigger CVE 2021_0430"),
arg_param_end
@ -4166,10 +4174,15 @@ static int CmdHF14AMfSim(const char *Cmd) {
flags |= FLAG_NR_AR_ATTACK;
}
bool setEmulatorMem = arg_get_lit(ctx, 11);
bool verbose = arg_get_lit(ctx, 12);
if (arg_get_lit(ctx, 11)) {
flags |= FLAG_NESTED_AUTH_ATTACK;
}
if (arg_get_lit(ctx, 13)) {
bool setEmulatorMem = arg_get_lit(ctx, 12);
bool verbose = arg_get_lit(ctx, 13);
if (arg_get_lit(ctx, 14)) {
flags |= FLAG_CVE21_0430;
}
CLIParserFree(ctx);
@ -4225,6 +4238,24 @@ static int CmdHF14AMfSim(const char *Cmd) {
return PM3_EINVARG;
}
if ((flags & FLAG_NESTED_AUTH_ATTACK) == FLAG_NESTED_AUTH_ATTACK) {
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) {
PrintAndLogEx(INFO, "Note: option -y implies -x");
flags |= FLAG_NR_AR_ATTACK;
}
}
if (setEmulatorMem) {
if ((flags & FLAG_INTERACTIVE) != FLAG_INTERACTIVE) {
PrintAndLogEx(INFO, "Note: option -e implies -i");
flags |= FLAG_INTERACTIVE;
}
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) {
PrintAndLogEx(WARNING, "Option -e requires -x or -y");
return PM3_EINVARG;
}
}
PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") ""
, csize
, uidsize
@ -4281,7 +4312,9 @@ static int CmdHF14AMfSim(const char *Cmd) {
const nonces_t *data = (nonces_t *)resp.data.asBytes;
readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose);
if (setEmulatorMem) {
cont = true;
}
break;
}
if (keypress) {

View file

@ -160,6 +160,38 @@ bool mfkey32_moebius(nonces_t *data, uint64_t *outputkey) {
return isSuccess;
}
// recover key from 2 reader responses on 2 different tag challenges
// skip "several found keys". Only return true if ONE key is found
bool mfkey32_nested(nonces_t *data, uint64_t *outputkey) {
struct Crypto1State *s, *t;
uint64_t key = 0; // recovered key
bool isSuccess = false;
uint32_t uid = data->cuid;
uint32_t nt = data->nonce;
uint32_t nt_enc = data->nonce2;
uint32_t ar = prng_successor(nt, 64);
uint32_t nr_enc = data->nr;
uint32_t ar_enc = data->ar;
uint32_t ks0 = nt_enc ^ nt;
uint32_t ks2 = ar_enc ^ ar;
s = lfsr_recovery32(ks0, uid ^ nt);
for (t = s; t->odd | t->even; ++t) {
crypto1_word(t, nr_enc, 1);
if (ks2 == crypto1_word(t, 0, 0)) {
lfsr_rollback_word(t, 0, 0);
lfsr_rollback_word(t, nr_enc, 1);
lfsr_rollback_word(t, uid ^ nt, 0);
crypto1_get_lfsr(t, &key);
isSuccess = true;
break;
}
}
*outputkey = (isSuccess) ? key : 0;
crypto1_destroy(s);
return isSuccess;
}
// recover key from reader response and tag response of one authentication sequence
int mfkey64(nonces_t *data, uint64_t *outputkey) {
uint64_t key = 0; // recovered key

View file

@ -26,6 +26,7 @@
uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint32_t ar, uint64_t par_info, uint64_t ks_info, uint64_t **keys);
bool mfkey32(nonces_t *data, uint64_t *outputkey);
bool mfkey32_moebius(nonces_t *data, uint64_t *outputkey);
bool mfkey32_nested(nonces_t *data, uint64_t *outputkey);
int mfkey64(nonces_t *data, uint64_t *outputkey);
int compare_uint64(const void *a, const void *b);

View file

@ -167,6 +167,13 @@ typedef enum {
//-----------------------------------------------------------------------------
// "hf 14a sim -x", "hf mf sim -x" attacks
//-----------------------------------------------------------------------------
typedef enum {
EMPTY,
FIRST,
SECOND,
NESTED
} nonce_state;
typedef struct {
uint32_t cuid;
uint32_t nonce;
@ -178,11 +185,7 @@ typedef struct {
uint32_t nr2;
uint8_t sector;
uint8_t keytype;
enum {
EMPTY,
FIRST,
SECOND,
} state;
uint8_t state;
} PACKED nonces_t;
#endif // _MIFARE_H_

View file

@ -780,6 +780,7 @@ typedef struct {
#define FLAG_FORCED_SAK 0x1000
#define FLAG_CVE21_0430 0x2000
#define FLAG_RATS_IN_DATA 0x4000
#define FLAG_NESTED_AUTH_ATTACK 0x8000
#define MODE_SIM_CSN 0