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:
iceman1001 2020-01-14 16:00:31 +01:00
commit b37a4c14eb
9 changed files with 478 additions and 19 deletions

View file

@ -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"},