From 225b18d5fc048d08807b4928c5a2a2448de752ab Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 09:34:46 +0200 Subject: [PATCH 1/9] Implemented a function to safely load dictionaries. --- client/cmdhfmf.c | 17 ++++------- client/cmdlft55xx.c | 3 +- client/fileutils.c | 73 +++++++++++++++++++++++++++++++++++++++++++++ client/fileutils.h | 12 +++++++- 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 99d6afd97..87fa500a8 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1591,7 +1591,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { 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; @@ -1613,7 +1612,6 @@ 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)); @@ -1788,17 +1786,14 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Load the dictionary if (strlen(filename) != 0) { - 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)! - // loadfiledictionary will reallocate to correct size. - PrintAndLogEx(FAILED, "Dictionary is too large: %d (allowed: %d)", data_length, max_dictionary_size); - free(keyBlock); - free(e_sector); - return PM3_EMALLOC; + int res = loadFileDICTIONARY_safe(filename, &keyBlock, 6, &key_cnt); + if (res != PM3_SUCCESS || key_cnt <= 0) { + PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); + free(keyBlock); // free the memory, just in case an allocation happened + goto useDefaultKeys; } } else { +useDefaultKeys: keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) { free(e_sector); diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 80954dddb..3802beab9 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -2127,7 +2127,6 @@ static int CmdT55xxChkPwds(const char *Cmd) { if (use_pwd_file) { uint16_t keycount = 0; - size_t datalen = 0; // TODO, a way of reallocating memory if file was larger keyBlock = calloc(4 * 200, sizeof(uint8_t)); @@ -2136,7 +2135,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { return PM3_ESOFT; } - int res = loadFileDICTIONARY(filename, keyBlock, &datalen, 4, &keycount); + int res = loadFileDICTIONARY_safe(filename, &keyBlock, 4, &keycount); if (res || keycount == 0) { PrintAndLogEx(WARNING, "No keys found in file"); free(keyBlock); diff --git a/client/fileutils.c b/client/fileutils.c index 15c6efec6..ff2dc510f 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -649,6 +649,79 @@ out: return retval; } +int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t keylen, uint16_t *keycnt) { + + int block_size = 512; + int allocation_size = block_size; + size_t counter = 0; + int retval = PM3_SUCCESS; + char *path; + if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic") != PM3_SUCCESS) + return PM3_EFILE; + + // t5577 == 4bytes + // mifare == 6 bytes + // iclass == 8 bytes + // default to 6 bytes. + if (keylen != 4 && keylen != 6 && keylen != 8) { + keylen = 6; + } + + // double up since its chars + keylen <<= 1; + + char line[255]; + + // allocate some space for the dictionary + *data = (uint8_t*) malloc(keylen * allocation_size * sizeof(uint8_t)); + if (*data == NULL) return PM3_EFILE; + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); + retval = PM3_EFILE; + goto out; } + + // read file + while (fgets(line, sizeof(line), f)) { + // check if we have enough space (if not allocate more) + if ((*keycnt) >= allocation_size) { + allocation_size += block_size; + *data = (uint8_t*) realloc((void*) *data, keylen * allocation_size * sizeof(uint8_t)); + if (*data == NULL) return PM3_EFILE; + } + + // add null terminator + line[keylen] = 0; + + // smaller keys than expected is skipped + if (strlen(line) < keylen) + continue; + + // The line start with # is comment, skip + if (line[0] == '#') + continue; + + if (!isxdigit(line[0])) { + PrintAndLogEx(FAILED, "file content error. '%s' must include " _BLUE_("%2d") "HEX symbols", line, keylen); + continue; + } + + uint64_t key = strtoull(line, NULL, 16); + + num_to_bytes(key, keylen >> 1, *data + counter); + (*keycnt)++; + memset(line, 0, sizeof(line)); + counter += (keylen >> 1); + } + fclose(f); + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") "keys from dictionary file " _YELLOW_("%s"), *keycnt, path); + +out: + free(path); + return retval; +} + int convertOldMfuDump(uint8_t **dump, size_t *dumplen) { if (!dump || !dumplen || *dumplen < OLD_MFU_DUMP_PREFIX_LENGTH) return 1; diff --git a/client/fileutils.h b/client/fileutils.h index 03a47112e..b2c16c282 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -149,7 +149,6 @@ int loadFileEML(const char *preferredName, void *data, size_t *datalen); */ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen); - /** * @brief Utility function to load data from a DICTIONARY textfile. This method takes a preferred name. * E.g. mfc_default_keys.dic @@ -163,6 +162,17 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_ */ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint16_t *keycnt); +/** + * @brief Utility function to load data from a DICTIONARY safely into a textfile. This method takes a preferred name. + * E.g. mfc_default_keys.dic + * + * @param preferredName + * @param data The data array to store the loaded bytes from file + * @param keylen the number of bytes a key per row is + * @return 0 for ok, 1 for failz +*/ +int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t keylen, uint16_t *keycnt); + /** * @brief Utility function to check and convert old mfu dump format to new * From 9dc3c39a95c0d06abfd5524339060c220ba56fba Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 09:45:11 +0200 Subject: [PATCH 2/9] Added additional checks to the from load dictionary returned data points. --- client/cmdhfmf.c | 6 +++--- client/cmdlft55xx.c | 11 ++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 87fa500a8..66aca7421 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1585,7 +1585,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint64_t key64 = 0; bool calibrate = true; // Attack key storage variables - uint8_t *keyBlock; + uint8_t *keyBlock = NULL; uint16_t key_cnt = 0; sector_t *e_sector; uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR; @@ -1787,9 +1787,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Load the dictionary if (strlen(filename) != 0) { int res = loadFileDICTIONARY_safe(filename, &keyBlock, 6, &key_cnt); - if (res != PM3_SUCCESS || key_cnt <= 0) { + if (res != PM3_SUCCESS || key_cnt <= 0 || keyBlock == NULL) { PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); - free(keyBlock); // free the memory, just in case an allocation happened + if (keyBlock != NULL) free(keyBlock); goto useDefaultKeys; } } else { diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 3802beab9..f256a5ee3 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -2128,17 +2128,10 @@ static int CmdT55xxChkPwds(const char *Cmd) { if (use_pwd_file) { uint16_t keycount = 0; - // TODO, a way of reallocating memory if file was larger - keyBlock = calloc(4 * 200, sizeof(uint8_t)); - if (keyBlock == NULL) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); - return PM3_ESOFT; - } - int res = loadFileDICTIONARY_safe(filename, &keyBlock, 4, &keycount); - if (res || keycount == 0) { + if (res || keycount == 0 || keyBlock == NULL) { PrintAndLogEx(WARNING, "No keys found in file"); - free(keyBlock); + if (keyBlock != NULL) free(keyBlock); return PM3_ESOFT; } From ae7b0de2c9965156c3bb692cf627c3a428792430 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 09:49:21 +0200 Subject: [PATCH 3/9] Fixed a typo and added and improved the checks for the returned data of loadFileDICTIONARY_safe. --- client/cmdlft55xx.c | 2 +- client/fileutils.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index f256a5ee3..48baea594 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -2129,7 +2129,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { uint16_t keycount = 0; int res = loadFileDICTIONARY_safe(filename, &keyBlock, 4, &keycount); - if (res || keycount == 0 || keyBlock == NULL) { + if (res || keycount <= 0 || keyBlock == NULL) { PrintAndLogEx(WARNING, "No keys found in file"); if (keyBlock != NULL) free(keyBlock); return PM3_ESOFT; diff --git a/client/fileutils.h b/client/fileutils.h index b2c16c282..1428c0d8c 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -163,7 +163,7 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint16_t *keycnt); /** - * @brief Utility function to load data from a DICTIONARY safely into a textfile. This method takes a preferred name. + * @brief Utility function to load data safely from a DICTIONARY textfile. This method takes a preferred name. * E.g. mfc_default_keys.dic * * @param preferredName From 1ce10cfea6da4d278c93203e7318f17a9d6bfaf5 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 10:09:01 +0200 Subject: [PATCH 4/9] Added additional checks (loadFileDICTIONARY_safe). --- client/cmdlft55xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 48baea594..f17c8e192 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -2129,7 +2129,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { uint16_t keycount = 0; int res = loadFileDICTIONARY_safe(filename, &keyBlock, 4, &keycount); - if (res || keycount <= 0 || keyBlock == NULL) { + if (res != PM3_SUCCESS || keycount <= 0 || keyBlock == NULL) { PrintAndLogEx(WARNING, "No keys found in file"); if (keyBlock != NULL) free(keyBlock); return PM3_ESOFT; From 7eb79732ff862ab7cb33db55259ec457c349590d Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 10:38:56 +0200 Subject: [PATCH 5/9] Added security features to the loadFileDICTIONARY_safe function (memset after a realloc). --- client/fileutils.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/fileutils.c b/client/fileutils.c index ff2dc510f..787ce0cfb 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -673,7 +673,7 @@ int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t k char line[255]; // allocate some space for the dictionary - *data = (uint8_t*) malloc(keylen * allocation_size * sizeof(uint8_t)); + *data = (uint8_t*) calloc(keylen * allocation_size, sizeof(uint8_t)); if (*data == NULL) return PM3_EFILE; FILE *f = fopen(path, "r"); @@ -688,7 +688,12 @@ int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t k if ((*keycnt) >= allocation_size) { allocation_size += block_size; *data = (uint8_t*) realloc((void*) *data, keylen * allocation_size * sizeof(uint8_t)); - if (*data == NULL) return PM3_EFILE; + if (*data == NULL) { + return PM3_EFILE; + } else { + // zero the new memeory (safety first) + memset(*data + counter, 0, block_size); + } } // add null terminator From e259b26d60b281ad5d25aaa63b34e4611c5d434b Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 10:45:45 +0200 Subject: [PATCH 6/9] Streamlined the naming conventen and types. --- client/cmdhfmf.c | 6 +++++- client/cmdlft55xx.c | 2 +- client/fileutils.c | 14 +++++++------- client/fileutils.h | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 66aca7421..11aded711 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1786,7 +1786,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Load the dictionary if (strlen(filename) != 0) { - int res = loadFileDICTIONARY_safe(filename, &keyBlock, 6, &key_cnt); + int res = loadFileDICTIONARY_safe(filename, (void**) &keyBlock, 6, &key_cnt); if (res != PM3_SUCCESS || key_cnt <= 0 || keyBlock == NULL) { PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); if (keyBlock != NULL) free(keyBlock); @@ -1806,6 +1806,10 @@ useDefaultKeys: key_cnt = ARRAYLEN(g_mifare_default_keys); } + for (int k = 0; k < key_cnt; k++) { + PrintAndLogEx(SUCCESS, "ID: %d KEY: %s", k, sprint_hex((keyBlock + (6 * k)), sizeof(key))); + } + // Use the dictionary to find sector keys on the card PrintAndLogEx(INFO, "Enter dictionary run..."); diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index f17c8e192..965a5db0b 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -2128,7 +2128,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { if (use_pwd_file) { uint16_t keycount = 0; - int res = loadFileDICTIONARY_safe(filename, &keyBlock, 4, &keycount); + int res = loadFileDICTIONARY_safe(filename, (void**) &keyBlock, 4, &keycount); if (res != PM3_SUCCESS || keycount <= 0 || keyBlock == NULL) { PrintAndLogEx(WARNING, "No keys found in file"); if (keyBlock != NULL) free(keyBlock); diff --git a/client/fileutils.c b/client/fileutils.c index 787ce0cfb..1b2073ed2 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -649,7 +649,7 @@ out: return retval; } -int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t keylen, uint16_t *keycnt) { +int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint16_t *keycnt) { int block_size = 512; int allocation_size = block_size; @@ -673,8 +673,8 @@ int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t k char line[255]; // allocate some space for the dictionary - *data = (uint8_t*) calloc(keylen * allocation_size, sizeof(uint8_t)); - if (*data == NULL) return PM3_EFILE; + *pdata = calloc(keylen * allocation_size, sizeof(uint8_t)); + if (*pdata == NULL) return PM3_EFILE; FILE *f = fopen(path, "r"); if (!f) { @@ -687,12 +687,12 @@ int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t k // check if we have enough space (if not allocate more) if ((*keycnt) >= allocation_size) { allocation_size += block_size; - *data = (uint8_t*) realloc((void*) *data, keylen * allocation_size * sizeof(uint8_t)); - if (*data == NULL) { + *pdata = realloc(*pdata, keylen * allocation_size * sizeof(uint8_t)); + if (*pdata == NULL) { return PM3_EFILE; } else { // zero the new memeory (safety first) - memset(*data + counter, 0, block_size); + memset(*pdata + counter, 0, block_size); } } @@ -714,7 +714,7 @@ int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t k uint64_t key = strtoull(line, NULL, 16); - num_to_bytes(key, keylen >> 1, *data + counter); + num_to_bytes(key, keylen >> 1, *pdata + counter); (*keycnt)++; memset(line, 0, sizeof(line)); counter += (keylen >> 1); diff --git a/client/fileutils.h b/client/fileutils.h index 1428c0d8c..d6392c6b1 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -171,7 +171,7 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u * @param keylen the number of bytes a key per row is * @return 0 for ok, 1 for failz */ -int loadFileDICTIONARY_safe(const char *preferredName, uint8_t **data, uint8_t keylen, uint16_t *keycnt); +int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint16_t *keycnt); /** * @brief Utility function to check and convert old mfu dump format to new From 7237a9638b16d89ca481febec7d197f86b9bdf02 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 11:19:54 +0200 Subject: [PATCH 7/9] Removed debugging stuff. --- client/cmdhfmf.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 11aded711..0b7727031 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1806,10 +1806,6 @@ useDefaultKeys: key_cnt = ARRAYLEN(g_mifare_default_keys); } - for (int k = 0; k < key_cnt; k++) { - PrintAndLogEx(SUCCESS, "ID: %d KEY: %s", k, sprint_hex((keyBlock + (6 * k)), sizeof(key))); - } - // Use the dictionary to find sector keys on the card PrintAndLogEx(INFO, "Enter dictionary run..."); From 2290b2bd880df92d730493955230889c3835f64c Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 11:30:30 +0200 Subject: [PATCH 8/9] Fixed the description of the loadFileDICTIONARY_safe function. --- client/fileutils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/fileutils.h b/client/fileutils.h index d6392c6b1..113cac222 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -167,7 +167,7 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u * E.g. mfc_default_keys.dic * * @param preferredName - * @param data The data array to store the loaded bytes from file + * @param pdata A pointer to a pointer (for reverencing the loaded dictionary) * @param keylen the number of bytes a key per row is * @return 0 for ok, 1 for failz */ From 5167507a831fb411844aab99ba8888b20e416209 Mon Sep 17 00:00:00 2001 From: Matthias Konrath Date: Wed, 28 Aug 2019 12:14:40 +0200 Subject: [PATCH 9/9] Fixed the loadFileDICTIONARY_safe memset alignment. Thanks to @doegox for pointing that out. --- client/fileutils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/fileutils.c b/client/fileutils.c index 1b2073ed2..500bb7ef8 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -691,8 +691,8 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key if (*pdata == NULL) { return PM3_EFILE; } else { - // zero the new memeory (safety first) - memset(*pdata + counter, 0, block_size); + // zero the new memory (safety first) + memset(*pdata + allocation_size - block_size, 0, block_size); } }