mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 13:23:51 -07:00
Improved the reliability, speed and redability. Ready for a pull mr. iceman ;)
This commit is contained in:
parent
abb69e2dc9
commit
87cbf3aa70
3 changed files with 233 additions and 287 deletions
475
client/cmdhfmf.c
475
client/cmdhfmf.c
|
@ -169,15 +169,16 @@ static int usage_hf14_hardnested(void) {
|
||||||
static int usage_hf14_hardautopwn(void) {
|
static int usage_hf14_hardautopwn(void) {
|
||||||
PrintAndLogEx(NORMAL, "Usage:");
|
PrintAndLogEx(NORMAL, "Usage:");
|
||||||
PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] <sector number> <key A|B> <key (12 hex symbols)>");
|
PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] <sector number> <key A|B> <key (12 hex symbols)>");
|
||||||
PrintAndLogEx(NORMAL, " * <card memory> [d] [f] [s] [t] [i]");
|
PrintAndLogEx(NORMAL, " * <card memory> [d] [f] [s] [i] [l]");
|
||||||
PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, <other> - 1K)");
|
PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, <other> - 1K)");
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(NORMAL, "Options:");
|
PrintAndLogEx(NORMAL, "Options:");
|
||||||
PrintAndLogEx(NORMAL, " h this help");
|
PrintAndLogEx(NORMAL, " h this help");
|
||||||
PrintAndLogEx(NORMAL, " k <sector> <keytype> <key> if a known key for a block is supplied");
|
PrintAndLogEx(NORMAL, " k <sector> <keytype> <key> if a known key for a block is supplied");
|
||||||
PrintAndLogEx(NORMAL, " d write keys to binary file");
|
PrintAndLogEx(NORMAL, " d write keys to binary file");
|
||||||
PrintAndLogEx(NORMAL, " f <name> keys to test (speed up the cracking, if some keys are known)");
|
PrintAndLogEx(NORMAL, " f <name>.dic dictionary file for key discovery (the file has to end in .dic)");
|
||||||
PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)");
|
PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)");
|
||||||
|
PrintAndLogEx(NORMAL, " l legacy mode (use the slow mfchk for the key enumeration)");
|
||||||
PrintAndLogEx(NORMAL, " i <X> set type of SIMD instructions. Without this flag programs autodetect it.");
|
PrintAndLogEx(NORMAL, " i <X> set type of SIMD instructions. Without this flag programs autodetect it.");
|
||||||
PrintAndLogEx(NORMAL, " i 5 = AVX512");
|
PrintAndLogEx(NORMAL, " i 5 = AVX512");
|
||||||
PrintAndLogEx(NORMAL, " i 2 = AVX2");
|
PrintAndLogEx(NORMAL, " i 2 = AVX2");
|
||||||
|
@ -187,9 +188,12 @@ static int usage_hf14_hardautopwn(void) {
|
||||||
PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)");
|
PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)");
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(NORMAL, "Examples:");
|
PrintAndLogEx(NORMAL, "Examples:");
|
||||||
|
PrintAndLogEx(NORMAL, " hf mf hardautopwn d");
|
||||||
|
PrintAndLogEx(NORMAL, " hf mf hardautopwn * 1 d f default_keys");
|
||||||
PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF d");
|
PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF d");
|
||||||
PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF * 1 d f default_keys.dic");
|
PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF * 1 d f default_keys");
|
||||||
PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF * 4 s i 5");
|
PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF * 4 s i 5");
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1559,57 +1563,44 @@ static int CmdHF14AMfNestedHard(const char *Cmd) {
|
||||||
|
|
||||||
|
|
||||||
static int CmdHF14AMfHardAuto(const char *Cmd) {
|
static int CmdHF14AMfHardAuto(const char *Cmd) {
|
||||||
/*
|
|
||||||
Author: Matthias Konrath
|
|
||||||
Company: Trustworks GmbH
|
|
||||||
Email: m.konrath@trustworks.at
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint8_t blockNo = 0;
|
uint8_t blockNo = 0;
|
||||||
uint8_t keyType = 0;
|
uint8_t keyType = 0;
|
||||||
uint8_t *keyBlock, *p;
|
uint8_t *keyBlock;
|
||||||
uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR;
|
uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR;
|
||||||
sector_t *e_sector;
|
sector_t *e_sector;
|
||||||
uint8_t arr[80];
|
uint8_t key[6] = {0};
|
||||||
uint8_t key[6] = {0, 0, 0, 0, 0, 0};
|
uint8_t tmpKey[6] = {0};
|
||||||
uint8_t tmpKey[6];
|
|
||||||
uint64_t key64 = 0;
|
uint64_t key64 = 0;
|
||||||
|
uint16_t keycnt = 0;
|
||||||
|
size_t datalen = 0;
|
||||||
|
uint32_t chunksize;
|
||||||
|
|
||||||
uint64_t t1;
|
uint64_t t1; // For the timier
|
||||||
uint8_t foundKeysDictionary = 0;
|
uint8_t foundKeysDictionary = 0;
|
||||||
uint8_t foundKeysReuse = 0;
|
uint8_t foundKeysReused = 0;
|
||||||
uint8_t foundKeysHardnested = 0;
|
uint8_t foundKeysHardnested = 0;
|
||||||
|
|
||||||
keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6);
|
char filename[FILE_PATH_SIZE] = {0};
|
||||||
if (keyBlock == NULL) return 1;
|
|
||||||
|
|
||||||
for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++)
|
|
||||||
num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6);
|
|
||||||
|
|
||||||
FILE* f;
|
|
||||||
char buf[13] = {0};
|
|
||||||
char filename[FILE_PATH_SIZE] = {0}, *fptr;
|
|
||||||
uint8_t cmdp = 0;
|
uint8_t cmdp = 0;
|
||||||
char ctmp;
|
char ctmp;
|
||||||
|
|
||||||
uint64_t foundkey = 0;
|
uint64_t foundkey = 0;
|
||||||
int16_t isOK = 0;
|
int16_t isOK = 0;
|
||||||
|
|
||||||
int i, i2, keycnt = 0;;
|
int i, i2, i3; // Loop counter
|
||||||
int current_sector_i, current_key_type_i, default_keys_i, found_keys_i;
|
int current_sector_i = 0, current_key_type_i = 0;
|
||||||
uint32_t keyitems = ARRAYLEN(g_mifare_default_keys);
|
|
||||||
|
|
||||||
bool slow = false;
|
bool slow = false;
|
||||||
bool nonce_file_read = false;
|
bool nonce_file_read = false;
|
||||||
bool nonce_file_write = false;
|
bool nonce_file_write = false;
|
||||||
bool createDumpFile = false;
|
bool createDumpFile = false;
|
||||||
bool know_target_key = false;
|
bool know_target_key = false;
|
||||||
int tests = 0;
|
bool legacy_mfchk = false;
|
||||||
|
bool firstChunk = 0, lastChunk = 0;
|
||||||
|
|
||||||
ctmp = tolower(param_getchar(Cmd, 0));
|
ctmp = tolower(param_getchar(Cmd, 0));
|
||||||
if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_hardautopwn();
|
if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_hardautopwn();
|
||||||
|
|
||||||
|
|
||||||
while ((ctmp = param_getchar(Cmd, cmdp))) {
|
while ((ctmp = param_getchar(Cmd, cmdp))) {
|
||||||
switch (tolower(ctmp)) {
|
switch (tolower(ctmp)) {
|
||||||
case 'h':
|
case 'h':
|
||||||
|
@ -1623,24 +1614,12 @@ static int CmdHF14AMfHardAuto(const char *Cmd) {
|
||||||
case 'd':
|
case 'd':
|
||||||
createDumpFile = true;
|
createDumpFile = true;
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
legacy_mfchk = true;
|
||||||
|
break;
|
||||||
case '*':
|
case '*':
|
||||||
// sectors
|
// Get the number of sectors
|
||||||
switch (param_getchar(Cmd, cmdp + 1)) {
|
sectorsCnt = NumOfSectors(param_getchar(Cmd, cmdp + 1));
|
||||||
case '0':
|
|
||||||
sectorsCnt = MIFARE_MINI_MAXSECTOR;
|
|
||||||
break;
|
|
||||||
case '1':
|
|
||||||
sectorsCnt = MIFARE_1K_MAXSECTOR;
|
|
||||||
break;
|
|
||||||
case '2':
|
|
||||||
sectorsCnt = MIFARE_2K_MAXSECTOR;
|
|
||||||
break;
|
|
||||||
case '4':
|
|
||||||
sectorsCnt = MIFARE_4K_MAXSECTOR;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sectorsCnt = MIFARE_1K_MAXSECTOR;
|
|
||||||
}
|
|
||||||
cmdp ++;
|
cmdp ++;
|
||||||
break;
|
break;
|
||||||
case 'k':
|
case 'k':
|
||||||
|
@ -1707,31 +1686,22 @@ static int CmdHF14AMfHardAuto(const char *Cmd) {
|
||||||
|
|
||||||
// Print parameters
|
// Print parameters
|
||||||
PrintAndLogEx(NORMAL, "Used Parameters:");
|
PrintAndLogEx(NORMAL, "Used Parameters:");
|
||||||
PrintAndLogEx(NORMAL, "[+] Dumping the found keys: %s", createDumpFile ? "True" : "False");
|
PrintAndLogEx(SUCCESS, "Dumping the found keys: %s", createDumpFile ? "True" : "False");
|
||||||
PrintAndLogEx(NORMAL, "[+] Card sectors: %d", sectorsCnt);
|
PrintAndLogEx(SUCCESS, "Card sectors: %d", sectorsCnt);
|
||||||
PrintAndLogEx(NORMAL, "[+] Key supplied: %s", know_target_key ? "True" : "False");
|
PrintAndLogEx(SUCCESS, "Key supplied: %s", know_target_key ? "True" : "False");
|
||||||
PrintAndLogEx(NORMAL, "[+] Known sector: %d", blockNo);
|
PrintAndLogEx(SUCCESS, "Known sector: %d", blockNo);
|
||||||
PrintAndLogEx(NORMAL, "[+] Keytype: %c", keyType ? 'B' : 'A');
|
PrintAndLogEx(SUCCESS, "Keytype: %c", keyType ? 'B' : 'A');
|
||||||
PrintAndLogEx(NORMAL, "[+] Kown key: 0x%02x%02x%02x%02x%02x%02x", key[0], key[1], key[2], key[3], key[4], key[5]);
|
PrintAndLogEx(SUCCESS, "Kown key: 0x%02x%02x%02x%02x%02x%02x", key[0], key[1], key[2], key[3], key[4], key[5]);
|
||||||
PrintAndLogEx(NORMAL, "[+] Dictionary: %s", filename);
|
PrintAndLogEx(SUCCESS, "Dictionary: %s", filename);
|
||||||
|
PrintAndLogEx(SUCCESS, "Legacy mode (mfchk): %s", legacy_mfchk ? "True" : "False");
|
||||||
e_sector = calloc(sectorsCnt, sizeof(sector_t));
|
PrintAndLogEx(WARNING, "Starting attack!");
|
||||||
|
if (know_target_key == false)
|
||||||
if (know_target_key) {
|
|
||||||
// check if we can authenticate to sector
|
|
||||||
if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) {
|
|
||||||
PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to sector:%3d key type:%c", blockNo, keyType ? 'B' : 'A');
|
|
||||||
free(e_sector);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintAndLogEx(WARNING, "No known key was supplied, if no usable key is found in the dictionary, then this attack will fail!");
|
PrintAndLogEx(WARNING, "No known key was supplied, if no usable key is found in the dictionary, then this attack will fail!");
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the datastructures
|
// Create the key storage stucture
|
||||||
for (i=0; i<80; i++) {
|
e_sector = calloc(sectorsCnt, sizeof(sector_t));
|
||||||
arr[i] = 0;
|
if (e_sector == NULL) return PM3_EMALLOC;
|
||||||
}
|
// Clear the key storage datastructure
|
||||||
for (i=0; i<sectorsCnt; i++) {
|
for (i=0; i<sectorsCnt; i++) {
|
||||||
for (i2=0; i2<2; i2++) {
|
for (i2=0; i2<2; i2++) {
|
||||||
e_sector[i].Key[i2] = 0;
|
e_sector[i].Key[i2] = 0;
|
||||||
|
@ -1739,238 +1709,197 @@ static int CmdHF14AMfHardAuto(const char *Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the keyfiles
|
// Load the dictionary
|
||||||
if (strlen(filename) != 0) {
|
if (strlen(filename) != 0) {
|
||||||
f = fopen(filename, "r");
|
keyBlock = calloc(6 * 1000, sizeof(uint8_t));
|
||||||
if (!f) {
|
loadFileDICTIONARY(filename, keyBlock, &datalen, 6, &keycnt);
|
||||||
PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename);
|
} else {
|
||||||
free(e_sector);
|
keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6);
|
||||||
return 1;
|
if (keyBlock == NULL) return 1;
|
||||||
|
|
||||||
|
for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) {
|
||||||
|
num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6);
|
||||||
}
|
}
|
||||||
|
keycnt = ARRAYLEN(g_mifare_default_keys);
|
||||||
|
}
|
||||||
|
|
||||||
// read file
|
// Start the timer
|
||||||
while (fgets(buf, sizeof(buf), f)) {
|
|
||||||
if (strlen(buf) < 12 || buf[11] == '\n')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
while (fgetc(f) != '\n' && !feof(f)) ; //goto next line
|
|
||||||
|
|
||||||
if (buf[0] == '#') continue; //The line start with # is comment, skip
|
|
||||||
if (!isxdigit(buf[0])) {
|
|
||||||
PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[12] = 0;
|
|
||||||
if (keyitems - keycnt < 2) {
|
|
||||||
p = realloc(keyBlock, 6 * (keyitems += 64));
|
|
||||||
if (!p) {
|
|
||||||
PrintAndLogEx(FAILED, "Cannot allocate memory for default keys");
|
|
||||||
free(keyBlock);
|
|
||||||
fclose(f);
|
|
||||||
free(e_sector);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
keyBlock = p;
|
|
||||||
}
|
|
||||||
int pos = 6 * keycnt;
|
|
||||||
memset(keyBlock + pos, 0, 6);
|
|
||||||
num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + pos);
|
|
||||||
keycnt++;
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
t1 = msclock();
|
t1 = msclock();
|
||||||
|
|
||||||
// If no key is supplied by the user, brute force with the dictionary
|
PrintAndLogEx(SUCCESS, "Enumerating the card keys with the dictionary!");
|
||||||
if (know_target_key == false) {
|
if (legacy_mfchk) {
|
||||||
for (current_sector_i=0; current_sector_i < sectorsCnt; current_sector_i++) {
|
// Check all the sectors
|
||||||
for (current_key_type_i=0; current_key_type_i < 2; current_key_type_i++) {
|
for (i=0; i<sectorsCnt; i++) {
|
||||||
for (default_keys_i=0; default_keys_i < keycnt; default_keys_i++) {
|
for (i2=0; i2<2; i2++) {
|
||||||
// Iterate over the keys
|
// Check if the key is known
|
||||||
for (i=0; i<6; i++) {
|
if (e_sector[i].foundKey[i2] == 0) {
|
||||||
tmpKey[i] = keyBlock[i + (6*default_keys_i)];
|
for (i3=0; i3<keycnt; i3++) {
|
||||||
}
|
printf("."); fflush(stdout);
|
||||||
|
if (mfCheckKeys(i*4, i2, true, 1, (keyBlock + (6*i3)), &key64) == PM3_SUCCESS) {
|
||||||
if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) {
|
e_sector[i].Key[i2] = bytes_to_num((keyBlock + (6*i3)), 6);
|
||||||
PrintAndLogEx(SUCCESS, "Jackpot, we found a key! Now let the fun begin!");
|
e_sector[i].foundKey[i2] = 1;
|
||||||
PrintAndLogEx(SUCCESS, "[Dictio. KEYS] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
break;
|
||||||
current_sector_i,
|
|
||||||
current_key_type_i ? 'B' : 'A',
|
|
||||||
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
|
||||||
// Store the new key
|
|
||||||
for (i=0; i<6; i++) {
|
|
||||||
key[i] = tmpKey[i];
|
|
||||||
}
|
}
|
||||||
know_target_key = true;
|
|
||||||
blockNo = current_sector_i;
|
|
||||||
keyType = current_key_type_i;
|
|
||||||
foundKeysDictionary++;
|
|
||||||
|
|
||||||
// Exit the loop
|
|
||||||
current_sector_i = sectorsCnt;
|
|
||||||
current_key_type_i = 2;
|
|
||||||
default_keys_i = keycnt;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
printf("\n"); fflush(stdout);
|
||||||
|
} else {
|
||||||
|
chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt;
|
||||||
|
firstChunk = true; lastChunk = false;
|
||||||
|
for (uint8_t strategy = 1; strategy < 3; strategy++) {
|
||||||
|
PrintAndLogEx(SUCCESS, "Running strategy %u", strategy);
|
||||||
|
// main keychunk loop
|
||||||
|
for (i = 0; i < keycnt; i += chunksize) {
|
||||||
|
|
||||||
|
if (kbd_enter_pressed()) {
|
||||||
|
PrintAndLogEx(WARNING, "\naborted via keyboard!\n");
|
||||||
|
i = keycnt; strategy = 3; break; // Exit the loop
|
||||||
|
}
|
||||||
|
uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i;
|
||||||
|
// last chunk?
|
||||||
|
if (size == keycnt - i)
|
||||||
|
lastChunk = true;
|
||||||
|
int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false);
|
||||||
|
if (firstChunk)
|
||||||
|
firstChunk = false;
|
||||||
|
// all keys, aborted
|
||||||
|
if (res == 0 || res == 2) {
|
||||||
|
i = keycnt; strategy = 3; break; // Exit the loop
|
||||||
|
}
|
||||||
|
} // end chunks of keys
|
||||||
|
firstChunk = true;
|
||||||
|
lastChunk = false;
|
||||||
|
} // end strategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the user defined / bruteforced key
|
// Check if at least one key was recovered
|
||||||
|
for (i=0; i<sectorsCnt; i++) {
|
||||||
|
for (i2=0; i2<2; i2++) {
|
||||||
|
if (e_sector[i].foundKey[i2] == 1) {
|
||||||
|
num_to_bytes(e_sector[i].Key[i2], 6, tmpKey);
|
||||||
|
// Store vaild credentials for the nested attack
|
||||||
|
if (know_target_key == false) {
|
||||||
|
num_to_bytes(e_sector[i].Key[i2], 6, key);
|
||||||
|
know_target_key = true;
|
||||||
|
blockNo = i; keyType = i2;
|
||||||
|
}
|
||||||
|
PrintAndLogEx(SUCCESS, "[ DICTI. KEY ] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
||||||
|
i,
|
||||||
|
i2 ? 'B' : 'A',
|
||||||
|
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
||||||
|
foundKeysDictionary++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if at least one sector key was found
|
||||||
if (know_target_key) {
|
if (know_target_key) {
|
||||||
e_sector[blockNo].Key[keyType] = bytes_to_num(key, 6);
|
// Check if we can authenticate to sector
|
||||||
arr[blockNo + (keyType * sectorsCnt)] = 1;
|
if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to sector:%3d key type:%c key: " _RED_("0x%02x%02x%02x%02x%02x%02x"),
|
||||||
|
blockNo,
|
||||||
|
keyType ? 'B' : 'A',
|
||||||
|
key[0], key[1], key[2], key[3], key[4], key[5]);
|
||||||
|
free(e_sector);
|
||||||
|
free(keyBlock);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(FAILED, "No usable key was found!");
|
PrintAndLogEx(FAILED, "No usable key was found!");
|
||||||
free(e_sector);
|
free(e_sector);
|
||||||
|
free(keyBlock);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
// Clear the tmpKey
|
||||||
|
num_to_bytes(0, 6, tmpKey);
|
||||||
|
|
||||||
// Iterate over each sector and key(A/B)
|
// Iterate over each sector and key(A/B)
|
||||||
for (current_sector_i=0; current_sector_i < sectorsCnt; current_sector_i++) {
|
for (current_sector_i=0; current_sector_i < sectorsCnt; current_sector_i++) {
|
||||||
for (current_key_type_i=0; current_key_type_i < 2; current_key_type_i++) {
|
for (current_key_type_i=0; current_key_type_i < 2; current_key_type_i++) {
|
||||||
|
|
||||||
foundkey = e_sector[current_sector_i].Key[current_key_type_i];
|
// If the key is already known, just skip it
|
||||||
|
if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) {
|
||||||
|
|
||||||
// Try the found keys
|
// Try the found keys
|
||||||
if (foundkey == 0) {
|
if (bytes_to_num(tmpKey, 6) != 0) {
|
||||||
for (found_keys_i=0; found_keys_i < sectorsCnt; found_keys_i++) {
|
// Check if the sector is already broken
|
||||||
// Iterate over the keys
|
// Check if the key works
|
||||||
if (arr[found_keys_i + (current_key_type_i * sectorsCnt)] == 1) {
|
for (i=0; i<sectorsCnt; i++) {
|
||||||
num_to_bytes(e_sector[found_keys_i].Key[current_key_type_i], 6, tmpKey);
|
for (i2=0; i2<2; i2++) {
|
||||||
if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) {
|
if (e_sector[i].foundKey[i2] == 0) {
|
||||||
PrintAndLogEx(SUCCESS, "[REUSED KEYS] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
if (mfCheckKeys(i*4, i2, true, 1, tmpKey, &key64) == PM3_SUCCESS) {
|
||||||
current_sector_i,
|
e_sector[i].Key[i2] = bytes_to_num(tmpKey, 6);
|
||||||
current_key_type_i ? 'B' : 'A',
|
e_sector[i].foundKey[i2] = 1;
|
||||||
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
PrintAndLogEx(SUCCESS, "[ REUSED KEY ] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
||||||
foundkey = bytes_to_num(tmpKey, 6);
|
i,
|
||||||
foundKeysReuse++;
|
i2 ? 'B' : 'A',
|
||||||
break;
|
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
||||||
}
|
foundKeysReused++;
|
||||||
}
|
}
|
||||||
if (arr[found_keys_i + (((current_key_type_i+1)%2) * sectorsCnt)] == 1) {
|
}
|
||||||
num_to_bytes(e_sector[found_keys_i].Key[(current_key_type_i+1)%2], 6, tmpKey);
|
|
||||||
if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) {
|
|
||||||
PrintAndLogEx(SUCCESS, "[REUSED KEYS] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
|
||||||
current_sector_i,
|
|
||||||
current_key_type_i ? 'B' : 'A',
|
|
||||||
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
|
||||||
foundkey = bytes_to_num(tmpKey, 6);
|
|
||||||
foundKeysReuse++;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Clear the last found key
|
||||||
// Try the default keys
|
num_to_bytes(0, 6, tmpKey);
|
||||||
if (foundkey == 0) {
|
|
||||||
for (default_keys_i=0; default_keys_i < keycnt; default_keys_i++) {
|
// Use the hardnested attack
|
||||||
// Iterate over the keys
|
if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) {
|
||||||
for (i=0; i<6; i++) {
|
PrintAndLogEx(SUCCESS, "[ HARD. ATT. ] sector no:%3d, target key type:%c, Slow: %s",
|
||||||
tmpKey[i] = keyBlock[i + (6*default_keys_i)];
|
current_sector_i,
|
||||||
|
current_key_type_i ? 'B' : 'A',
|
||||||
|
slow ? "Yes" : "No");
|
||||||
|
|
||||||
|
isOK = mfnestedhard(blockNo, keyType, key, current_sector_i*4, current_key_type_i, NULL, nonce_file_read, nonce_file_write, slow, 0, &foundkey, NULL);
|
||||||
|
|
||||||
|
DropField();
|
||||||
|
if (isOK) {
|
||||||
|
switch (isOK) {
|
||||||
|
case 1 :
|
||||||
|
PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n");
|
||||||
|
break;
|
||||||
|
case 2 :
|
||||||
|
PrintAndLogEx(NORMAL, "Button pressed. Aborted.\n");
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(e_sector);
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) {
|
// Copy the found key to the tmpKey variale (for the following print statement, and the mfCheckKeys above)
|
||||||
PrintAndLogEx(SUCCESS, "[Dictio. KEYS] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
num_to_bytes(foundkey, 6, tmpKey);
|
||||||
current_sector_i,
|
e_sector[current_sector_i].Key[current_key_type_i] = foundkey;
|
||||||
current_key_type_i ? 'B' : 'A',
|
e_sector[current_sector_i].foundKey[current_key_type_i] = 1;
|
||||||
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
PrintAndLogEx(SUCCESS, "[HARDNE. KEYS] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
||||||
foundkey = bytes_to_num(tmpKey, 6);
|
current_sector_i,
|
||||||
foundKeysDictionary++;
|
current_key_type_i ? 'B' : 'A',
|
||||||
break;
|
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
||||||
}
|
foundKeysHardnested++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Bruteforce with hardnested
|
|
||||||
if (foundkey == 0) {
|
|
||||||
PrintAndLogEx(SUCCESS, "[ BRUTEFORCE ] sector no:%3d, target key type:%c, Slow: %s, Tests: %d ",
|
|
||||||
current_sector_i,
|
|
||||||
current_key_type_i ? 'B' : 'A',
|
|
||||||
slow ? "Yes" : "No",
|
|
||||||
tests);
|
|
||||||
|
|
||||||
isOK = mfnestedhard(blockNo, keyType, key, current_sector_i*4, current_key_type_i, NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, NULL);
|
|
||||||
|
|
||||||
DropField();
|
|
||||||
if (isOK) {
|
|
||||||
switch (isOK) {
|
|
||||||
case 1 :
|
|
||||||
PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n");
|
|
||||||
break;
|
|
||||||
case 2 :
|
|
||||||
PrintAndLogEx(NORMAL, "Button pressed. Aborted.\n");
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
free(e_sector);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
num_to_bytes(foundkey, 6, tmpKey);
|
|
||||||
PrintAndLogEx(SUCCESS, "[CRACKED KEY] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"),
|
|
||||||
current_sector_i,
|
|
||||||
current_key_type_i ? 'B' : 'A',
|
|
||||||
tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]);
|
|
||||||
foundKeysHardnested++;
|
|
||||||
}
|
|
||||||
// Add the key
|
|
||||||
if (foundkey != 0) {
|
|
||||||
e_sector[current_sector_i].Key[current_key_type_i] = foundkey;
|
|
||||||
arr[current_sector_i + (current_key_type_i * sectorsCnt)] = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link the found keys
|
|
||||||
for (i=0; i<sectorsCnt; i++) {
|
|
||||||
for (i2=0; i2<2; i2++) {
|
|
||||||
e_sector[i].foundKey[i2] = arr[i + (i2 * sectorsCnt)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump all the data
|
|
||||||
printKeyTable(sectorsCnt, e_sector);
|
printKeyTable(sectorsCnt, e_sector);
|
||||||
|
// Create a dumpfile
|
||||||
if (createDumpFile) {
|
if (createDumpFile) {
|
||||||
fptr = GenerateFilename("hf-mf-", "-key.bin");
|
PrintAndLogEx(SUCCESS, "\nDumping the found keys:");
|
||||||
if (fptr == NULL) {
|
createKeyDump(sectorsCnt, e_sector, GenerateFilename("hf-mf-", "-key.bin"));
|
||||||
free(e_sector);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *fkeys = fopen(fptr, "wb");
|
|
||||||
if (fkeys == NULL) {
|
|
||||||
PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), fptr);
|
|
||||||
free(e_sector);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
PrintAndLogEx(SUCCESS, "Printing keys to binary file " _YELLOW_("%s")"...", fptr);
|
|
||||||
|
|
||||||
for (i = 0; i < sectorsCnt; i++) {
|
|
||||||
num_to_bytes(e_sector[i].Key[0], 6, tmpKey);
|
|
||||||
fwrite(tmpKey, 1, 6, fkeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < sectorsCnt; i++) {
|
|
||||||
num_to_bytes(e_sector[i].Key[1], 6, tmpKey);
|
|
||||||
fwrite(tmpKey, 1, 6, fkeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fkeys);
|
|
||||||
PrintAndLogEx(SUCCESS, "Found keys have been dumped to " _YELLOW_("%s")" --> 0xffffffffffff has been inserted for unknown keys.", fptr);
|
|
||||||
}
|
}
|
||||||
|
// Generate and show statistics
|
||||||
t1 = msclock() - t1;
|
t1 = msclock() - t1;
|
||||||
PrintAndLogEx(SUCCESS, "Key statistics: Dictionary: " _GREEN_("%d") ", Reuse: " _YELLOW_("%d") ", Bruteforce: " _MAGENTA_("%d") ", Total: " _YELLOW_("%d"), foundKeysDictionary, foundKeysReuse, foundKeysHardnested, sectorsCnt*2);
|
PrintAndLogEx(SUCCESS, "Keys statistics (total: %d):"
|
||||||
|
"\n\t(*) Broken with hardnested attack: " _MAGENTA_("%d")
|
||||||
|
"\n\t(*) Reused keys: " _YELLOW_("%d")
|
||||||
|
"\n\t(*) Discovered with the dictionary: " _YELLOW_("%d")
|
||||||
|
, sectorsCnt*2, foundKeysHardnested, foundKeysReused, foundKeysDictionary);
|
||||||
PrintAndLogEx(SUCCESS, "Required time for the hardautopwn attack: " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
|
PrintAndLogEx(SUCCESS, "Required time for the hardautopwn attack: " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
|
||||||
|
|
||||||
free(e_sector);
|
free(e_sector);
|
||||||
|
free(keyBlock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2007,7 +1936,6 @@ static int CmdHF14AMfChk_fast(const char *Cmd) {
|
||||||
char filename[FILE_PATH_SIZE] = {0};
|
char filename[FILE_PATH_SIZE] = {0};
|
||||||
char buf[13];
|
char buf[13];
|
||||||
char *fptr;
|
char *fptr;
|
||||||
uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
uint8_t *keyBlock, *p;
|
uint8_t *keyBlock, *p;
|
||||||
uint8_t sectorsCnt = 1;
|
uint8_t sectorsCnt = 1;
|
||||||
int i, keycnt = 0;
|
int i, keycnt = 0;
|
||||||
|
@ -2218,30 +2146,7 @@ out:
|
||||||
|
|
||||||
if (createDumpFile) {
|
if (createDumpFile) {
|
||||||
fptr = GenerateFilename("hf-mf-", "-key.bin");
|
fptr = GenerateFilename("hf-mf-", "-key.bin");
|
||||||
if (fptr == NULL)
|
createKeyDump(sectorsCnt, e_sector, fptr);
|
||||||
return 1;
|
|
||||||
|
|
||||||
FILE *fkeys = fopen(fptr, "wb");
|
|
||||||
if (fkeys == NULL) {
|
|
||||||
PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), fptr);
|
|
||||||
free(keyBlock);
|
|
||||||
free(e_sector);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
PrintAndLogEx(SUCCESS, "Printing keys to binary file " _YELLOW_("%s")"...", fptr);
|
|
||||||
|
|
||||||
for (i = 0; i < sectorsCnt; i++) {
|
|
||||||
num_to_bytes(e_sector[i].Key[0], 6, tempkey);
|
|
||||||
fwrite(tempkey, 1, 6, fkeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < sectorsCnt; i++) {
|
|
||||||
num_to_bytes(e_sector[i].Key[1], 6, tempkey);
|
|
||||||
fwrite(tempkey, 1, 6, fkeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fkeys);
|
|
||||||
PrintAndLogEx(SUCCESS, "Found keys have been dumped to " _YELLOW_("%s")" --> 0xffffffffffff has been inserted for unknown keys.", fptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,36 @@ out:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int createKeyDump(uint8_t sectorsCnt, sector_t *e_sector, char* fptr) {
|
||||||
|
uint8_t tmpKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (fptr == NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fkeys = fopen(fptr, "wb");
|
||||||
|
if (fkeys == NULL) {
|
||||||
|
PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), fptr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
PrintAndLogEx(SUCCESS, "Printing keys to binary file " _YELLOW_("%s")"...", fptr);
|
||||||
|
|
||||||
|
for (i = 0; i < sectorsCnt; i++) {
|
||||||
|
num_to_bytes(e_sector[i].Key[0], 6, tmpKey);
|
||||||
|
fwrite(tmpKey, 1, 6, fkeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sectorsCnt; i++) {
|
||||||
|
num_to_bytes(e_sector[i].Key[1], 6, tmpKey);
|
||||||
|
fwrite(tmpKey, 1, 6, fkeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fkeys);
|
||||||
|
PrintAndLogEx(SUCCESS, "Found keys have been dumped to " _YELLOW_("%s")" --> 0xffffffffffff has been inserted for unknown keys.", fptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int loadFile(const char *preferredName, const char *suffix, void *data, size_t maxdatalen, size_t *datalen) {
|
int loadFile(const char *preferredName, const char *suffix, void *data, size_t maxdatalen, size_t *datalen) {
|
||||||
|
|
||||||
if (data == NULL) return 1;
|
if (data == NULL) return 1;
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "../ui.h"
|
#include "../ui.h"
|
||||||
#include "../emv/emvjson.h"
|
#include "../emv/emvjson.h"
|
||||||
#include "mifare/mifare4.h"
|
#include "mifare/mifare4.h"
|
||||||
|
#include "mifare/mifarehost.h"
|
||||||
#include "cmdhfmfu.h"
|
#include "cmdhfmfu.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -103,6 +104,16 @@ int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t
|
||||||
*/
|
*/
|
||||||
int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen);
|
int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Utility function to save a keydump.
|
||||||
|
*
|
||||||
|
* @param sectorsCnt the used sectors
|
||||||
|
* @param e_sector the keys in question
|
||||||
|
* @param fptr string pointer to the filename
|
||||||
|
* @return 0 for ok, 1 for failz
|
||||||
|
*/
|
||||||
|
int createKeyDump(uint8_t sectorsCnt, sector_t *e_sector, char* fptr);
|
||||||
|
|
||||||
/** STUB
|
/** STUB
|
||||||
* @brief Utility function to load data from a binary file. This method takes a preferred name.
|
* @brief Utility function to load data from a binary file. This method takes a preferred name.
|
||||||
* E.g. dumpdata-15.bin
|
* E.g. dumpdata-15.bin
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue