From 5bbffafe654f5fc3a7faaa9504a020a286d583d4 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 17 Sep 2024 13:01:39 +0800 Subject: [PATCH 1/6] Updated hf iclass legbrute Update legbrute to perform a double check when finding a potentially valid raw key, to ensure that is indeed the raw key and works against multiple macs (for the same epurse values). --- client/src/cmdhficlass.c | 66 ++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index fa43d8deb..d6a7855b5 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3929,14 +3929,15 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass legbrute", "This command take sniffed trace data and partial raw key and bruteforces the remaining 40 bits of the raw key.", - "hf iclass legbrute --csn 8D7BD711FEFF12E0 --epurse feffffffffffffff --macs 00000000BD478F76 --pk B4F12AADC5301225" + "hf iclass legbrute --csn 8D7BD711FEFF12E0 --epurse feffffffffffffff --macs1 1306cad9b6c24466 --macs2 f0bf905e35f97923 --pk B4F12AADC5301225" ); void *argtable[] = { arg_param_begin, arg_str1(NULL, "csn", "", "Specify CSN as 8 hex bytes"), arg_str1(NULL, "epurse", "", "Specify ePurse as 8 hex bytes"), - arg_str1(NULL, "macs", "", "MACs"), + arg_str1(NULL, "macs1", "", "MACs captured from the reader"), + arg_str1(NULL, "macs2", "", "MACs captured from the reader, different than the first set (with the same csn and epurse value)"), arg_str1(NULL, "pk", "", "Partial Key from legrec or starting key of keyblock from legbrute"), arg_param_end }; @@ -3972,7 +3973,19 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { if (macs_len > 0) { if (macs_len != 8) { - PrintAndLogEx(ERR, "MAC is incorrect length"); + PrintAndLogEx(ERR, "MAC1 is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + int macs2_len = 0; + uint8_t macs2[8] = {0}; + CLIGetHexWithReturn(ctx, 4, macs2, &macs2_len); + + if (macs2_len > 0) { + if (macs2_len != 8) { + PrintAndLogEx(ERR, "MAC2 is incorrect length"); CLIParserFree(ctx); return PM3_EINVARG; } @@ -3980,7 +3993,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { int startingkey_len = 0; uint8_t startingKey[8] = {0}; - CLIGetHexWithReturn(ctx, 4, startingKey, &startingkey_len); + CLIGetHexWithReturn(ctx, 5, startingKey, &startingkey_len); if (startingkey_len > 0) { if (startingkey_len != 8) { @@ -3995,17 +4008,25 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { uint8_t CCNR[12]; uint8_t MAC_TAG[4] = {0, 0, 0, 0}; + uint8_t CCNR2[12]; + uint8_t MAC_TAG2[4] = {0, 0, 0, 0}; // Copy CCNR and MAC_TAG memcpy(CCNR, epurse, 8); + memcpy(CCNR2, epurse, 8); memcpy(CCNR + 8, macs, 4); + memcpy(CCNR2 + 8, macs2, 4); memcpy(MAC_TAG, macs + 4, 4); + memcpy(MAC_TAG2, macs2 + 4, 4); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, 8)); PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, 8)); - PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, 8)); - PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); - PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); + PrintAndLogEx(SUCCESS, " MACS1: %s", sprint_hex(macs, 8)); + PrintAndLogEx(SUCCESS, " MACS2: %s", sprint_hex(macs2, 8)); + PrintAndLogEx(SUCCESS, " CCNR1: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); + PrintAndLogEx(SUCCESS, " CCNR2: " _GREEN_("%s"), sprint_hex(CCNR2, sizeof(CCNR2))); + PrintAndLogEx(SUCCESS, "TAG MAC1: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); + PrintAndLogEx(SUCCESS, "TAG MAC2: %s", sprint_hex(MAC_TAG2, sizeof(MAC_TAG2))); PrintAndLogEx(SUCCESS, "Starting Key: %s", sprint_hex(startingKey, 8)); uint32_t keycount = 1000000; @@ -4020,8 +4041,9 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { memcpy(lookup.mac, MAC_TAG, 4); uint32_t block_index = 0; + bool verified = false; - while (item == NULL) { + while (!verified) { for (uint32_t t = 0; t < num_threads; t++) { thread_data[t].start_index = block_index * keycount + t * keys_per_thread; thread_data[t].keycount = keys_per_thread; @@ -4075,6 +4097,30 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { // Binary search item = (iclass_prekey_t *)bsearch(&lookup, prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32); + if (item != NULL) { + //verify this against macs2 + PrintAndLogEx(WARNING, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(item->key, 8)); + //generate the macs from the key and not the other way around, so we can quickly validate it + uint8_t verification_mac[4] = {0, 0, 0, 0}; + doMAC(CCNR2, item->key, verification_mac); + PrintAndLogEx(INFO, "Usr Provided Mac2: " _GREEN_("%s"), sprint_hex(MAC_TAG2, sizeof(MAC_TAG2))); + PrintAndLogEx(INFO, "Verification Mac: " _GREEN_("%s"), sprint_hex(verification_mac, sizeof(verification_mac))); + bool check_values = true; + for (int i = 0; i < 4; i++) { + if (MAC_TAG2[i] != verification_mac[i]) { + check_values = false; + } + } + if(check_values){ + PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(item->key, 8)); + verified = true; + }else{ + PrintAndLogEx(INFO, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(item->key, 8)); + item = NULL; + } + + } + for (uint32_t t = 0; t < num_threads; t++) { free(thread_data[t].keyBlock); } @@ -4082,10 +4128,6 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { block_index++; } - if (item != NULL) { - PrintAndLogEx(SUCCESS, "Found valid RAW key " _GREEN_("%s"), sprint_hex(item->key, 8)); - } - free(prekey); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; From f70008bd0cfe085280ad18781b4c6bd8eb6669f2 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 17 Sep 2024 15:08:15 +0800 Subject: [PATCH 2/6] Optimized legbrute Removed multithreading and bottlenecks in favor of sequentially generated keys. Multithreading can/should be reimplemented but optimized for keyspaces --- client/src/cmdhficlass.c | 113 ++++++++++++++------------------------- client/src/cmdhficlass.h | 1 + 2 files changed, 41 insertions(+), 73 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index d6a7855b5..2fbef3a6d 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3923,6 +3923,22 @@ void *generate_key_blocks(void *arg) { return NULL; } +void generate_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { + uint32_t carry = index; + memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); + + for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { + uint8_t increment_value = (carry & 0x1F) << 3; // Use the first 5 bits of carry and shift left by 3 to occupy the first 5 bits + keyBlock[j] = (keyBlock[j] & 0x07) | increment_value; // Preserve last 3 bits, modify the first 5 bits + + carry >>= 5; // Shift right by 5 bits for the next byte + if (carry == 0) { + // If no more carry, break early to avoid unnecessary loops + break; + } + } +} + static int CmdHFiClassLegRecLookUp(const char *Cmd) { //Standalone Command Start @@ -3939,6 +3955,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { arg_str1(NULL, "macs1", "", "MACs captured from the reader"), arg_str1(NULL, "macs2", "", "MACs captured from the reader, different than the first set (with the same csn and epurse value)"), arg_str1(NULL, "pk", "", "Partial Key from legrec or starting key of keyblock from legbrute"), + arg_int0(NULL, "index", "", "Where to start from to retrieve the key, default 0"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4003,6 +4020,8 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { } } + uint64_t index = arg_get_int_def(ctx, 6, 0); //has to be 64 as we're bruteforcing 40 bits + CLIParserFree(ctx); //Standalone Command End @@ -4029,80 +4048,31 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { PrintAndLogEx(SUCCESS, "TAG MAC2: %s", sprint_hex(MAC_TAG2, sizeof(MAC_TAG2))); PrintAndLogEx(SUCCESS, "Starting Key: %s", sprint_hex(startingKey, 8)); - uint32_t keycount = 1000000; - uint32_t keys_per_thread = 250000; - uint32_t num_threads = keycount / keys_per_thread; - pthread_t threads[num_threads]; - ThreadData thread_data[num_threads]; - iclass_prekey_t *prekey = NULL; - iclass_prekey_t lookup; - iclass_prekey_t *item = NULL; - - memcpy(lookup.mac, MAC_TAG, 4); - - uint32_t block_index = 0; bool verified = false; + uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t generated_mac[4] = {0, 0, 0, 0}; while (!verified) { - for (uint32_t t = 0; t < num_threads; t++) { - thread_data[t].start_index = block_index * keycount + t * keys_per_thread; - thread_data[t].keycount = keys_per_thread; - thread_data[t].startingKey = startingKey; - thread_data[t].keyBlock = calloc(keys_per_thread, PICOPASS_BLOCK_SIZE); - if (thread_data[t].keyBlock == NULL) { - PrintAndLogEx(ERR, "Memory allocation failed for keyBlock in thread %d.", t); - for (uint32_t i = 0; i < t; i++) { - free(thread_data[i].keyBlock); - } - return PM3_EINVARG; + //generate the key block + generate_key_block_inverted(startingKey, index, div_key); + + //generate the relevant macs + + doMAC(CCNR, div_key, generated_mac); + bool mac_match = true; + for (int i = 0; i < 4; i++) { + if (MAC_TAG[i] != generated_mac[i]) { + mac_match = false; } - - pthread_create(&threads[t], NULL, generate_key_blocks, (void *)&thread_data[t]); } - for (uint32_t t = 0; t < num_threads; t++) { - pthread_join(threads[t], NULL); - } - - if (prekey == NULL) { - prekey = calloc(keycount, sizeof(iclass_prekey_t)); - } else { - prekey = realloc(prekey, (block_index + 1) * keycount * sizeof(iclass_prekey_t)); - } - - if (prekey == NULL) { - PrintAndLogEx(ERR, "Memory allocation failed for prekey."); - for (uint32_t t = 0; t < num_threads; t++) { - free(thread_data[t].keyBlock); - } - return PM3_EINVARG; - } - - PrintAndLogEx(INFO, "Generating diversified keys...Index: " _YELLOW_("%d")" of ~1099511",block_index); - for (uint32_t t = 0; t < num_threads; t++) { - if(!t){ - PrintAndLogEx(SUCCESS, "KeyBlock Starting Key: %s", sprint_hex(thread_data[t].keyBlock[0], 8)); - } - GenerateMacKeyFrom(csn, CCNR, true, false, (uint8_t *)thread_data[t].keyBlock, keys_per_thread, prekey + (block_index * keycount) + (t * keys_per_thread)); - } - - PrintAndLogEx(INFO, "Sorting..."); - - // Sort mac list - qsort(prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32); - - PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT"); - - // Binary search - item = (iclass_prekey_t *)bsearch(&lookup, prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32); - - if (item != NULL) { + if (mac_match) { //verify this against macs2 - PrintAndLogEx(WARNING, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(item->key, 8)); + PrintAndLogEx(WARNING, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(div_key, 8)); //generate the macs from the key and not the other way around, so we can quickly validate it uint8_t verification_mac[4] = {0, 0, 0, 0}; - doMAC(CCNR2, item->key, verification_mac); + doMAC(CCNR2, div_key, verification_mac); PrintAndLogEx(INFO, "Usr Provided Mac2: " _GREEN_("%s"), sprint_hex(MAC_TAG2, sizeof(MAC_TAG2))); PrintAndLogEx(INFO, "Verification Mac: " _GREEN_("%s"), sprint_hex(verification_mac, sizeof(verification_mac))); bool check_values = true; @@ -4112,23 +4082,20 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { } } if(check_values){ - PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(item->key, 8)); + PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(div_key, 8)); verified = true; }else{ - PrintAndLogEx(INFO, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(item->key, 8)); - item = NULL; + PrintAndLogEx(INFO, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(div_key, 8)); } } - - for (uint32_t t = 0; t < num_threads; t++) { - free(thread_data[t].keyBlock); + if(index % 1000000 == 0){ + PrintAndLogEx(INFO, "Tested: " _YELLOW_("%d")" keys", index); + PrintAndLogEx(INFO, "Last Generated Key Value: " _YELLOW_("%s"), sprint_hex(div_key, 8)); } - - block_index++; + index++; } - free(prekey); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 2a3139059..1f1604ed0 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -43,4 +43,5 @@ uint32_t picopass_elite_rng(void); uint32_t picopass_elite_lcg(void); uint8_t picopass_elite_nextByte(void); void *generate_key_blocks(void *arg); +void generate_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock); #endif From 3c258827e8cc0282163d6080f2291712063f7204 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 17 Sep 2024 15:35:23 +0800 Subject: [PATCH 3/6] Removed unused multithreading functions and updated to uint64_t --- client/src/cmdhficlass.c | 30 ++---------------------------- client/src/cmdhficlass.h | 2 +- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 2fbef3a6d..f778b563b 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3890,32 +3890,6 @@ static int CmdHFiClassRecover(uint8_t key[8]) { return resp.status; } -typedef struct { - uint32_t start_index; - uint32_t keycount; - const uint8_t *startingKey; - uint8_t (*keyBlock)[PICOPASS_BLOCK_SIZE]; -} ThreadData; - -void *generate_key_blocks(void *arg) { - ThreadData *data = (ThreadData *)arg; - uint32_t start_index = data->start_index; - uint32_t keycount = data->keycount; - const uint8_t *startingKey = data->startingKey; - uint8_t (*keyBlock)[PICOPASS_BLOCK_SIZE] = data->keyBlock; - - for (uint32_t i = 0; i < keycount; i++) { - uint32_t carry = start_index + i; - memcpy(keyBlock[i], startingKey, PICOPASS_BLOCK_SIZE); - - for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { - uint8_t increment_value = (carry & 0x1F) << 3; // Use only the first 5 bits of carry - keyBlock[i][j] = (keyBlock[i][j] & 0x07) | increment_value; // Preserve the last three bits - - carry >>= 5; // Shift right by 5 bits for the next byte - if (carry == 0) { - // If no more carry, break early to avoid unnecessary loops - break; } } } @@ -3923,8 +3897,8 @@ void *generate_key_blocks(void *arg) { return NULL; } -void generate_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { - uint32_t carry = index; +void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock) { + uint64_t carry = index; memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 1f1604ed0..1507d1d04 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -43,5 +43,5 @@ uint32_t picopass_elite_rng(void); uint32_t picopass_elite_lcg(void); uint8_t picopass_elite_nextByte(void); void *generate_key_blocks(void *arg); -void generate_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock); +void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock); #endif From f24b087c19d0c68e7530582077d2402508037a22 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 17 Sep 2024 15:46:33 +0800 Subject: [PATCH 4/6] Update cmdhficlass.c Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f778b563b..316d137a1 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3890,13 +3890,6 @@ static int CmdHFiClassRecover(uint8_t key[8]) { return resp.status; } - } - } - } - - return NULL; -} - void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock) { uint64_t carry = index; memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); @@ -4059,7 +4052,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(div_key, 8)); verified = true; }else{ - PrintAndLogEx(INFO, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(div_key, 8)); + PrintAndLogEx(INFO, _YELLOW_("Raw Key Invalid")); } } From ac0260570afd815341db4d056c3163edc1b65f5d Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 17 Sep 2024 16:35:52 +0800 Subject: [PATCH 5/6] Update cmdhficlass.c Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 316d137a1..b6bd0ffa6 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4057,7 +4057,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { } if(index % 1000000 == 0){ - PrintAndLogEx(INFO, "Tested: " _YELLOW_("%d")" keys", index); + PrintAndLogEx(INFO, "Tested: " _YELLOW_("%" PRIu64 )" million keys", index/1000000); PrintAndLogEx(INFO, "Last Generated Key Value: " _YELLOW_("%s"), sprint_hex(div_key, 8)); } index++; From 43e6d42ce9eeed9fc9b406db2cf11d786dc2bc12 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 17 Sep 2024 17:18:23 +0800 Subject: [PATCH 6/6] Update cmdhficlass.h Signed-off-by: Antiklesys --- client/src/cmdhficlass.h | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 1507d1d04..6fac5cee5 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -42,6 +42,5 @@ void picopass_elite_reset(void); uint32_t picopass_elite_rng(void); uint32_t picopass_elite_lcg(void); uint8_t picopass_elite_nextByte(void); -void *generate_key_blocks(void *arg); void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock); #endif