mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 05:13:46 -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...
|
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]
|
## [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 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)
|
- Fixed flashing - client doesnt fail every other flash attempt (@iceman1001)
|
||||||
- Changed `pref show` - add option to dump as JSON (@doegox)
|
- 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: {
|
case EMPTY: {
|
||||||
// first nonce collect
|
// first nonce collect
|
||||||
ar_nr_nonces[index].cuid = cuid;
|
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);
|
// 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
|
// store nt_par_err
|
||||||
buf[(keyType * 8) + 2] = 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);
|
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
|
||||||
// send some crap to fail auth
|
// send some crap to fail auth
|
||||||
ReaderTransmit(nack, sizeof(nack), NULL);
|
ReaderTransmit(nack, sizeof(nack), NULL);
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "crc16.h"
|
#include "crc16.h"
|
||||||
#include "dbprint.h"
|
#include "dbprint.h"
|
||||||
#include "ticks.h"
|
#include "ticks.h"
|
||||||
|
#include "parity.h"
|
||||||
|
|
||||||
static bool IsKeyBReadable(uint8_t blockNo) {
|
static bool IsKeyBReadable(uint8_t blockNo) {
|
||||||
uint8_t sector_trailer[16];
|
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_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_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_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 ...
|
*@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) {
|
void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) {
|
||||||
tag_response_info_t *responses;
|
tag_response_info_t *responses;
|
||||||
|
@ -539,6 +541,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
|
bool running_nested_auth_attack = false;
|
||||||
bool button_pushed = BUTTON_PRESS();
|
bool button_pushed = BUTTON_PRESS();
|
||||||
while ((button_pushed == false) && (finished == false)) {
|
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
|
// Load key into crypto
|
||||||
crypto1_init(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY));
|
crypto1_init(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY));
|
||||||
|
running_nested_auth_attack = false;
|
||||||
if (!encrypted_data) {
|
if (!encrypted_data) {
|
||||||
// Receive Cmd in clear txt
|
// Receive Cmd in clear txt
|
||||||
// Update crypto state (UID ^ NONCE)
|
// 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);
|
ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0);
|
||||||
num_to_bytes(ans, 4, rAUTH_AT);
|
num_to_bytes(ans, 4, rAUTH_AT);
|
||||||
*/
|
*/
|
||||||
// rAUTH_NT, rAUTH_NT_keystream contains prepared nonce and keystream for nested authentication
|
|
||||||
// we need calculate parity bits for non-encrypted sequence
|
// if key not known and FLAG_NESTED_AUTH_ATTACK and we have nt/nt_enc/parity, send recorded nt_enc and parity
|
||||||
mf_crypto1_encryptEx(pcs, rAUTH_NT, rAUTH_NT_keystream, response, 4, response_par);
|
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);
|
EmSendCmdPar(response, 4, response_par);
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
|
|
||||||
|
@ -1145,6 +1177,12 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
|
||||||
// test if auth KO
|
// test if auth KO
|
||||||
if (cardRr != prng_successor(nonce, 64)) {
|
if (cardRr != prng_successor(nonce, 64)) {
|
||||||
// Collect AR/NR per keytype & sector
|
// 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) {
|
if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) {
|
||||||
|
|
||||||
for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
|
for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
|
||||||
|
@ -1319,26 +1357,45 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1
|
||||||
|
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
|
|
||||||
// NR AR ATTACK
|
|
||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) {
|
if (running_nested_auth_attack) {
|
||||||
for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
|
if ((nonce_state)ar_nr_resp[0].state == NESTED) {
|
||||||
if (ar_nr_resp[i].state == SECOND) {
|
running_nested_auth_attack = false;
|
||||||
index = i;
|
if (g_dbglevel >= DBG_INFO) {
|
||||||
if (g_dbglevel >= DBG_INFO) {
|
Dbprintf("Collected nested AR/NR which can be used to extract sector %d " _YELLOW_("%s")
|
||||||
Dbprintf("Collected two pairs of AR/NR which can be used to extract sector %d " _YELLOW_("%s")
|
, ar_nr_resp[0].sector
|
||||||
, ar_nr_resp[i].sector
|
, (ar_nr_resp[0].keytype == AUTHKEYA) ? "key A" : "key B"
|
||||||
, (ar_nr_resp[i].keytype == AUTHKEYA) ? "key A" : "key B"
|
);
|
||||||
);
|
Dbprintf("../tools/mfc/card_reader/mfkey32nested %08x %08x %08x %08x %08x",
|
||||||
Dbprintf("../tools/mfc/card_reader/mfkey32v2 %08x %08x %08x %08x %08x %08x %08x",
|
ar_nr_resp[0].cuid, //UID
|
||||||
ar_nr_resp[i].cuid, //UID
|
ar_nr_resp[0].nonce, //NT
|
||||||
ar_nr_resp[i].nonce, //NT
|
ar_nr_resp[0].nonce2,//NT_ENC
|
||||||
ar_nr_resp[i].nr, //NR1
|
ar_nr_resp[0].nr, //NR1
|
||||||
ar_nr_resp[i].ar, //AR1
|
ar_nr_resp[0].ar //AR1
|
||||||
ar_nr_resp[i].nonce2,//NT2
|
);
|
||||||
ar_nr_resp[i].nr2, //NR2
|
}
|
||||||
ar_nr_resp[i].ar2 //AR2
|
}
|
||||||
);
|
} 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 ((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")
|
||||||
|
, ar_nr_resp[i].sector
|
||||||
|
, (ar_nr_resp[i].keytype == AUTHKEYA) ? "key A" : "key B"
|
||||||
|
);
|
||||||
|
Dbprintf("../tools/mfc/card_reader/mfkey32v2 %08x %08x %08x %08x %08x %08x %08x",
|
||||||
|
ar_nr_resp[i].cuid, //UID
|
||||||
|
ar_nr_resp[i].nonce, //NT
|
||||||
|
ar_nr_resp[i].nr, //NR1
|
||||||
|
ar_nr_resp[i].ar, //AR1
|
||||||
|
ar_nr_resp[i].nonce2,//NT2
|
||||||
|
ar_nr_resp[i].nr2, //NR2
|
||||||
|
ar_nr_resp[i].ar2 //AR2
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4044,7 +4044,13 @@ void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t key = 0;
|
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 sector = data.sector;
|
||||||
uint8_t keytype = data.keytype;
|
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 --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n"
|
||||||
"hf mf sim --2k --> MIFARE 2k\n"
|
"hf mf sim --2k --> MIFARE 2k\n"
|
||||||
"hf mf sim --4k --> MIFARE 4k"
|
"hf mf sim --4k --> MIFARE 4k"
|
||||||
|
"hf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
void *argtable[] = {
|
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_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_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("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("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader."),
|
||||||
arg_lit0("e", "emukeys", "Fill simulator keys from found keys"),
|
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("v", "verbose", "verbose output"),
|
||||||
arg_lit0(NULL, "cve", "trigger CVE 2021_0430"),
|
arg_lit0(NULL, "cve", "trigger CVE 2021_0430"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
|
@ -4166,10 +4174,15 @@ static int CmdHF14AMfSim(const char *Cmd) {
|
||||||
flags |= FLAG_NR_AR_ATTACK;
|
flags |= FLAG_NR_AR_ATTACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setEmulatorMem = arg_get_lit(ctx, 11);
|
if (arg_get_lit(ctx, 11)) {
|
||||||
bool verbose = arg_get_lit(ctx, 12);
|
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;
|
flags |= FLAG_CVE21_0430;
|
||||||
}
|
}
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
@ -4225,6 +4238,24 @@ static int CmdHF14AMfSim(const char *Cmd) {
|
||||||
return PM3_EINVARG;
|
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") ""
|
PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") ""
|
||||||
, csize
|
, csize
|
||||||
, uidsize
|
, uidsize
|
||||||
|
@ -4281,7 +4312,9 @@ static int CmdHF14AMfSim(const char *Cmd) {
|
||||||
|
|
||||||
const nonces_t *data = (nonces_t *)resp.data.asBytes;
|
const nonces_t *data = (nonces_t *)resp.data.asBytes;
|
||||||
readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose);
|
readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose);
|
||||||
cont = true;
|
if (setEmulatorMem) {
|
||||||
|
cont = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (keypress) {
|
if (keypress) {
|
||||||
|
|
|
@ -160,6 +160,38 @@ bool mfkey32_moebius(nonces_t *data, uint64_t *outputkey) {
|
||||||
return isSuccess;
|
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
|
// recover key from reader response and tag response of one authentication sequence
|
||||||
int mfkey64(nonces_t *data, uint64_t *outputkey) {
|
int mfkey64(nonces_t *data, uint64_t *outputkey) {
|
||||||
uint64_t key = 0; // recovered key
|
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);
|
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(nonces_t *data, uint64_t *outputkey);
|
||||||
bool mfkey32_moebius(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 mfkey64(nonces_t *data, uint64_t *outputkey);
|
||||||
|
|
||||||
int compare_uint64(const void *a, const void *b);
|
int compare_uint64(const void *a, const void *b);
|
||||||
|
|
|
@ -167,6 +167,13 @@ typedef enum {
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// "hf 14a sim -x", "hf mf sim -x" attacks
|
// "hf 14a sim -x", "hf mf sim -x" attacks
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
typedef enum {
|
||||||
|
EMPTY,
|
||||||
|
FIRST,
|
||||||
|
SECOND,
|
||||||
|
NESTED
|
||||||
|
} nonce_state;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t cuid;
|
uint32_t cuid;
|
||||||
uint32_t nonce;
|
uint32_t nonce;
|
||||||
|
@ -178,11 +185,7 @@ typedef struct {
|
||||||
uint32_t nr2;
|
uint32_t nr2;
|
||||||
uint8_t sector;
|
uint8_t sector;
|
||||||
uint8_t keytype;
|
uint8_t keytype;
|
||||||
enum {
|
uint8_t state;
|
||||||
EMPTY,
|
|
||||||
FIRST,
|
|
||||||
SECOND,
|
|
||||||
} state;
|
|
||||||
} PACKED nonces_t;
|
} PACKED nonces_t;
|
||||||
|
|
||||||
#endif // _MIFARE_H_
|
#endif // _MIFARE_H_
|
||||||
|
|
|
@ -780,6 +780,7 @@ typedef struct {
|
||||||
#define FLAG_FORCED_SAK 0x1000
|
#define FLAG_FORCED_SAK 0x1000
|
||||||
#define FLAG_CVE21_0430 0x2000
|
#define FLAG_CVE21_0430 0x2000
|
||||||
#define FLAG_RATS_IN_DATA 0x4000
|
#define FLAG_RATS_IN_DATA 0x4000
|
||||||
|
#define FLAG_NESTED_AUTH_ATTACK 0x8000
|
||||||
|
|
||||||
|
|
||||||
#define MODE_SIM_CSN 0
|
#define MODE_SIM_CSN 0
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue