From 407078d90a9d6ec7b040adbccd662d14db65d2c6 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Thu, 22 Aug 2019 12:49:26 +0200 Subject: [PATCH 01/10] Experimental hardautopwn implementation. --- client/cmdhfmf.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index aaaeafb99..6c5424568 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -166,6 +166,34 @@ static int usage_hf14_hardnested(void) { PrintAndLogEx(NORMAL, " hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF"); return 0; } +static int usage_hf14_hardautopwn(void) { + PrintAndLogEx(NORMAL, "Usage:"); + PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] "); + PrintAndLogEx(NORMAL, " [d] [f] [s] [t] [i]"); + PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); + PrintAndLogEx(NORMAL, " d write keys to binary file"); + PrintAndLogEx(NORMAL, " f keys to test (speed up the cracking, if some keys are known)"); + PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)"); + PrintAndLogEx(NORMAL, " t tests?"); + PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); + PrintAndLogEx(NORMAL, " i 5 = AVX512"); + PrintAndLogEx(NORMAL, " i 2 = AVX2"); + PrintAndLogEx(NORMAL, " i a = AVX"); + PrintAndLogEx(NORMAL, " i s = SSE2"); + PrintAndLogEx(NORMAL, " i m = MMX"); + PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf hardautopwn b 0 A FFFFFFFFFFFF 1 d"); + PrintAndLogEx(NORMAL, " hf mf hardautopwn 0 A FFFFFFFFFFFF 1 d f default_keys.dic"); + PrintAndLogEx(NORMAL, " hf mf hardautopwn 0 A FFFFFFFFFFFF 4 A f nonces.bin w s"); + PrintAndLogEx(NORMAL, ""); + return 0; +} static int usage_hf14_chk(void) { PrintAndLogEx(NORMAL, "Usage: hf mf chk [h] |<*card memory> [t|d] [] []"); PrintAndLogEx(NORMAL, "Options:"); @@ -1530,6 +1558,402 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { return 0; } + +static int CmdHF14AMfHardAuto(const char *Cmd) { + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t *keyBlock, *p; + uint8_t sectorsCnt = 1; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t trgkey[6] = {0, 0, 0, 0, 0, 0}; + uint8_t cmdp = 0; + uint64_t key64 = 0; + char filename[FILE_PATH_SIZE] = {0}, *fptr; + char ctmp; + + keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); + 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); + + bool slow = false; + bool nonce_file_read = false; + bool nonce_file_write = false; + bool createDumpFile = false; + bool know_target_key = false; + int tests = 0; + + ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_hardautopwn(); + + + while ((ctmp = param_getchar(Cmd, cmdp))) { + switch (tolower(ctmp)) { + case 'h': + return usage_hf14_hardautopwn(); + case 'f': + if (param_getstr(Cmd, cmdp +1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + } + cmdp ++; + break; + case 'd': + createDumpFile = true; + break; + case '*': + // sectors + switch (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 ++; + break; + case 'k': + // Get the known block number + if (param_getchar(Cmd, cmdp + 1) == 0x00) { + PrintAndLogEx(WARNING, "Block number is missing"); + return 1; + } + blockNo = param_get8(Cmd, cmdp + 1); + // Get the knonwn block type + ctmp = tolower(param_getchar(Cmd, cmdp + 2)); + if (ctmp != 'a' && ctmp != 'b') { + PrintAndLogEx(WARNING, "Key type must be A or B"); + return 1; + } + if (ctmp != 'a') { + keyType = 1; + } + // Get the known block key + if (param_gethex(Cmd, cmdp + 3, key, 12)) { + PrintAndLogEx(WARNING, "Key must include 12 HEX symbols"); + return 1; + } + know_target_key = true; + cmdp += 3; + case 's': + slow = true; + break; + case 'i': + SetSIMDInstr(SIMD_AUTO); + ctmp = tolower(param_getchar(Cmd, cmdp + 1)); + switch (ctmp) { + case '5': + SetSIMDInstr(SIMD_AVX512); + break; + case '2': + SetSIMDInstr(SIMD_AVX2); + break; + case 'a': + SetSIMDInstr(SIMD_AVX); + break; + case 's': + SetSIMDInstr(SIMD_SSE2); + break; + case 'm': + SetSIMDInstr(SIMD_MMX); + break; + case 'n': + SetSIMDInstr(SIMD_NONE); + break; + default: + PrintAndLogEx(WARNING, "Unknown SIMD type. %c", ctmp); + return 1; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", ctmp); + usage_hf14_hardnested(); + return 1; + } + cmdp++; + } + + // Print parameters + PrintAndLogEx(NORMAL, "Used Parameters:"); + PrintAndLogEx(NORMAL, "\t[+] Dumping the found keys: %d", createDumpFile); + PrintAndLogEx(NORMAL, "\t[+] Card sectors: %d", sectorsCnt); + PrintAndLogEx(NORMAL, "\t[+] Key supplied: %d", know_target_key); + PrintAndLogEx(NORMAL, "\t[+] Known block: %d", blockNo); + PrintAndLogEx(NORMAL, "\t[+] Keytype: %c", keyType ? 'B' : 'A'); + PrintAndLogEx(NORMAL, "\t[+] Kown key: 0x%02x%02x%02x%02x%02x%02x", key[0], key[1], key[2], key[3], key[4], key[5]); + PrintAndLogEx(NORMAL, "\t[+] Dictionary: %s", filename); + + + 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 block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + return 3; + } + } else { + PrintAndLogEx(WARNING, "No known key was supplied, if no usable key is found in the dictionary, then this attack will fail!"); + } + + + // General stuff + // Add check for the hardnested attack!! + uint64_t foundkey = 0; + int16_t isOK = 0; + + // Bruteforce stuff + FILE* f; + sector_t *e_sector = calloc(sectorsCnt, sizeof(sector_t)); + uint8_t arr[80]; + uint8_t tmpKey[6]; + char buf[13] = {0}; + int i, i2, keycnt = 0;; + int current_sector_i, current_key_type_i, default_keys_i, found_keys_i; + uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); + + + // Clear the datastructures + for (i=0; i<80; i++) { + arr[i] = 0; + } + for (i=0; i 0xffffffffffff has been inserted for unknown keys.", fptr); + } + + free(e_sector); + + 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; + } + return 2; + } + + + return 0; +} + /* static int randInRange(int min, int max) { return min + (int)(rand() / (double)(RAND_MAX) * (max - min + 1)); @@ -3638,6 +4062,7 @@ static command_t CommandTable[] = { {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack. read parity error messages."}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack. Test nested authentication"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened Mifare cards"}, + {"hardautopwn", CmdHF14AMfHardAuto, AlwaysAvailable, "Nested attack for hardened Mifare cards that breaks all sector keys autmatically"}, {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, {"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for Mifare NACK bug"}, {"chk", CmdHF14AMfChk, IfPm3Iso14443a, "Check keys"}, From abb69e2dc9fe7a7e114955b6543e35f396718735 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Thu, 22 Aug 2019 14:55:06 +0200 Subject: [PATCH 02/10] Further improved the hardautopwn feature. --- client/cmdhfmf.c | 162 ++++++++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 71 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 6c5424568..c68daa9f3 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -168,17 +168,16 @@ static int usage_hf14_hardnested(void) { } static int usage_hf14_hardautopwn(void) { PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] "); - PrintAndLogEx(NORMAL, " [d] [f] [s] [t] [i]"); + PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] "); + PrintAndLogEx(NORMAL, " * [d] [f] [s] [t] [i]"); PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); + PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); PrintAndLogEx(NORMAL, " d write keys to binary file"); PrintAndLogEx(NORMAL, " f keys to test (speed up the cracking, if some keys are known)"); PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)"); - PrintAndLogEx(NORMAL, " t tests?"); PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); PrintAndLogEx(NORMAL, " i 5 = AVX512"); PrintAndLogEx(NORMAL, " i 2 = AVX2"); @@ -188,9 +187,9 @@ static int usage_hf14_hardautopwn(void) { PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf hardautopwn b 0 A FFFFFFFFFFFF 1 d"); - PrintAndLogEx(NORMAL, " hf mf hardautopwn 0 A FFFFFFFFFFFF 1 d f default_keys.dic"); - PrintAndLogEx(NORMAL, " hf mf hardautopwn 0 A FFFFFFFFFFFF 4 A f nonces.bin w s"); + 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 * 4 s i 5"); PrintAndLogEx(NORMAL, ""); return 0; } @@ -1560,22 +1559,45 @@ static int CmdHF14AMfNestedHard(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 keyType = 0; uint8_t *keyBlock, *p; - uint8_t sectorsCnt = 1; + uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; + sector_t *e_sector; + uint8_t arr[80]; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t trgkey[6] = {0, 0, 0, 0, 0, 0}; - uint8_t cmdp = 0; + uint8_t tmpKey[6]; uint64_t key64 = 0; - char filename[FILE_PATH_SIZE] = {0}, *fptr; - char ctmp; + + uint64_t t1; + uint8_t foundKeysDictionary = 0; + uint8_t foundKeysReuse = 0; + uint8_t foundKeysHardnested = 0; keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); 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; + char ctmp; + + uint64_t foundkey = 0; + int16_t isOK = 0; + + int i, i2, keycnt = 0;; + int current_sector_i, current_key_type_i, default_keys_i, found_keys_i; + uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); bool slow = false; bool nonce_file_read = false; @@ -1624,7 +1646,7 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { case 'k': // Get the known block number if (param_getchar(Cmd, cmdp + 1) == 0x00) { - PrintAndLogEx(WARNING, "Block number is missing"); + PrintAndLogEx(WARNING, "Sector number is missing"); return 1; } blockNo = param_get8(Cmd, cmdp + 1); @@ -1685,42 +1707,27 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { // Print parameters PrintAndLogEx(NORMAL, "Used Parameters:"); - PrintAndLogEx(NORMAL, "\t[+] Dumping the found keys: %d", createDumpFile); - PrintAndLogEx(NORMAL, "\t[+] Card sectors: %d", sectorsCnt); - PrintAndLogEx(NORMAL, "\t[+] Key supplied: %d", know_target_key); - PrintAndLogEx(NORMAL, "\t[+] Known block: %d", blockNo); - PrintAndLogEx(NORMAL, "\t[+] Keytype: %c", keyType ? 'B' : 'A'); - PrintAndLogEx(NORMAL, "\t[+] Kown key: 0x%02x%02x%02x%02x%02x%02x", key[0], key[1], key[2], key[3], key[4], key[5]); - PrintAndLogEx(NORMAL, "\t[+] Dictionary: %s", filename); + PrintAndLogEx(NORMAL, "[+] Dumping the found keys: %s", createDumpFile ? "True" : "False"); + PrintAndLogEx(NORMAL, "[+] Card sectors: %d", sectorsCnt); + PrintAndLogEx(NORMAL, "[+] Key supplied: %s", know_target_key ? "True" : "False"); + PrintAndLogEx(NORMAL, "[+] Known sector: %d", blockNo); + PrintAndLogEx(NORMAL, "[+] 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(NORMAL, "[+] Dictionary: %s", filename); + e_sector = calloc(sectorsCnt, sizeof(sector_t)); 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 block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + 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!"); } - - // General stuff - // Add check for the hardnested attack!! - uint64_t foundkey = 0; - int16_t isOK = 0; - - // Bruteforce stuff - FILE* f; - sector_t *e_sector = calloc(sectorsCnt, sizeof(sector_t)); - uint8_t arr[80]; - uint8_t tmpKey[6]; - char buf[13] = {0}; - int i, i2, keycnt = 0;; - int current_sector_i, current_key_type_i, default_keys_i, found_keys_i; - uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); - - // Clear the datastructures for (i=0; i<80; i++) { arr[i] = 0; @@ -1737,6 +1744,7 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { f = fopen(filename, "r"); if (!f) { PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + free(e_sector); return 1; } @@ -1760,6 +1768,7 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { PrintAndLogEx(FAILED, "Cannot allocate memory for default keys"); free(keyBlock); fclose(f); + free(e_sector); return 2; } keyBlock = p; @@ -1774,8 +1783,9 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); } + t1 = msclock(); - // If no key is supplied by the user brute force with the dictionary + // If no key is supplied by the user, brute force with the dictionary if (know_target_key == false) { 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++) { @@ -1786,7 +1796,8 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { } if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "[ KEY ENUM ] Valid KEY FOUND: block:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), + PrintAndLogEx(SUCCESS, "Jackpot, we found a key! Now let the fun begin!"); + PrintAndLogEx(SUCCESS, "[Dictio. 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]); @@ -1797,6 +1808,7 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { know_target_key = true; blockNo = current_sector_i; keyType = current_key_type_i; + foundKeysDictionary++; // Exit the loop current_sector_i = sectorsCnt; @@ -1809,17 +1821,17 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { } } - // Set the user defined key + // Set the user defined / bruteforced key if (know_target_key) { e_sector[blockNo].Key[keyType] = bytes_to_num(key, 6); arr[blockNo + (keyType * sectorsCnt)] = 1; } else { PrintAndLogEx(FAILED, "No usable key was found!"); - return 1; + free(e_sector); + return 1; } - - // Iterate over each sector and key + // Iterate over each sector and key(A/B) 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++) { @@ -1827,29 +1839,29 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { // Try the found keys if (foundkey == 0) { - for (found_keys_i=0; found_keys_i < current_sector_i; found_keys_i++) { + for (found_keys_i=0; found_keys_i < sectorsCnt; found_keys_i++) { // Iterate over the keys if (arr[found_keys_i + (current_key_type_i * sectorsCnt)] == 1) { num_to_bytes(e_sector[found_keys_i].Key[current_key_type_i], 6, tmpKey); if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "[FOUND KEYS %c] Valid KEY FOUND: block:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), - current_key_type_i ? 'B' : 'A', + 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; } } 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, "[FOUND KEYS %c] Valid KEY FOUND: block:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), - (current_key_type_i+1)%2 ? 'B' : 'A', + 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; } } @@ -1864,29 +1876,48 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { } if (mfCheckKeys(current_sector_i*4, current_key_type_i, true, 1, tmpKey, &key64) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "[DEFAULT KEYS] Valid KEY FOUND: block:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), + PrintAndLogEx(SUCCESS, "[Dictio. 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); + foundKeysDictionary++; break; } } } // Bruteforce with hardnested if (foundkey == 0) { - PrintAndLogEx(SUCCESS, "[ BRUTEFORCE ] block no:%3d, target key type:%c, Slow: %s, Tests: %d ", + 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, know_target_key ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); + 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: block:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), + 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) { @@ -1908,8 +1939,10 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { if (createDumpFile) { fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) + if (fptr == NULL) { + free(e_sector); return 1; + } FILE *fkeys = fopen(fptr, "wb"); if (fkeys == NULL) { @@ -1933,24 +1966,11 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { PrintAndLogEx(SUCCESS, "Found keys have been dumped to " _YELLOW_("%s")" --> 0xffffffffffff has been inserted for unknown keys.", fptr); } + 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, "Required time for the hardautopwn attack: " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + free(e_sector); - - 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; - } - return 2; - } - - return 0; } From 87cbf3aa70cc974810467435c2a7ef6988c960df Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Fri, 23 Aug 2019 17:17:17 +0200 Subject: [PATCH 03/10] Improved the reliability, speed and redability. Ready for a pull mr. iceman ;) --- client/cmdhfmf.c | 479 ++++++++++++++++++--------------------------- client/fileutils.c | 30 +++ client/fileutils.h | 11 ++ 3 files changed, 233 insertions(+), 287 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index c68daa9f3..e968548b6 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -169,15 +169,16 @@ static int usage_hf14_hardnested(void) { static int usage_hf14_hardautopwn(void) { PrintAndLogEx(NORMAL, "Usage:"); PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] "); - PrintAndLogEx(NORMAL, " * [d] [f] [s] [t] [i]"); + PrintAndLogEx(NORMAL, " * [d] [f] [s] [i] [l]"); PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); PrintAndLogEx(NORMAL, " d write keys to binary file"); - PrintAndLogEx(NORMAL, " f keys to test (speed up the cracking, if some keys are known)"); + PrintAndLogEx(NORMAL, " f .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, " l legacy mode (use the slow mfchk for the key enumeration)"); PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); PrintAndLogEx(NORMAL, " i 5 = AVX512"); 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, ""); 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 * 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, ""); return 0; } @@ -1559,57 +1563,44 @@ static int CmdHF14AMfNestedHard(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 keyType = 0; - uint8_t *keyBlock, *p; + uint8_t *keyBlock; uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; sector_t *e_sector; - uint8_t arr[80]; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t tmpKey[6]; + uint8_t key[6] = {0}; + uint8_t tmpKey[6] = {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 foundKeysReuse = 0; + uint8_t foundKeysReused = 0; uint8_t foundKeysHardnested = 0; - keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); - 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; + char filename[FILE_PATH_SIZE] = {0}; uint8_t cmdp = 0; char ctmp; uint64_t foundkey = 0; int16_t isOK = 0; - int i, i2, keycnt = 0;; - int current_sector_i, current_key_type_i, default_keys_i, found_keys_i; - uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); + int i, i2, i3; // Loop counter + int current_sector_i = 0, current_key_type_i = 0; bool slow = false; bool nonce_file_read = false; bool nonce_file_write = false; bool createDumpFile = false; bool know_target_key = false; - int tests = 0; + bool legacy_mfchk = false; + bool firstChunk = 0, lastChunk = 0; ctmp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_hardautopwn(); - while ((ctmp = param_getchar(Cmd, cmdp))) { switch (tolower(ctmp)) { case 'h': @@ -1623,24 +1614,12 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { case 'd': createDumpFile = true; break; + case 'l': + legacy_mfchk = true; + break; case '*': - // sectors - switch (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; - } + // Get the number of sectors + sectorsCnt = NumOfSectors(param_getchar(Cmd, cmdp + 1)); cmdp ++; break; case 'k': @@ -1707,31 +1686,22 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { // Print parameters PrintAndLogEx(NORMAL, "Used Parameters:"); - PrintAndLogEx(NORMAL, "[+] Dumping the found keys: %s", createDumpFile ? "True" : "False"); - PrintAndLogEx(NORMAL, "[+] Card sectors: %d", sectorsCnt); - PrintAndLogEx(NORMAL, "[+] Key supplied: %s", know_target_key ? "True" : "False"); - PrintAndLogEx(NORMAL, "[+] Known sector: %d", blockNo); - PrintAndLogEx(NORMAL, "[+] 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(NORMAL, "[+] Dictionary: %s", filename); - - e_sector = calloc(sectorsCnt, sizeof(sector_t)); - - 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(SUCCESS, "Dumping the found keys: %s", createDumpFile ? "True" : "False"); + PrintAndLogEx(SUCCESS, "Card sectors: %d", sectorsCnt); + PrintAndLogEx(SUCCESS, "Key supplied: %s", know_target_key ? "True" : "False"); + PrintAndLogEx(SUCCESS, "Known sector: %d", blockNo); + PrintAndLogEx(SUCCESS, "Keytype: %c", keyType ? 'B' : 'A'); + PrintAndLogEx(SUCCESS, "Kown key: 0x%02x%02x%02x%02x%02x%02x", key[0], key[1], key[2], key[3], key[4], key[5]); + PrintAndLogEx(SUCCESS, "Dictionary: %s", filename); + PrintAndLogEx(SUCCESS, "Legacy mode (mfchk): %s", legacy_mfchk ? "True" : "False"); + PrintAndLogEx(WARNING, "Starting attack!"); + if (know_target_key == false) PrintAndLogEx(WARNING, "No known key was supplied, if no usable key is found in the dictionary, then this attack will fail!"); - } - - // Clear the datastructures - for (i=0; i<80; i++) { - arr[i] = 0; - } + + // Create the key storage stucture + e_sector = calloc(sectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) return PM3_EMALLOC; + // Clear the key storage datastructure for (i=0; i (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 0xffffffffffff has been inserted for unknown keys.", fptr); + PrintAndLogEx(SUCCESS, "\nDumping the found keys:"); + createKeyDump(sectorsCnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); } - + // Generate and show statistics 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); free(e_sector); + free(keyBlock); return 0; } @@ -2007,7 +1936,6 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; char buf[13]; char *fptr; - uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t *keyBlock, *p; uint8_t sectorsCnt = 1; int i, keycnt = 0; @@ -2218,30 +2146,7 @@ out: if (createDumpFile) { fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) - 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); + createKeyDump(sectorsCnt, e_sector, fptr); } } diff --git a/client/fileutils.c b/client/fileutils.c index b89925334..124a51edc 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -293,6 +293,36 @@ out: 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) { if (data == NULL) return 1; diff --git a/client/fileutils.h b/client/fileutils.h index 74d54000b..61b7b0468 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -49,6 +49,7 @@ #include "../ui.h" #include "../emv/emvjson.h" #include "mifare/mifare4.h" +#include "mifare/mifarehost.h" #include "cmdhfmfu.h" 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); +/** + * @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 * @brief Utility function to load data from a binary file. This method takes a preferred name. * E.g. dumpdata-15.bin From 7dbb5427eaf0b92d49be89ffed341d4fb5f46324 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Sat, 24 Aug 2019 18:11:03 +0200 Subject: [PATCH 04/10] Transformation from hardautopwn to autopwn, now are all the mifare classic attacks automated. --- client/cmdhfmf.c | 397 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 271 insertions(+), 126 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index e968548b6..460089151 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -166,20 +166,24 @@ static int usage_hf14_hardnested(void) { PrintAndLogEx(NORMAL, " hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF"); return 0; } -static int usage_hf14_hardautopwn(void) { +static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " hf mf hardautopwn [k] "); - PrintAndLogEx(NORMAL, " * [d] [f] [s] [i] [l]"); + PrintAndLogEx(NORMAL, " hf mf autopwn [k] "); + PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l]"); PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Description:"); + PrintAndLogEx(NORMAL, " This command is used to automate the attack process on mifare classic nfc cards."); + PrintAndLogEx(NORMAL, " The program tries to identify the prng type and then automatically attack it with the best algorithm."); + PrintAndLogEx(NORMAL, " If all the sector keys are broken, they will be dumped to a file."); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); - PrintAndLogEx(NORMAL, " d write keys to binary file"); PrintAndLogEx(NORMAL, " f .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) for hardnested"); PrintAndLogEx(NORMAL, " l legacy mode (use the slow mfchk for the key enumeration)"); - PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); + PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it. (for hardnested)"); PrintAndLogEx(NORMAL, " i 5 = AVX512"); PrintAndLogEx(NORMAL, " i 2 = AVX2"); PrintAndLogEx(NORMAL, " i a = AVX"); @@ -188,13 +192,11 @@ static int usage_hf14_hardautopwn(void) { PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); PrintAndLogEx(NORMAL, ""); 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 * 1 d f default_keys"); - PrintAndLogEx(NORMAL, " hf mf hardautopwn k 0 A FFFFFFFFFFFF * 4 s i 5"); - - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " hf mf autopwn"); + PrintAndLogEx(NORMAL, " hf mf autopwn * 1 f default_keys"); + PrintAndLogEx(NORMAL, " hf mf autopwn k 0 A FFFFFFFFFFFF"); + PrintAndLogEx(NORMAL, " hf mf autopwn k 0 A FFFFFFFFFFFF * 1 f default_keys"); + PrintAndLogEx(NORMAL, " hf mf autopwn k 0 A FFFFFFFFFFFF * 4 i 5"); return 0; } static int usage_hf14_chk(void) { @@ -1562,58 +1564,52 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { } -static int CmdHF14AMfHardAuto(const char *Cmd) { +static int CmdHF14AMfAutoPWN(const char *Cmd) { + // Nested and Hardnested parameter uint8_t blockNo = 0; uint8_t keyType = 0; - uint8_t *keyBlock; - uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; - sector_t *e_sector; uint8_t key[6] = {0}; - uint8_t tmpKey[6] = {0}; uint64_t key64 = 0; + // Attack key storage variables + uint8_t *keyBlock; uint16_t keycnt = 0; + sector_t *e_sector; + uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; + uint8_t tmpKey[6] = {0}; size_t datalen = 0; - uint32_t chunksize; - - uint64_t t1; // For the timier - uint8_t foundKeysDictionary = 0; - uint8_t foundKeysReused = 0; - uint8_t foundKeysHardnested = 0; - + // For the timier + uint64_t t1; + // Parameters and dictionary file char filename[FILE_PATH_SIZE] = {0}; uint8_t cmdp = 0; char ctmp; - + // Nested and Hardnested returned status uint64_t foundkey = 0; int16_t isOK = 0; - - int i, i2, i3; // Loop counter + // Loop counter + int i, i2, i3; int current_sector_i = 0, current_key_type_i = 0; - + // Settings bool slow = false; bool nonce_file_read = false; bool nonce_file_write = false; - bool createDumpFile = false; bool know_target_key = false; bool legacy_mfchk = false; - bool firstChunk = 0, lastChunk = 0; + bool prng_type = false; + bool calibrate = true; + // Parse the options given by the user ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_hardautopwn(); - while ((ctmp = param_getchar(Cmd, cmdp))) { switch (tolower(ctmp)) { case 'h': - return usage_hf14_hardautopwn(); + return usage_hf14_autopwn(); case 'f': if (param_getstr(Cmd, cmdp +1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { PrintAndLogEx(FAILED, "Filename too long"); } cmdp ++; break; - case 'd': - createDumpFile = true; - break; case 'l': legacy_mfchk = true; break; @@ -1684,20 +1680,6 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { cmdp++; } - // Print parameters - PrintAndLogEx(NORMAL, "Used Parameters:"); - PrintAndLogEx(SUCCESS, "Dumping the found keys: %s", createDumpFile ? "True" : "False"); - PrintAndLogEx(SUCCESS, "Card sectors: %d", sectorsCnt); - PrintAndLogEx(SUCCESS, "Key supplied: %s", know_target_key ? "True" : "False"); - PrintAndLogEx(SUCCESS, "Known sector: %d", blockNo); - PrintAndLogEx(SUCCESS, "Keytype: %c", keyType ? 'B' : 'A'); - PrintAndLogEx(SUCCESS, "Kown key: 0x%02x%02x%02x%02x%02x%02x", key[0], key[1], key[2], key[3], key[4], key[5]); - PrintAndLogEx(SUCCESS, "Dictionary: %s", filename); - PrintAndLogEx(SUCCESS, "Legacy mode (mfchk): %s", legacy_mfchk ? "True" : "False"); - PrintAndLogEx(WARNING, "Starting attack!"); - if (know_target_key == false) - PrintAndLogEx(WARNING, "No known key was supplied, if no usable key is found in the dictionary, then this attack will fail!"); - // Create the key storage stucture e_sector = calloc(sectorsCnt, sizeof(sector_t)); if (e_sector == NULL) return PM3_EMALLOC; @@ -1709,9 +1691,74 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { } } + // Get the card prng type (weak=true / hard=false) + prng_type = detect_classic_prng(); + + // Print operating parameters + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Card sectors .. " _YELLOW_("%d"), sectorsCnt); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Key supplied .. " _YELLOW_("%s"), know_target_key ? "True" : "False"); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Known sector .. " _YELLOW_("%d"), blockNo); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Keytype ....... " _YELLOW_("%c"), keyType ? 'B' : 'A'); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Kown key ...... " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4], key[5]); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); + PrintAndLogEx(SUCCESS, "[ SETTINGS ] Legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); + PrintAndLogEx(WARNING, "Starting attack!"); + + // Check the user supplied key + if (know_target_key == false) + PrintAndLogEx(WARNING, "No known key was supplied, the following attacks might fail!"); + else { + if (mfCheckKeys(FirstBlockOfSector(blockNo), keyType, true, 1, key, &key64) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "[ SETTINGS ] The following key will be used to the nested / hardnested attack: Sector:" + _RED_("%3d") " key type:"_RED_("%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]); + + // Store the key for the nested / hardnested attack (if supplied by the user) + e_sector[blockNo].Key[keyType] = bytes_to_num(key, 6); + e_sector[blockNo].foundKey[keyType] = 3; + } else { + know_target_key = false; + PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector:"_RED_("%3d") " key type:"_RED_("%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]); + PrintAndLogEx(WARNING, "Let's see if just the sector or keytype are not correct, and then we also give the dictionary a try ;)"); + } + // Check if the user supplied key is used by other sectors + for (i=0; i just be nice and correct it ;) + 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, "[ SETTINGS ] The following key will be used to the nested / hardnested attack: Sector:" + _RED_("%3d") " key type:"_RED_("%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]); + } + } + } + } + } + } + // Load the dictionary if (strlen(filename) != 0) { - keyBlock = calloc(6 * 1000, sizeof(uint8_t)); + keyBlock = calloc(6 * 2000, sizeof(uint8_t)); loadFileDICTIONARY(filename, keyBlock, &datalen, 6, &keycnt); } else { keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); @@ -1726,6 +1773,7 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { // Start the timer t1 = msclock(); + // Use the dictionary to find sector keys on the card PrintAndLogEx(SUCCESS, "Enumerating the card keys with the dictionary!"); if (legacy_mfchk) { // Check all the sectors @@ -1735,7 +1783,7 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { if (e_sector[i].foundKey[i2] == 0) { for (i3=0; i3 (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; - firstChunk = true; lastChunk = false; + int chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; + bool firstChunk = true, lastChunk = false; for (uint8_t strategy = 1; strategy < 3; strategy++) { PrintAndLogEx(SUCCESS, "Running strategy %u", strategy); // main keychunk loop @@ -1774,45 +1822,82 @@ static int CmdHF14AMfHardAuto(const char *Cmd) { } // end strategy } - // Check if at least one key was recovered + // Analyse the dictionary attack for (i=0; i The fast check --> mfCheckKeys_fast(sectorsCnt, true, true, 2, 1, tmpKey, e_sector, false); + // Returns false keys, so we just stick to the slower mfchk. for (i=0; i try hardnested instead!"); + goto tryHardnested; + break; + case -4 : //key not found + calibrate = false; + PrintAndLogEx(FAILED, "Nested attack failed --> try hardnested instead!"); + goto tryHardnested; + break; + case -5 : + calibrate = false; + e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmpKey, 6); + e_sector[current_sector_i].foundKey[current_key_type_i] = 5; break; default : + PrintAndLogEx(ERR, "unknown Error.\n"); + return 1; break; } - free(e_sector); - return 2; - } + } else { + tryHardnested: // If the nested attack failes then we try the hardnested attack + PrintAndLogEx(SUCCESS, "[ HARDNESTED ] Sector no:%3d, target key type:%c, Slow: %s", + current_sector_i, + current_key_type_i ? 'B' : 'A', + slow ? "Yes" : "No"); - // Copy the found key to the tmpKey variale (for the following print statement, and the mfCheckKeys above) - num_to_bytes(foundkey, 6, tmpKey); - e_sector[current_sector_i].Key[current_key_type_i] = foundkey; - e_sector[current_sector_i].foundKey[current_key_type_i] = 1; - PrintAndLogEx(SUCCESS, "[HARDNE. 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]); - foundKeysHardnested++; + isOK = mfnestedhard(FirstBlockOfSector(blockNo), keyType, key, FirstBlockOfSector(current_sector_i), 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; + } + + // Copy the found key to the tmpKey variale (for the following print statement, and the mfCheckKeys above) + num_to_bytes(foundkey, 6, tmpKey); + e_sector[current_sector_i].Key[current_key_type_i] = foundkey; + e_sector[current_sector_i].foundKey[current_key_type_i] = 6; + } + // Check if the key was found + if (e_sector[current_sector_i].foundKey[current_key_type_i] != 0) { + PrintAndLogEx(SUCCESS, "[BROCKEN 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]); + } else { + PrintAndLogEx(FAILED, "[BROCKEN KEY] Valid KEY NOT 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]); + } } } } } + // Show the results to the user + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Found KEYS:"); printKeyTable(sectorsCnt, e_sector); - // Create a dumpfile - if (createDumpFile) { - PrintAndLogEx(SUCCESS, "\nDumping the found keys:"); - createKeyDump(sectorsCnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); - } + PrintAndLogEx(SUCCESS, "[ INFO ] Key res types:"); + PrintAndLogEx(SUCCESS, " 1: Dictionary"); + PrintAndLogEx(SUCCESS, " 2: Darkside attack"); + PrintAndLogEx(SUCCESS, " 3: User supplied"); + PrintAndLogEx(SUCCESS, " 4: Reused"); + PrintAndLogEx(SUCCESS, " 5: Nested"); + PrintAndLogEx(SUCCESS, " 6: Hardnested"); + // Generate and show statistics t1 = msclock() - t1; - 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 autopwn attack: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + + // Create a dumpfile if all keys are known + 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++) { + // If the key is already known, just skip it + if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { + PrintAndLogEx(FAILED, "Not all keys were discovered --> no dump file will be created!"); + goto notAllKeysFound; + } + } + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "All keys were discovered, dumping them now:"); + createKeyDump(sectorsCnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); + notAllKeysFound: free(e_sector); - free(keyBlock); return 0; } @@ -3987,7 +4132,7 @@ static command_t CommandTable[] = { {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack. read parity error messages."}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack. Test nested authentication"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened Mifare cards"}, - {"hardautopwn", CmdHF14AMfHardAuto, AlwaysAvailable, "Nested attack for hardened Mifare cards that breaks all sector keys autmatically"}, + {"autopwn", CmdHF14AMfAutoPWN, AlwaysAvailable, "Automatic attack tool, to extrackt the nfc keys (with dicrionaries, nested and hardnested attacks)"}, {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, {"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for Mifare NACK bug"}, {"chk", CmdHF14AMfChk, IfPm3Iso14443a, "Check keys"}, From bd01b7bd96859abf5b9f6035e0a81ff1ba407af3 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Sun, 25 Aug 2019 11:13:53 +0200 Subject: [PATCH 05/10] Now the keys are transfered to the simulator memory and the whole card gets dumped at the end. --- client/cmdhfmf.c | 111 ++++++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 460089151..473d4b297 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -169,13 +169,13 @@ static int usage_hf14_hardnested(void) { static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, "Usage:"); PrintAndLogEx(NORMAL, " hf mf autopwn [k] "); - PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l]"); + PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l] [t] [d]"); PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Description:"); PrintAndLogEx(NORMAL, " This command is used to automate the attack process on mifare classic nfc cards."); PrintAndLogEx(NORMAL, " The program tries to identify the prng type and then automatically attack it with the best algorithm."); - PrintAndLogEx(NORMAL, " If all the sector keys are broken, they will be dumped to a file."); + PrintAndLogEx(NORMAL, " After the program is done, the keys and card data is dumped."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); @@ -1575,6 +1575,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint16_t keycnt = 0; sector_t *e_sector; uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; + int blockCnt = MIFARE_1K_MAXBLOCK; uint8_t tmpKey[6] = {0}; size_t datalen = 0; // For the timier @@ -1589,6 +1590,11 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Loop counter int i, i2, i3; int current_sector_i = 0, current_key_type_i = 0; + // Dumping and transfere to simulater memory + uint8_t block[16] = {0x00}; + uint8_t *dump; + int bytes; + char* fnameptr = filename; // Settings bool slow = false; bool nonce_file_read = false; @@ -1616,6 +1622,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { case '*': // Get the number of sectors sectorsCnt = NumOfSectors(param_getchar(Cmd, cmdp + 1)); + blockCnt = NumOfBlocks(param_getchar(Cmd, cmdp + 1)); cmdp ++; break; case 'k': @@ -1695,14 +1702,14 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { prng_type = detect_classic_prng(); // Print operating parameters - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Card sectors .. " _YELLOW_("%d"), sectorsCnt); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Key supplied .. " _YELLOW_("%s"), know_target_key ? "True" : "False"); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Known sector .. " _YELLOW_("%d"), blockNo); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Keytype ....... " _YELLOW_("%c"), keyType ? 'B' : 'A'); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Kown key ...... " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4], key[5]); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); - PrintAndLogEx(SUCCESS, "[ SETTINGS ] Legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); + PrintAndLogEx(INFO, "[ SETTINGS ] Card sectors .. " _YELLOW_("%d"), sectorsCnt); + PrintAndLogEx(INFO, "[ SETTINGS ] Key supplied .. " _YELLOW_("%s"), know_target_key ? "True" : "False"); + PrintAndLogEx(INFO, "[ SETTINGS ] Known sector .. " _YELLOW_("%d"), blockNo); + PrintAndLogEx(INFO, "[ SETTINGS ] Keytype ....... " _YELLOW_("%c"), keyType ? 'B' : 'A'); + PrintAndLogEx(INFO, "[ SETTINGS ] Kown key ...... " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4], key[5]); + PrintAndLogEx(INFO, "[ SETTINGS ] Card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); + PrintAndLogEx(INFO, "[ SETTINGS ] Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); + PrintAndLogEx(INFO, "[ SETTINGS ] Legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); PrintAndLogEx(WARNING, "Starting attack!"); // Check the user supplied key @@ -1710,7 +1717,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(WARNING, "No known key was supplied, the following attacks might fail!"); else { if (mfCheckKeys(FirstBlockOfSector(blockNo), keyType, true, 1, key, &key64) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "[ SETTINGS ] The following key will be used to the nested / hardnested attack: Sector:" + PrintAndLogEx(INFO, "[ SETTINGS ] The following key will be used to the nested / hardnested attack: Sector:" _RED_("%3d") " key type:"_RED_("%c") " key: " _RED_("0x%02x%02x%02x%02x%02x%02x"), blockNo, keyType ? 'B' : 'A', @@ -1774,7 +1781,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { t1 = msclock(); // Use the dictionary to find sector keys on the card - PrintAndLogEx(SUCCESS, "Enumerating the card keys with the dictionary!"); + PrintAndLogEx(INFO, "Enumerating the card keys with the dictionary!"); if (legacy_mfchk) { // Check all the sectors for (i=0; i (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; bool firstChunk = true, lastChunk = false; for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(SUCCESS, "Running strategy %u", strategy); + PrintAndLogEx(INFO, "Running strategy %u", strategy); // main keychunk loop for (i = 0; i < keycnt; i += chunksize) { @@ -1851,7 +1858,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (know_target_key == false) { // Check if the darkside attack can be used if (prng_type) { - PrintAndLogEx(WARNING, "No key was found ... time to go to the dark side ;)"); + PrintAndLogEx(INFO, "No key was found ... time to go to the dark side ;)"); int isOK = mfDarkside(FirstBlockOfSector(blockNo), keyType, &key64); switch (isOK) { case -1 : @@ -1933,7 +1940,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Use the nested / hardnested attack if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { if (prng_type) { - PrintAndLogEx(SUCCESS, "[ NESTED ] Sector no:%3d, target key type:%c", + PrintAndLogEx(INFO, "[ NESTED ] Sector no:%3d, target key type:%c", current_sector_i, current_key_type_i ? 'B' : 'A'); @@ -1969,7 +1976,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } } else { tryHardnested: // If the nested attack failes then we try the hardnested attack - PrintAndLogEx(SUCCESS, "[ HARDNESTED ] Sector no:%3d, target key type:%c, Slow: %s", + PrintAndLogEx(INFO, "[ HARDNESTED ] Sector no:%3d, target key type:%c, Slow: %s", current_sector_i, current_key_type_i ? 'B' : 'A', slow ? "Yes" : "No"); @@ -1998,12 +2005,12 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i] != 0) { - PrintAndLogEx(SUCCESS, "[BROCKEN KEY] Valid KEY FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), + PrintAndLogEx(SUCCESS, "[BROCKEN 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]); } else { - PrintAndLogEx(FAILED, "[BROCKEN KEY] Valid KEY NOT FOUND: sector:%3d key type:%c key: " _YELLOW_("0x%02x%02x%02x%02x%02x%02x"), + PrintAndLogEx(FAILED, "[BROCKEN KEY] Valid KEY NOT 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]); @@ -2015,35 +2022,61 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Show the results to the user PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Found KEYS:"); + PrintAndLogEx(INFO, "Found KEYS:"); printKeyTable(sectorsCnt, e_sector); - PrintAndLogEx(SUCCESS, "[ INFO ] Key res types:"); - PrintAndLogEx(SUCCESS, " 1: Dictionary"); - PrintAndLogEx(SUCCESS, " 2: Darkside attack"); - PrintAndLogEx(SUCCESS, " 3: User supplied"); - PrintAndLogEx(SUCCESS, " 4: Reused"); - PrintAndLogEx(SUCCESS, " 5: Nested"); - PrintAndLogEx(SUCCESS, " 6: Hardnested"); + PrintAndLogEx(INFO, "[ INFO ] Key res types:"); + PrintAndLogEx(INFO, " 1: Dictionary"); + PrintAndLogEx(INFO, " 2: Darkside attack"); + PrintAndLogEx(INFO, " 3: User supplied"); + PrintAndLogEx(INFO, " 4: Reused"); + PrintAndLogEx(INFO, " 5: Nested"); + PrintAndLogEx(INFO, " 6: Hardnested"); // Generate and show statistics t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Required time for the autopwn attack: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "Required time for the autopwn attack: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); - // Create a dumpfile if all keys are known - 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++) { - // If the key is already known, just skip it - if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - PrintAndLogEx(FAILED, "Not all keys were discovered --> no dump file will be created!"); - goto notAllKeysFound; - } - } - } + // Transfere the found keys to the simulator and dump the keys and card data PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "All keys were discovered, dumping them now:"); + PrintAndLogEx(INFO, "Dumping the keys:"); createKeyDump(sectorsCnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); - notAllKeysFound: + PrintAndLogEx(SUCCESS, "Transfering the found keys to the simulator memory"); + for (current_sector_i=0; current_sector_i < sectorsCnt; current_sector_i++) { + mfEmlGetMem(block, current_sector_i, 1); + if (e_sector[current_sector_i].foundKey[0]) + num_to_bytes(e_sector[current_sector_i].Key[0], 6, block); + if (e_sector[current_sector_i].foundKey[1]) + num_to_bytes(e_sector[current_sector_i].Key[1], 6, block + 10); + mfEmlSetMem(block, FirstBlockOfSector(current_sector_i) + NumBlocksPerSector(current_sector_i) - 1, 1); + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_EML_LOAD, sectorsCnt, 0, 0, NULL, 0); + + bytes = blockCnt * MFBLOCK_SIZE; + dump = calloc(bytes, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + memset(dump, 0, bytes); + + PrintAndLogEx(INFO, "Downloading the card content from emulator memory"); + if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(dump); + return PM3_ETIMEOUT; + } + + fnameptr += sprintf(fnameptr, "hf-mf-"); + FillFileNameByUID(fnameptr, dump, "-dump", 4); + + saveFile(filename, ".bin", dump, bytes); + saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); + saveFileJSON(filename, jsfCardMemory, dump, bytes); + free(dump); + free(e_sector); return 0; } From 47b2c5ddc62b28ef366843fc5cb3d0b0e127f2c8 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Sun, 25 Aug 2019 11:16:10 +0200 Subject: [PATCH 06/10] Removed old command options. --- client/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 473d4b297..067cdb212 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -169,7 +169,7 @@ static int usage_hf14_hardnested(void) { static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, "Usage:"); PrintAndLogEx(NORMAL, " hf mf autopwn [k] "); - PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l] [t] [d]"); + PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l]"); PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Description:"); From 13641771baefae4b0cb8b176dcdcbe1549b4279c Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Sun, 25 Aug 2019 22:24:52 +0200 Subject: [PATCH 07/10] Improved help message, implementation of a verbose option (to reduce the output), corrected some spelling mistakes and cleaned up variable names. --- client/cmdhfmf.c | 206 ++++++++++++++++++++++++++--------------------- 1 file changed, 112 insertions(+), 94 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 067cdb212..4cd1a02a7 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -169,8 +169,7 @@ static int usage_hf14_hardnested(void) { static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, "Usage:"); PrintAndLogEx(NORMAL, " hf mf autopwn [k] "); - PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l]"); - PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K)"); + PrintAndLogEx(NORMAL, " [*] [f] .dic [s] [i] [l] [v]"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Description:"); PrintAndLogEx(NORMAL, " This command is used to automate the attack process on mifare classic nfc cards."); @@ -182,7 +181,13 @@ static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); PrintAndLogEx(NORMAL, " f .dic dictionary file for key discovery (the file has to end in .dic)"); PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards) for hardnested"); + PrintAndLogEx(NORMAL, " v verbose output (statistcs)"); PrintAndLogEx(NORMAL, " l legacy mode (use the slow mfchk for the key enumeration)"); + PrintAndLogEx(NORMAL, " * all sectors based on card memory, other values then below defaults to 1k"); + PrintAndLogEx(NORMAL, " * 0 = MINI(320 bytes)"); + PrintAndLogEx(NORMAL, " * 1 = 1K"); + PrintAndLogEx(NORMAL, " * 2 = 2K"); + PrintAndLogEx(NORMAL, " * 4 = 4K"); PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it. (for hardnested)"); PrintAndLogEx(NORMAL, " i 5 = AVX512"); PrintAndLogEx(NORMAL, " i 2 = AVX2"); @@ -1570,14 +1575,16 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint8_t keyType = 0; uint8_t key[6] = {0}; uint64_t key64 = 0; + bool calibrate = true; // Attack key storage variables uint8_t *keyBlock; uint16_t keycnt = 0; - sector_t *e_sector; + sector_t *eSector; uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; int blockCnt = MIFARE_1K_MAXBLOCK; uint8_t tmpKey[6] = {0}; size_t datalen = 0; + bool knowTargetKey = false; // For the timier uint64_t t1; // Parameters and dictionary file @@ -1597,12 +1604,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { char* fnameptr = filename; // Settings bool slow = false; - bool nonce_file_read = false; - bool nonce_file_write = false; - bool know_target_key = false; bool legacy_mfchk = false; bool prng_type = false; - bool calibrate = true; + bool verbose = false; // Parse the options given by the user ctmp = tolower(param_getchar(Cmd, 0)); @@ -1619,6 +1623,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { case 'l': legacy_mfchk = true; break; + case 'v': + verbose = true; + break; case '*': // Get the number of sectors sectorsCnt = NumOfSectors(param_getchar(Cmd, cmdp + 1)); @@ -1646,7 +1653,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(WARNING, "Key must include 12 HEX symbols"); return 1; } - know_target_key = true; + knowTargetKey = true; cmdp += 3; case 's': slow = true; @@ -1688,13 +1695,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Create the key storage stucture - e_sector = calloc(sectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return PM3_EMALLOC; + eSector = calloc(sectorsCnt, sizeof(sector_t)); + if (eSector == NULL) return PM3_EMALLOC; // Clear the key storage datastructure for (i=0; i just be nice and correct it ;) - if (know_target_key == false) { - num_to_bytes(e_sector[i].Key[i2], 6, key); - know_target_key = true; + if (knowTargetKey == false) { + num_to_bytes(eSector[i].Key[i2], 6, key); + knowTargetKey = true; blockNo = i; keyType = i2; - PrintAndLogEx(SUCCESS, "[ SETTINGS ] The following key will be used to the nested / hardnested attack: Sector:" + PrintAndLogEx(SUCCESS, "[ SETTINGS ] The following key will be used for the nested / hardnested attack: sector:" _RED_("%3d") " key type:"_RED_("%c") " key: " _RED_("0x%02x%02x%02x%02x%02x%02x"), blockNo, keyType ? 'B' : 'A', @@ -1769,7 +1777,10 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { loadFileDICTIONARY(filename, keyBlock, &datalen, 6, &keycnt); } else { keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); - if (keyBlock == NULL) return 1; + if (keyBlock == NULL) { + free(eSector); + 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); @@ -1787,12 +1798,12 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { for (i=0; i The fast check --> mfCheckKeys_fast(sectorsCnt, true, true, 2, 1, tmpKey, e_sector, false); + // The fast check --> mfCheckKeys_fast(sectorsCnt, true, true, 2, 1, tmpKey, eSector, false); // Returns false keys, so we just stick to the slower mfchk. for (i=0; i try hardnested instead!"); goto tryHardnested; break; @@ -1966,11 +1979,12 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { break; case -5 : calibrate = false; - e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmpKey, 6); - e_sector[current_sector_i].foundKey[current_key_type_i] = 5; + eSector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmpKey, 6); + eSector[current_sector_i].foundKey[current_key_type_i] = 5; break; default : PrintAndLogEx(ERR, "unknown Error.\n"); + free(eSector); return 1; break; } @@ -1981,30 +1995,30 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { current_key_type_i ? 'B' : 'A', slow ? "Yes" : "No"); - isOK = mfnestedhard(FirstBlockOfSector(blockNo), keyType, key, FirstBlockOfSector(current_sector_i), current_key_type_i, NULL, nonce_file_read, nonce_file_write, slow, 0, &foundkey, NULL); + isOK = mfnestedhard(FirstBlockOfSector(blockNo), keyType, key, FirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL); DropField(); if (isOK) { switch (isOK) { case 1 : - PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n"); + PrintAndLogEx(ERR, "\nError: No response from Proxmark3."); break; case 2 : - PrintAndLogEx(NORMAL, "Button pressed. Aborted.\n"); + PrintAndLogEx(NORMAL, "\nButton pressed. Aborted."); break; default : break; } - free(e_sector); + free(eSector); return 2; } // Copy the found key to the tmpKey variale (for the following print statement, and the mfCheckKeys above) num_to_bytes(foundkey, 6, tmpKey); - e_sector[current_sector_i].Key[current_key_type_i] = foundkey; - e_sector[current_sector_i].foundKey[current_key_type_i] = 6; + eSector[current_sector_i].Key[current_key_type_i] = foundkey; + eSector[current_sector_i].foundKey[current_key_type_i] = 6; } // Check if the key was found - if (e_sector[current_sector_i].foundKey[current_key_type_i] != 0) { + if (eSector[current_sector_i].foundKey[current_key_type_i] != 0) { PrintAndLogEx(SUCCESS, "[BROCKEN 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', @@ -2022,32 +2036,30 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Show the results to the user PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Found KEYS:"); - printKeyTable(sectorsCnt, e_sector); - PrintAndLogEx(INFO, "[ INFO ] Key res types:"); - PrintAndLogEx(INFO, " 1: Dictionary"); - PrintAndLogEx(INFO, " 2: Darkside attack"); - PrintAndLogEx(INFO, " 3: User supplied"); - PrintAndLogEx(INFO, " 4: Reused"); - PrintAndLogEx(INFO, " 5: Nested"); - PrintAndLogEx(INFO, " 6: Hardnested"); + PrintAndLogEx(INFO, "Found Keys:"); + printKeyTable(sectorsCnt, eSector); + if (verbose) { + PrintAndLogEx(INFO, "[ INFO ] Key res types:"); + PrintAndLogEx(INFO, " 1: Dictionary"); + PrintAndLogEx(INFO, " 2: Darkside attack"); + PrintAndLogEx(INFO, " 3: User supplied"); + PrintAndLogEx(INFO, " 4: Reused"); + PrintAndLogEx(INFO, " 5: Nested"); + PrintAndLogEx(INFO, " 6: Hardnested"); + } - // Generate and show statistics - t1 = msclock() - t1; - PrintAndLogEx(INFO, "Required time for the autopwn attack: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); - // Transfere the found keys to the simulator and dump the keys and card data PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Dumping the keys:"); - createKeyDump(sectorsCnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); + createKeyDump(sectorsCnt, eSector, GenerateFilename("hf-mf-", "-key.bin")); PrintAndLogEx(SUCCESS, "Transfering the found keys to the simulator memory"); for (current_sector_i=0; current_sector_i < sectorsCnt; current_sector_i++) { mfEmlGetMem(block, current_sector_i, 1); - if (e_sector[current_sector_i].foundKey[0]) - num_to_bytes(e_sector[current_sector_i].Key[0], 6, block); - if (e_sector[current_sector_i].foundKey[1]) - num_to_bytes(e_sector[current_sector_i].Key[1], 6, block + 10); + if (eSector[current_sector_i].foundKey[0]) + num_to_bytes(eSector[current_sector_i].Key[0], 6, block); + if (eSector[current_sector_i].foundKey[1]) + num_to_bytes(eSector[current_sector_i].Key[1], 6, block + 10); mfEmlSetMem(block, FirstBlockOfSector(current_sector_i) + NumBlocksPerSector(current_sector_i) - 1, 1); } @@ -2058,6 +2070,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { dump = calloc(bytes, sizeof(uint8_t)); if (!dump) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + free(eSector); return PM3_EMALLOC; } memset(dump, 0, bytes); @@ -2065,6 +2078,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, "Downloading the card content from emulator memory"); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(eSector); free(dump); return PM3_ETIMEOUT; } @@ -2075,9 +2089,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); saveFileJSON(filename, jsfCardMemory, dump, bytes); - free(dump); - free(e_sector); + // Generate and show statistics + t1 = msclock() - t1; + PrintAndLogEx(INFO, "Required time for the autopwn attack: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + + free(dump); + free(eSector); return 0; } From 2edee59837f0903c5db1a7ceb3e7addcd910e87c Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Mon, 26 Aug 2019 12:27:40 +0200 Subject: [PATCH 08/10] Changed the variable names, added aditional dictionary size checks, fixed a corner case with the key transferes. --- client/cmdhfmf.c | 204 +++++++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 97 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 4cd1a02a7..1fb2d48ff 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -179,7 +179,7 @@ static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " k if a known key for a block is supplied"); - PrintAndLogEx(NORMAL, " f .dic dictionary file for key discovery (the file has to end in .dic)"); + PrintAndLogEx(NORMAL, " f .dic dictionary file for key discovery (the file has to end in .dic) max 2000 entries allowed"); PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards) for hardnested"); PrintAndLogEx(NORMAL, " v verbose output (statistcs)"); PrintAndLogEx(NORMAL, " l legacy mode (use the slow mfchk for the key enumeration)"); @@ -1578,13 +1578,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { bool calibrate = true; // Attack key storage variables uint8_t *keyBlock; - uint16_t keycnt = 0; - sector_t *eSector; - uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; - int blockCnt = MIFARE_1K_MAXBLOCK; - uint8_t tmpKey[6] = {0}; - size_t datalen = 0; - bool knowTargetKey = false; + uint16_t key_cnt = 0; + sector_t *e_sector; + uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR; + int block_cnt = MIFARE_1K_MAXBLOCK; + uint8_t tmp_key[6] = {0}; + size_t data_length = 0; + bool know_target_key = false; // For the timier uint64_t t1; // Parameters and dictionary file @@ -1607,6 +1607,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { bool legacy_mfchk = false; bool prng_type = false; bool verbose = false; + int max_dictionary_size = 2000; // Parse the options given by the user ctmp = tolower(param_getchar(Cmd, 0)); @@ -1628,8 +1629,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { break; case '*': // Get the number of sectors - sectorsCnt = NumOfSectors(param_getchar(Cmd, cmdp + 1)); - blockCnt = NumOfBlocks(param_getchar(Cmd, cmdp + 1)); + sectors_cnt = NumOfSectors(param_getchar(Cmd, cmdp + 1)); + block_cnt = NumOfBlocks(param_getchar(Cmd, cmdp + 1)); cmdp ++; break; case 'k': @@ -1653,7 +1654,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(WARNING, "Key must include 12 HEX symbols"); return 1; } - knowTargetKey = true; + know_target_key = true; cmdp += 3; case 's': slow = true; @@ -1695,13 +1696,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Create the key storage stucture - eSector = calloc(sectorsCnt, sizeof(sector_t)); - if (eSector == NULL) return PM3_EMALLOC; + e_sector = calloc(sectors_cnt, sizeof(sector_t)); + if (e_sector == NULL) return PM3_EMALLOC; // Clear the key storage datastructure - for (i=0; i just be nice and correct it ;) - if (knowTargetKey == false) { - num_to_bytes(eSector[i].Key[i2], 6, key); - knowTargetKey = true; + 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, "[ SETTINGS ] The following key will be used for the nested / hardnested attack: sector:" _RED_("%3d") " key type:"_RED_("%c") " key: " _RED_("0x%02x%02x%02x%02x%02x%02x"), @@ -1773,19 +1774,26 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Load the dictionary if (strlen(filename) != 0) { - keyBlock = calloc(6 * 2000, sizeof(uint8_t)); - loadFileDICTIONARY(filename, keyBlock, &datalen, 6, &keycnt); + keyBlock = calloc(6 * max_dictionary_size, sizeof(uint8_t)); + loadFileDICTIONARY(filename, keyBlock, &data_length, 6, &key_cnt); + if ((data_length / 6) > max_dictionary_size) { + // This is not a good solution (loadFileDICTIONARY needs a maxdatalen)! + PrintAndLogEx(FAILED, "The loaded dictionary is too large: %d (allowed: %d)", data_length, max_dictionary_size); + free(keyBlock); // This won't work too well, because data on the stack is already overflown !!! + free(e_sector); + return 1; + } } else { keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) { - free(eSector); + free(e_sector); 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); + key_cnt = ARRAYLEN(g_mifare_default_keys); } // Start the timer @@ -1795,15 +1803,15 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, "Enumerating the card keys with the dictionary!"); if (legacy_mfchk) { // Check all the sectors - for (i=0; i (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; + int chunksize = key_cnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : key_cnt; bool firstChunk = true, lastChunk = false; for (uint8_t strategy = 1; strategy < 3; strategy++) { PrintAndLogEx(INFO, "Running strategy %u", strategy); // main keychunk loop - for (i = 0; i < keycnt; i += chunksize) { + for (i = 0; i < key_cnt; i += chunksize) { if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - i = keycnt; strategy = 3; break; // Exit the loop + i = key_cnt; strategy = 3; break; // Exit the loop } - uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; + uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; // last chunk? - if (size == keycnt - i) + if (size == key_cnt - i) lastChunk = true; - int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), eSector, false); + int res = mfCheckKeys_fast(sectors_cnt, 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 + i = key_cnt; strategy = 3; break; // Exit the loop } } // end chunks of keys firstChunk = true; @@ -1841,19 +1849,19 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Analyse the dictionary attack - for (i=0; i The fast check --> mfCheckKeys_fast(sectorsCnt, true, true, 2, 1, tmpKey, eSector, false); + if (bytes_to_num(tmp_key, 6) != 0) { + // The fast check --> mfCheckKeys_fast(sectors_cnt, true, true, 2, 1, tmp_key, e_sector, false); // Returns false keys, so we just stick to the slower mfchk. - for (i=0; i Date: Mon, 26 Aug 2019 14:29:21 +0200 Subject: [PATCH 09/10] Changed createKeyDump to a less generic name (createMfcKeyDump). --- client/cmdhfmf.c | 31 +++---------------------------- client/fileutils.c | 2 +- client/fileutils.h | 2 +- 3 files changed, 5 insertions(+), 30 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 1fb2d48ff..33f29f4d5 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -2059,7 +2059,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Transfere the found keys to the simulator and dump the keys and card data PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Dumping the keys:"); - createKeyDump(sectors_cnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); + createMfcKeyDump(sectors_cnt, e_sector, GenerateFilename("hf-mf-", "-key.bin")); PrintAndLogEx(SUCCESS, "Transfering the found keys to the simulator memory (Cmd Error: 04 can occour, but this shouldn't be a problem)"); for (current_sector_i=0; current_sector_i < sectors_cnt; current_sector_i++) { @@ -2352,7 +2352,7 @@ out: if (createDumpFile) { fptr = GenerateFilename("hf-mf-", "-key.bin"); - createKeyDump(sectorsCnt, e_sector, fptr); + createMfcKeyDump(sectorsCnt, e_sector, fptr); } } @@ -2378,7 +2378,6 @@ static int CmdHF14AMfChk(const char *Cmd) { uint8_t keyType = 0; uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); uint64_t key64 = 0; - uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; char *fptr; int clen = 0; int transferToEml = 0; @@ -2633,31 +2632,7 @@ out: if (createDumpFile) { fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) { - free(keyBlock); - free(e_sector); - return PM3_EFILE; - } - - FILE *fkeys = fopen(fptr, "wb"); - if (fkeys == NULL) { - PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), fptr); - free(keyBlock); - free(e_sector); - return PM3_EFILE; - } - PrintAndLogEx(INFO, "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 file " _YELLOW_("%s")". 0xffffffffffff has been inserted for unknown keys.", fptr); + createMfcKeyDump(SectorsCnt, e_sector, fptr); } free(keyBlock); diff --git a/client/fileutils.c b/client/fileutils.c index 124a51edc..3128fd451 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -293,7 +293,7 @@ out: return retval; } -int createKeyDump(uint8_t sectorsCnt, sector_t *e_sector, char* fptr) { +int createMfcKeyDump(uint8_t sectorsCnt, sector_t *e_sector, char* fptr) { uint8_t tmpKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; int i; diff --git a/client/fileutils.h b/client/fileutils.h index 61b7b0468..c0dad645b 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -112,7 +112,7 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s * @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); +int createMfcKeyDump(uint8_t sectorsCnt, sector_t *e_sector, char* fptr); /** STUB * @brief Utility function to load data from a binary file. This method takes a preferred name. From 917d76bcc1b3f3868387ec3602903abf32622d64 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Mon, 26 Aug 2019 14:58:24 +0200 Subject: [PATCH 10/10] Added changelog message for the autopwn feature. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8434cc84..253d981f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +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] - + - Add autopwn command to break mifare classic cards completely automatic (breaks all sector keys and dumps them and the card content) - Add Lua paths: look for scripts also in ~/.proxmark/lua{scripts,libs} and /usr/local/share/proxmark3/lua{scripts,libs} (@doegox) - Change Lua directory scripts/ to luascript/ (@doegox) - Change non-rdv4 PLATFORM must now use the generic PM3OTHER, simpler (@doegox)