From 407078d90a9d6ec7b040adbccd662d14db65d2c6 Mon Sep 17 00:00:00 2001 From: Matthias Konrath <office@inet-sec.at> Date: Thu, 22 Aug 2019 12:49:26 +0200 Subject: [PATCH] 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] <block number> <key A|B> <key (12 hex symbols)>"); + PrintAndLogEx(NORMAL, " <card memory> [d] [f] [s] [t] [i]"); + PrintAndLogEx(NORMAL, " (card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, <other> - 1K)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " k <block> <keytype> <key> if a known key for a block is supplied"); + 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, " s slower acquisition (required by some non standard cards)"); + PrintAndLogEx(NORMAL, " t tests?"); + PrintAndLogEx(NORMAL, " i <X> 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] <block number>|<*card memory> <key type (A/B/?)> [t|d] [<key (12 hex symbols)>] [<dic (*.dic)>]"); 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<sectorsCnt; i++) { + for (i2=0; i2<2; i2++) { + e_sector[i].Key[i2] = 0; + e_sector[i].foundKey[i2] = 0; + } + } + + // Load the keyfiles + if (strlen(filename) != 0) { + f = fopen(filename, "r"); + if (!f) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + return 1; + } + + // read file + 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); + 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); + } + + + // 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++) { + for (default_keys_i=0; default_keys_i < keycnt; default_keys_i++) { + // Iterate over the keys + for (i=0; i<6; i++) { + tmpKey[i] = keyBlock[i + (6*default_keys_i)]; + } + + 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"), + 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; + + // Exit the loop + current_sector_i = sectorsCnt; + current_key_type_i = 2; + default_keys_i = keycnt; + break; + } + } + } + } + } + + // Set the user defined 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; + } + + + // Iterate over each sector and key + 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++) { + + foundkey = e_sector[current_sector_i].Key[current_key_type_i]; + + // Try the found keys + if (foundkey == 0) { + for (found_keys_i=0; found_keys_i < current_sector_i; 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', + 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); + 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', + 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); + break; + } + } + } + } + // Try the default keys + if (foundkey == 0) { + for (default_keys_i=0; default_keys_i < keycnt; default_keys_i++) { + // Iterate over the keys + for (i=0; i<6; i++) { + tmpKey[i] = keyBlock[i + (6*default_keys_i)]; + } + + 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"), + 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); + break; + } + } + } + // Bruteforce with hardnested + if (foundkey == 0) { + PrintAndLogEx(SUCCESS, "[ BRUTEFORCE ] block 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); + 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"), + current_sector_i, + current_key_type_i ? 'B' : 'A', + tmpKey[0], tmpKey[1], tmpKey[2], tmpKey[3], tmpKey[4], tmpKey[5]); + } + // 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); + + 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(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); + } + + 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"},