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