From 95e563438808d762994e62a9f0941a4a78fc2396 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Fri, 21 Mar 2025 18:05:14 +0100 Subject: [PATCH 1/3] Add option to use SPI flash dictionary for autopwn --- armsrc/mifarecmd.c | 29 ++++++------ client/src/cmdhfmf.c | 102 ++++++++++++++++++++++++------------------- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 3fbd807f7..2e68ebd7e 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1896,32 +1896,33 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da if (exists_in_spiffs(MF_KEYS_FILE)) { size = size_in_spiffs(MF_KEYS_FILE); } - if (size == 0) { + if ((size == 0) || (size < MF_KEY_LENGTH)) { Dbprintf("Spiffs file: %s does not exists or empty.", MF_KEYS_FILE); goto OUT; } - keyCount = size / MF_KEY_LENGTH; - - if (keyCount == 0) - goto OUT; - - // limit size of available for keys in bigbuff + // Compute how many keys can fit in bigbuff // a key is 6bytes - uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * MF_KEY_LENGTH); + uint16_t key_mem_available = MIN(BigBuf_get_size() / MF_KEY_LENGTH, keyCount + (size / MF_KEY_LENGTH)); - keyCount = key_mem_available / MF_KEY_LENGTH; - - datain = BigBuf_malloc(key_mem_available); - if (datain == NULL) + uint8_t *dictkeys = BigBuf_malloc(key_mem_available * MF_KEY_LENGTH); + if (dictkeys == NULL) goto OUT; - if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE); + // Put user and hard-coded keys first + memcpy(dictkeys, datain, keyCount * MF_KEY_LENGTH); + + // Now append the SPI flash dictionnary + if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, dictkeys + keyCount * MF_KEY_LENGTH, (key_mem_available - keyCount) * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Loaded %u keys from spiffs file: %s", key_mem_available, MF_KEYS_FILE); + } } else { Dbprintf("Spiffs file: %s cannot be read.", MF_KEYS_FILE); goto OUT; } + // Replace client provided keys + datain = dictkeys; } #endif diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 1d79fe51d..63387642a 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -2490,6 +2490,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { arg_lit0(NULL, "slow", "Slower acquisition (required by some non standard cards)"), arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), arg_lit0(NULL, "ns", "No save to file"), @@ -2542,26 +2543,27 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { bool slow = arg_get_lit(ctx, 7); bool legacy_mfchk = arg_get_lit(ctx, 8); bool verbose = arg_get_lit(ctx, 9); + bool use_flashmemory = arg_get_lit(ctx, 10); - bool no_save = arg_get_lit(ctx, 10); + bool no_save = arg_get_lit(ctx, 11); - bool m0 = arg_get_lit(ctx, 11); - bool m1 = arg_get_lit(ctx, 12); - bool m2 = arg_get_lit(ctx, 13); - bool m4 = arg_get_lit(ctx, 14); + bool m0 = arg_get_lit(ctx, 12); + bool m1 = arg_get_lit(ctx, 13); + bool m2 = arg_get_lit(ctx, 14); + bool m4 = arg_get_lit(ctx, 15); - bool in = arg_get_lit(ctx, 15); + bool in = arg_get_lit(ctx, 16); #if defined(COMPILER_HAS_SIMD_X86) - bool im = arg_get_lit(ctx, 16); - bool is = arg_get_lit(ctx, 17); - bool ia = arg_get_lit(ctx, 18); - bool i2 = arg_get_lit(ctx, 19); + bool im = arg_get_lit(ctx, 17); + bool is = arg_get_lit(ctx, 1); + bool ia = arg_get_lit(ctx, 19); + bool i2 = arg_get_lit(ctx, 20); #endif #if defined(COMPILER_HAS_SIMD_AVX512) - bool i5 = arg_get_lit(ctx, 20); + bool i5 = arg_get_lit(ctx, 21); #endif #if defined(COMPILER_HAS_SIMD_NEON) - bool ie = arg_get_lit(ctx, 16); + bool ie = arg_get_lit(ctx, 17); #endif CLIParserFree(ctx); @@ -2784,6 +2786,11 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Start the timer uint64_t t1 = msclock(); + // If we use the dictionary in flash memory, we don't want to load keys + // from hard drive dictionary as it could exceed BigBuf capacity + if (use_flashmemory) { + fnlen = 0; + } int ret = mf_load_keys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen, true); if (ret != PM3_SUCCESS) { free(e_sector); @@ -2793,7 +2800,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { int32_t res = PM3_SUCCESS; // Use the dictionary to find sector keys on the card - if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); + if (verbose) { + PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); + } if (legacy_mfchk) { PrintAndLogEx(INFO, "." NOLF); @@ -2817,41 +2826,46 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); } else { + if (use_flashmemory) { + PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); + res = mf_check_keys_fast(sector_cnt, true, true, 1, key_cnt, keyBlock, e_sector, use_flashmemory, verbose); + } else { - uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : key_cnt; - bool firstChunk = true, lastChunk = false; + uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : 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 (uint32_t i = 0; i < key_cnt; i += chunksize) { + for (uint8_t strategy = 1; strategy < 3; strategy++) { + PrintAndLogEx(INFO, "Running strategy %u", strategy); + // main keychunk loop + for (uint32_t i = 0; i < key_cnt; i += chunksize) { - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - i = key_cnt; - strategy = 3; - break; // Exit the loop - } - uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; - // last chunk? - if (size == key_cnt - i) { - lastChunk = true; - } + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + i = key_cnt; + strategy = 3; + break; // Exit the loop + } + uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; + // last chunk? + if (size == key_cnt - i) { + lastChunk = true; + } - res = mf_check_keys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); - if (firstChunk) { - firstChunk = false; - } - // all keys, aborted - if (res == PM3_SUCCESS) { - i = key_cnt; - strategy = 3; - break; // Exit the loop - } - } // end chunks of keys - firstChunk = true; - lastChunk = false; - } // end strategy + res = mf_check_keys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); + if (firstChunk) { + firstChunk = false; + } + // all keys, aborted + if (res == PM3_SUCCESS) { + i = key_cnt; + strategy = 3; + break; // Exit the loop + } + } // end chunks of keys + firstChunk = true; + lastChunk = false; + } // end strategy + } } // Analyse the dictionary attack From c5fcb97bc1e072f0324c8b2649ba7cd11cc10347 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Fri, 21 Mar 2025 18:06:48 +0100 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90332f164..4578396f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +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] +- Added option to `hf mf autopwn` to use SPI flash dictionary (@jmichelp) - Changed `trace list -t seos` - now annotate ISO7816 (@iceman1001) - Updated aid and mad json files (@iceman1001) - Changed `hf 14a apdu` - now can be interrupted and dynamically adds time (@iceman1001) From e77fbc092cd098b97b70830919479256d8cb3b18 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Fri, 21 Mar 2025 18:14:11 +0100 Subject: [PATCH 3/3] Apply same logic to `hf mf fchk` --- client/src/cmdhfmf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 63387642a..dfc56ef91 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3397,6 +3397,12 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycnt = 0; + + // If we use the dictionary in flash memory, we don't want to load keys + // from hard drive dictionary as it could exceed BigBuf capacity + if (use_flashmemory) { + fnlen = 0; + } int ret = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); if (ret != PM3_SUCCESS) { return ret; @@ -3423,7 +3429,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { } if (use_flashmemory) { PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); - mf_check_keys_fast_ex(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory, false, false, singleSectorParams); + mf_check_keys_fast_ex(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, use_flashmemory, false, false, singleSectorParams); } else { // strategies. 1= deep first on sector 0 AB, 2= width first on all sectors