From bad5c1ea611f1cf1e88a0b700496431501c75bdd Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Sat, 27 May 2023 11:57:39 +0000 Subject: [PATCH 01/11] refactor bruteforce headers and namespace --- armsrc/em4x50.c | 6 +++--- client/src/cmdlfem4x50.c | 24 ++++++++++++------------ common/bruteforce.c | 24 ++++++++++++------------ common/bruteforce.h | 34 ++++++++++++++++++++-------------- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index d43a6a15f..31ea864a1 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -641,12 +641,12 @@ static bool brute(em4x50_data_t *etd, uint32_t *pwd) { int generator_ret = 0; int cnt = 0; - bf_generator_init(&ctx, etd->bruteforce_mode); + bf_generator_init(&ctx, etd->bruteforce_mode, BF_KEY_SIZE_32); - if (etd->bruteforce_mode == BRUTEFORCE_MODE_CHARSET) + if (etd->bruteforce_mode == BF_MODE_CHARSET) bf_generator_set_charset(&ctx, etd->bruteforce_charset); - while ((generator_ret = bf_generate32(&ctx)) == GENERATOR_NEXT) { + while ((generator_ret = bf_generate32(&ctx)) == BF_GENERATOR_NEXT) { *pwd = ctx.current_key32; WDT_HIT(); diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 017f1c121..a45f8a11c 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -377,15 +377,15 @@ int CmdEM4x50Brute(const char *Cmd) { PrintAndLogEx(INFO, "Chosen mode: %s", mode); if (strcmp(mode, "range") == 0) { - etd.bruteforce_mode = BRUTEFORCE_MODE_RANGE; + etd.bruteforce_mode = BF_MODE_RANGE; } else if (strcmp(mode, "charset") == 0) { - etd.bruteforce_mode = BRUTEFORCE_MODE_CHARSET; + etd.bruteforce_mode = BF_MODE_CHARSET; } else { PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode); return PM3_EINVARG; } - if (etd.bruteforce_mode == BRUTEFORCE_MODE_RANGE) { + if (etd.bruteforce_mode == BF_MODE_RANGE) { int begin_len = 0; uint8_t begin[4] = {0x0}; CLIGetHexWithReturn(ctx, 2, begin, &begin_len); @@ -406,14 +406,14 @@ int CmdEM4x50Brute(const char *Cmd) { etd.password1 = BYTES2UINT32_BE(begin); etd.password2 = BYTES2UINT32_BE(end); - } else if (etd.bruteforce_mode == BRUTEFORCE_MODE_CHARSET) { + } else if (etd.bruteforce_mode == BF_MODE_CHARSET) { bool enable_digits = arg_get_lit(ctx, 4); bool enable_uppercase = arg_get_lit(ctx, 5); if (enable_digits) - etd.bruteforce_charset |= CHARSET_DIGITS; + etd.bruteforce_charset |= BF_CHARSET_DIGITS; if (enable_uppercase) - etd.bruteforce_charset |= CHARSET_UPPERCASE; + etd.bruteforce_charset |= BF_CHARSET_UPPERCASE; if (etd.bruteforce_charset == 0) { PrintAndLogEx(FAILED, "Please enable at least one charset when using charset bruteforce mode."); @@ -432,21 +432,21 @@ int CmdEM4x50Brute(const char *Cmd) { const int speed = 27; int no_iter = 0; - if (etd.bruteforce_mode == BRUTEFORCE_MODE_RANGE) { + if (etd.bruteforce_mode == BF_MODE_RANGE) { no_iter = etd.password2 - etd.password1 + 1; PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]" , no_iter , etd.password1 , etd.password2 ); - } else if (etd.bruteforce_mode == BRUTEFORCE_MODE_CHARSET) { + } else if (etd.bruteforce_mode == BF_MODE_CHARSET) { unsigned int digits = 0; - if (etd.bruteforce_charset & CHARSET_DIGITS) - digits += CHARSET_DIGITS_SIZE; + if (etd.bruteforce_charset & BF_CHARSET_DIGITS) + digits += BF_CHARSET_DIGITS_SIZE; - if (etd.bruteforce_charset & CHARSET_UPPERCASE) - digits += CHARSET_UPPERCASE_SIZE; + if (etd.bruteforce_charset & BF_CHARSET_UPPERCASE) + digits += BF_CHARSET_UPPERCASE_SIZE; no_iter = pow(digits, 4); } diff --git a/common/bruteforce.c b/common/bruteforce.c index 891796690..7a4aa1ac4 100644 --- a/common/bruteforce.c +++ b/common/bruteforce.c @@ -28,22 +28,22 @@ uint8_t charset_uppercase[] = { 'X', 'Y', 'Z' }; -void bf_generator_init(generator_context_t *ctx, uint8_t mode) { +void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size) { memset(ctx, 0, sizeof(generator_context_t)); ctx->mode = mode; } int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets) { - if (ctx->mode != BRUTEFORCE_MODE_CHARSET) { + if (ctx->mode != BF_MODE_CHARSET) { return -1; } - if (charsets & CHARSET_DIGITS) { + if (charsets & BF_CHARSET_DIGITS) { memcpy(ctx->charset, charset_digits, sizeof(charset_digits)); ctx->charset_length += sizeof(charset_digits); } - if (charsets & CHARSET_UPPERCASE) { + if (charsets & BF_CHARSET_UPPERCASE) { memcpy(ctx->charset + ctx->charset_length, charset_uppercase, sizeof(charset_uppercase)); ctx->charset_length += sizeof(charset_uppercase); } @@ -54,19 +54,19 @@ int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets) { int bf_generate32(generator_context_t *ctx) { switch (ctx->mode) { - case BRUTEFORCE_MODE_RANGE: + case BF_MODE_RANGE: return _bf_generate_mode_range32(ctx); - case BRUTEFORCE_MODE_CHARSET: + case BF_MODE_CHARSET: return _bf_generate_mode_charset32(ctx); } - return GENERATOR_ERROR; + return BF_GENERATOR_ERROR; } int _bf_generate_mode_range32(generator_context_t *ctx) { if (ctx->current_key32 >= ctx->range_high) { - return GENERATOR_END; + return BF_GENERATOR_END; } // we use flag1 as indicator if value of range_low was already emitted @@ -74,17 +74,17 @@ int _bf_generate_mode_range32(generator_context_t *ctx) { if (ctx->current_key32 <= ctx->range_low && ctx->flag1 == false) { ctx->current_key32 = ctx->range_low; ctx->pos[0] = true; - return GENERATOR_NEXT; + return BF_GENERATOR_NEXT; } ctx->current_key32++; - return GENERATOR_NEXT; + return BF_GENERATOR_NEXT; } int _bf_generate_mode_charset32(generator_context_t *ctx) { if (ctx->flag1) - return GENERATOR_END; + return BF_GENERATOR_END; ctx->current_key32 = ctx->charset[ctx->pos[0]] << 24 | ctx->charset[ctx->pos[1]] << 16 | ctx->charset[ctx->pos[2]] << 8 | ctx->charset[ctx->pos[3]]; @@ -94,7 +94,7 @@ int _bf_generate_mode_charset32(generator_context_t *ctx) { // set flag1 to emit value last time and end generation ctx->flag1 = true; - return GENERATOR_NEXT; + return BF_GENERATOR_NEXT; } // increments values in array with carryover using modulo limit for each byte diff --git a/common/bruteforce.h b/common/bruteforce.h index 91e01172d..beb44775e 100644 --- a/common/bruteforce.h +++ b/common/bruteforce.h @@ -22,28 +22,33 @@ #include "common.h" typedef uint8_t bruteforce_mode_t; + +#define BF_KEY_SIZE_32 4 +#define BF_KEY_SIZE_48 6 + // bruteforcing all keys sequentially between X and Y -#define BRUTEFORCE_MODE_RANGE 1 +#define BF_MODE_RANGE 1 // try keys based on limited charset/passphrases // some payment systems use user-provided passphrase as system key -#define BRUTEFORCE_MODE_CHARSET 2 +#define BF_MODE_CHARSET 2 // "smart" mode - try some predictable patterns -#define BRUTEFORCE_MODE_SMART 3 +#define BF_MODE_SMART 3 typedef uint8_t bruteforce_charset_t; // bit flags - can be used together using logical OR -#define CHARSET_DIGITS 1 -#define CHARSET_UPPERCASE 2 +#define BF_CHARSET_DIGITS 1 +#define BF_CHARSET_UPPERCASE 2 -#define GENERATOR_END 0 -#define GENERATOR_NEXT 1 -#define GENERATOR_ERROR 2 +#define BF_GENERATOR_END 0 +#define BF_GENERATOR_NEXT 1 +#define BF_GENERATOR_ERROR 2 + +#define BF_CHARSET_DIGITS_SIZE 10 +#define BF_CHARSET_UPPERCASE_SIZE 25 -#define CHARSET_DIGITS_SIZE 10 -#define CHARSET_UPPERCASE_SIZE 25 extern uint8_t charset_digits[]; extern uint8_t charset_uppercase[]; @@ -53,12 +58,13 @@ typedef struct { // position of each of 4 bytes in 32 bit key in charset mode // add more bytes to support larger keys // pos[0] is most significant byte - all maths avoid relying on little/big endian memory layout - uint8_t pos[4]; + uint8_t pos[6]; // max supported key is now 48 bit + uint8_t key_length; // bytes uint32_t current_key32; uint8_t mode; uint8_t charset[ - CHARSET_DIGITS_SIZE - + CHARSET_UPPERCASE_SIZE + BF_CHARSET_DIGITS_SIZE + + BF_CHARSET_UPPERCASE_SIZE ]; uint8_t charset_length; @@ -69,7 +75,7 @@ typedef struct { } generator_context_t; -void bf_generator_init(generator_context_t *ctx, uint8_t mode); +void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size); int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets); int bf_generate32(generator_context_t *ctx); int _bf_generate_mode_range32(generator_context_t *ctx); From ec31ec21418f0a71e682bc1d1861cd991b961e1a Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Thu, 1 Jun 2023 20:13:38 +0000 Subject: [PATCH 02/11] make bruteforce code key length independent --- armsrc/em4x50.c | 4 ++-- common/bruteforce.c | 41 ++++++++++++++++++++++++++++------------- common/bruteforce.h | 16 ++++++++++------ 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 31ea864a1..a05fab35f 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -646,8 +646,8 @@ static bool brute(em4x50_data_t *etd, uint32_t *pwd) { if (etd->bruteforce_mode == BF_MODE_CHARSET) bf_generator_set_charset(&ctx, etd->bruteforce_charset); - while ((generator_ret = bf_generate32(&ctx)) == BF_GENERATOR_NEXT) { - *pwd = ctx.current_key32; + while ((generator_ret = bf_generate(&ctx)) == BF_GENERATOR_NEXT) { + *pwd = bf_get_key32(&ctx); WDT_HIT(); diff --git a/common/bruteforce.c b/common/bruteforce.c index 7a4aa1ac4..00d624244 100644 --- a/common/bruteforce.c +++ b/common/bruteforce.c @@ -51,46 +51,56 @@ int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets) { return 0; } -int bf_generate32(generator_context_t *ctx) { +int bf_generate(generator_context_t *ctx) { switch (ctx->mode) { case BF_MODE_RANGE: - return _bf_generate_mode_range32(ctx); + return _bf_generate_mode_range(ctx); case BF_MODE_CHARSET: - return _bf_generate_mode_charset32(ctx); + return _bf_generate_mode_charset(ctx); } return BF_GENERATOR_ERROR; } -int _bf_generate_mode_range32(generator_context_t *ctx) { +int _bf_generate_mode_range(generator_context_t *ctx) { - if (ctx->current_key32 >= ctx->range_high) { + if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) + return BF_GENERATOR_ERROR; + + if (ctx->current_key >= ctx->range_high) { return BF_GENERATOR_END; } // we use flag1 as indicator if value of range_low was already emitted // so the range generated is - if (ctx->current_key32 <= ctx->range_low && ctx->flag1 == false) { - ctx->current_key32 = ctx->range_low; - ctx->pos[0] = true; + if (ctx->current_key <= ctx->range_low && ctx->flag1 == false) { + ctx->current_key = ctx->range_low; + ctx->flag1 = true; return BF_GENERATOR_NEXT; } - ctx->current_key32++; + ctx->current_key++; return BF_GENERATOR_NEXT; } -int _bf_generate_mode_charset32(generator_context_t *ctx) { +int _bf_generate_mode_charset(generator_context_t *ctx) { + + if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) + return BF_GENERATOR_ERROR; if (ctx->flag1) return BF_GENERATOR_END; - ctx->current_key32 = ctx->charset[ctx->pos[0]] << 24 | ctx->charset[ctx->pos[1]] << 16 | - ctx->charset[ctx->pos[2]] << 8 | ctx->charset[ctx->pos[3]]; + uint8_t key_byte = 0; + + for (key_byte = 0; key_byte < ctx->key_length;key_byte++){ + ctx->current_key |= ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte) - 1 * 8); + } - if (bf_array_increment(ctx->pos, 4, ctx->charset_length) == -1) + + if (bf_array_increment(ctx->pos, ctx->key_length, ctx->charset_length) == -1) // set flag1 to emit value last time and end generation ctx->flag1 = true; @@ -127,3 +137,8 @@ int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) { return 0; } + +// get current key casted to 32 bit +uint32_t bf_get_key32(generator_context_t *ctx){ + return ctx->current_key & 0xFFFFFFFF; +} \ No newline at end of file diff --git a/common/bruteforce.h b/common/bruteforce.h index beb44775e..6a482a62b 100644 --- a/common/bruteforce.h +++ b/common/bruteforce.h @@ -55,12 +55,13 @@ extern uint8_t charset_uppercase[]; // structure to hold key generator temporary data typedef struct { - // position of each of 4 bytes in 32 bit key in charset mode + // position of each of bytes in charset mode - used to iterate over alphabets // add more bytes to support larger keys // pos[0] is most significant byte - all maths avoid relying on little/big endian memory layout uint8_t pos[6]; // max supported key is now 48 bit + uint8_t key_length; // bytes - uint32_t current_key32; + uint64_t current_key; // Use 64 bit and truncate when needed. uint8_t mode; uint8_t charset[ BF_CHARSET_DIGITS_SIZE @@ -77,9 +78,12 @@ typedef struct { void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size); int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets); -int bf_generate32(generator_context_t *ctx); -int _bf_generate_mode_range32(generator_context_t *ctx); -int _bf_generate_mode_charset32(generator_context_t *ctx); -int _bf_generate_mode_smart32(generator_context_t *ctx); +int bf_generate(generator_context_t *ctx); +int _bf_generate_mode_range(generator_context_t *ctx); +int _bf_generate_mode_charset(generator_context_t *ctx); +int _bf_generate_mode_smart(generator_context_t *ctx); int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo); +uint32_t bf_get_key32(generator_context_t *ctx); +uint32_t bf_get_key48(generator_context_t *ctx); + #endif // BRUTEFORCE_H__ From 71c12c18281dbacb21c485afd462b7d06ec310fb Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Fri, 16 Jun 2023 20:48:57 +0000 Subject: [PATCH 03/11] smart generators core settings --- common/bruteforce.c | 98 ++++++++++++++++++++++++++++++++------------- common/bruteforce.h | 24 ++++++++--- 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/common/bruteforce.c b/common/bruteforce.c index 00d624244..21c7a3daa 100644 --- a/common/bruteforce.c +++ b/common/bruteforce.c @@ -28,6 +28,13 @@ uint8_t charset_uppercase[] = { 'X', 'Y', 'Z' }; +smart_generator_t *smart_generators[] = { + smart_generator_test1, + smart_generator_test2, + NULL +}; + + void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size) { memset(ctx, 0, sizeof(generator_context_t)); ctx->mode = mode; @@ -63,6 +70,48 @@ int bf_generate(generator_context_t *ctx) { return BF_GENERATOR_ERROR; } + +// increments values in array with carryover using modulo limit for each byte +// this is used to iterate each byte in key over charset table +// returns -1 if incrementing reaches its end +int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) { + + uint8_t prev_value; + + // check if we reached max value already + uint8_t i; + for (i = 0; i < data_len; i++) + if (data[i] < modulo - 1) + break; + + if (i == data_len) + return -1; + + for (uint8_t pos = data_len - 1;; pos--) { + prev_value = ++data[pos]; + data[pos] = data[pos] % modulo; + if (prev_value == data[pos]) + return 0; + else if (pos == 0) { + // we cannot carryover to next byte + // with the max value check in place before, we should not reach this place + return -1; + } + } + + return 0; +} + +// get current key casted to 32 bit +uint32_t bf_get_key32(generator_context_t *ctx){ + return ctx->current_key & 0xFFFFFFFF; +} + +// get current key casted to 48 bit +uint64_t bf_get_key48(generator_context_t *ctx){ + return ctx->current_key & 0xFFFFFFFFFFFF; +} + int _bf_generate_mode_range(generator_context_t *ctx) { if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) @@ -107,38 +156,33 @@ int _bf_generate_mode_charset(generator_context_t *ctx) { return BF_GENERATOR_NEXT; } -// increments values in array with carryover using modulo limit for each byte -// this is used to iterate each byte in key over charset table -// returns -1 if incrementing reaches its end -int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) { +int bf_generate_mode_smart(generator_context_t *ctx){ - uint8_t prev_value; + int ret; - // check if we reached max value already - uint8_t i; - for (i = 0; i < data_len; i++) - if (data[i] < modulo - 1) - break; + while(1){ - if (i == data_len) - return -1; + if (smart_generators[ctx->smart_mode_stage] == NULL) + return BF_GENERATOR_END; - for (uint8_t pos = data_len - 1;; pos--) { - prev_value = ++data[pos]; - data[pos] = data[pos] % modulo; - if (prev_value == data[pos]) - return 0; - else if (pos == 0) { - // we cannot carryover to next byte - // with the max value check in place before, we should not reach this place - return -1; + ret = smart_generators[ctx->smart_mode_stage](ctx); + + switch (ret){ + case BF_GENERATOR_NEXT: + return ret; + case BF_GENERATOR_ERROR: + return ret; + case BF_GENERATOR_END: + ctx->smart_mode_stage++; + continue; } } - - return 0; } -// get current key casted to 32 bit -uint32_t bf_get_key32(generator_context_t *ctx){ - return ctx->current_key & 0xFFFFFFFF; -} \ No newline at end of file + +int smart_generator_test1(generator_context_t *ctx){ + return 0; +} +int smart_generator_test2(generator_context_t *ctx){ + return 0; +} diff --git a/common/bruteforce.h b/common/bruteforce.h index 6a482a62b..70dd8a535 100644 --- a/common/bruteforce.h +++ b/common/bruteforce.h @@ -21,8 +21,6 @@ #include "common.h" -typedef uint8_t bruteforce_mode_t; - #define BF_KEY_SIZE_32 4 #define BF_KEY_SIZE_48 6 @@ -37,7 +35,6 @@ typedef uint8_t bruteforce_mode_t; #define BF_MODE_SMART 3 -typedef uint8_t bruteforce_charset_t; // bit flags - can be used together using logical OR #define BF_CHARSET_DIGITS 1 #define BF_CHARSET_UPPERCASE 2 @@ -49,10 +46,12 @@ typedef uint8_t bruteforce_charset_t; #define BF_CHARSET_DIGITS_SIZE 10 #define BF_CHARSET_UPPERCASE_SIZE 25 - extern uint8_t charset_digits[]; extern uint8_t charset_uppercase[]; +typedef uint8_t bruteforce_charset_t; +typedef uint8_t bruteforce_mode_t; + // structure to hold key generator temporary data typedef struct { // position of each of bytes in charset mode - used to iterate over alphabets @@ -71,12 +70,17 @@ typedef struct { uint32_t range_low; uint32_t range_high; + uint16_t smart_mode_stage; // flags to use internally by generators as they wish bool flag1, flag2, flag3; + // counters to use internally by generators as they wish + uint32_t counter1, counter2; } generator_context_t; + void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size); +void bf_generator_clear(generator_context_t *ctx); // clear flags and counters used by generators int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets); int bf_generate(generator_context_t *ctx); int _bf_generate_mode_range(generator_context_t *ctx); @@ -84,6 +88,16 @@ int _bf_generate_mode_charset(generator_context_t *ctx); int _bf_generate_mode_smart(generator_context_t *ctx); int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo); uint32_t bf_get_key32(generator_context_t *ctx); -uint32_t bf_get_key48(generator_context_t *ctx); +uint64_t bf_get_key48(generator_context_t *ctx); + +// smart mode +typedef int (smart_generator_t)(generator_context_t *ctx); + +int bf_generate_mode_smart(generator_context_t *ctx); + +int smart_generator_test1(generator_context_t *ctx); +int smart_generator_test2(generator_context_t *ctx); + +extern smart_generator_t *smart_generators[]; // array of smart cracking functions #endif // BRUTEFORCE_H__ From 4003ad72fe9cfab3db019dbb6fe24bb9900d190a Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Mon, 16 Oct 2023 19:19:30 +0000 Subject: [PATCH 04/11] update --- armsrc/em4x50.c | 6 ++++- client/src/cmdlfem4x50.c | 13 +++++++--- common/bruteforce.c | 55 +++++++++++++++++++++++++++++----------- common/bruteforce.h | 2 +- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index a05fab35f..84cae8aed 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -643,8 +643,12 @@ static bool brute(em4x50_data_t *etd, uint32_t *pwd) { bf_generator_init(&ctx, etd->bruteforce_mode, BF_KEY_SIZE_32); - if (etd->bruteforce_mode == BF_MODE_CHARSET) + if (etd->bruteforce_mode == BF_MODE_CHARSET){ bf_generator_set_charset(&ctx, etd->bruteforce_charset); + } else if (etd->bruteforce_mode == BF_MODE_RANGE){ + ctx.range_low = etd->password1; + ctx.range_high = etd->password2; + } while ((generator_ret = bf_generate(&ctx)) == BF_GENERATOR_NEXT) { *pwd = bf_get_key32(&ctx); diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index a45f8a11c..b210ec73c 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -354,11 +354,12 @@ int CmdEM4x50Brute(const char *Cmd) { "lf em 4x50 brute --mode range --begin 12330000 --end 12340000 -> tries pwds from 0x12330000 to 0x12340000\n" "lf em 4x50 brute --mode charset --digits --uppercase -> tries all combinations of ASCII codes for digits and uppercase letters\n" + "lf em 4x50 brute --mode smart -> enable 'smart' pattern key cracking\n" ); void *argtable[] = { arg_param_begin, - arg_str1(NULL, "mode", "", "Bruteforce mode (range|charset)"), + arg_str1(NULL, "mode", "", "Bruteforce mode (range|charset|smart)"), arg_str0(NULL, "begin", "", "Range mode - start of the key range"), arg_str0(NULL, "end", "", "Range mode - end of the key range"), arg_lit0(NULL, "digits", "Charset mode - include ASCII codes for digits"), @@ -380,7 +381,10 @@ int CmdEM4x50Brute(const char *Cmd) { etd.bruteforce_mode = BF_MODE_RANGE; } else if (strcmp(mode, "charset") == 0) { etd.bruteforce_mode = BF_MODE_CHARSET; - } else { + } else if (strcmp(mode, "smart") == 0){ + etd.bruteforce_mode = BF_MODE_SMART; + } else + { PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode); return PM3_EINVARG; } @@ -458,7 +462,10 @@ int CmdEM4x50Brute(const char *Cmd) { dur_s -= dur_h * 3600 + dur_m * 60; - PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s); + if ( no_iter > 0 ) + PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s); + else + PrintAndLogEx(INFO, "Estimated duration: unknown"); // start clearCommandBuffer(); diff --git a/common/bruteforce.c b/common/bruteforce.c index 21c7a3daa..4ac8834c8 100644 --- a/common/bruteforce.c +++ b/common/bruteforce.c @@ -29,15 +29,15 @@ uint8_t charset_uppercase[] = { }; smart_generator_t *smart_generators[] = { - smart_generator_test1, - smart_generator_test2, + smart_generator_byte_repeat, NULL }; -void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size) { +void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_length) { memset(ctx, 0, sizeof(generator_context_t)); ctx->mode = mode; + ctx->key_length = key_length; } int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets) { @@ -65,7 +65,10 @@ int bf_generate(generator_context_t *ctx) { return _bf_generate_mode_range(ctx); case BF_MODE_CHARSET: return _bf_generate_mode_charset(ctx); - } + + case BF_MODE_SMART: + return _bf_generate_mode_smart(ctx); + } return BF_GENERATOR_ERROR; } @@ -112,6 +115,14 @@ uint64_t bf_get_key48(generator_context_t *ctx){ return ctx->current_key & 0xFFFFFFFFFFFF; } +void bf_generator_clear(generator_context_t *ctx){ + ctx->flag1 = 0; + ctx->flag2 = 0; + ctx->flag3 = 0; + ctx->counter1 = 0; + ctx->counter2 = 0; +} + int _bf_generate_mode_range(generator_context_t *ctx) { if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) @@ -135,35 +146,35 @@ int _bf_generate_mode_range(generator_context_t *ctx) { int _bf_generate_mode_charset(generator_context_t *ctx) { - if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) + if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48){ return BF_GENERATOR_ERROR; + } if (ctx->flag1) return BF_GENERATOR_END; uint8_t key_byte = 0; + ctx->current_key = 0; - for (key_byte = 0; key_byte < ctx->key_length;key_byte++){ - ctx->current_key |= ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte) - 1 * 8); + for (key_byte = 0; key_byte < ctx->key_length; key_byte++) + { + ctx->current_key |= (uint64_t) ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte - 1) * 8); } - - if (bf_array_increment(ctx->pos, ctx->key_length, ctx->charset_length) == -1) - // set flag1 to emit value last time and end generation + // set flag1 to emit value last time and end generation on next call ctx->flag1 = true; return BF_GENERATOR_NEXT; } -int bf_generate_mode_smart(generator_context_t *ctx){ +int _bf_generate_mode_smart(generator_context_t *ctx){ int ret; while(1){ - if (smart_generators[ctx->smart_mode_stage] == NULL) - return BF_GENERATOR_END; + return BF_GENERATOR_END; ret = smart_generators[ctx->smart_mode_stage](ctx); @@ -174,14 +185,28 @@ int bf_generate_mode_smart(generator_context_t *ctx){ return ret; case BF_GENERATOR_END: ctx->smart_mode_stage++; + bf_generator_clear(ctx); continue; } } } -int smart_generator_test1(generator_context_t *ctx){ - return 0; +int smart_generator_byte_repeat(generator_context_t *ctx){ + // key consists of repeated single byte + uint32_t current_byte = ctx->counter1; + + if (current_byte > 0xFF) + return BF_GENERATOR_END; + + ctx->current_key = 0; + + for (uint8_t key_byte = 0; key_byte < ctx->key_length;key_byte++){ + ctx->current_key |= (uint64_t)current_byte << ((ctx->key_length - key_byte - 1) * 8); + } + + ctx->counter1++; + return BF_GENERATOR_NEXT; } int smart_generator_test2(generator_context_t *ctx){ return 0; diff --git a/common/bruteforce.h b/common/bruteforce.h index 70dd8a535..16258465e 100644 --- a/common/bruteforce.h +++ b/common/bruteforce.h @@ -95,7 +95,7 @@ typedef int (smart_generator_t)(generator_context_t *ctx); int bf_generate_mode_smart(generator_context_t *ctx); -int smart_generator_test1(generator_context_t *ctx); +int smart_generator_byte_repeat(generator_context_t *ctx); int smart_generator_test2(generator_context_t *ctx); extern smart_generator_t *smart_generators[]; // array of smart cracking functions From dd859a2061faf1faf960e09409a46c54ec6ca271 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Mon, 15 Jan 2024 20:25:02 +0000 Subject: [PATCH 05/11] add smart bruteforce mode to MF Classic and EM4x50 --- armsrc/em4x50.c | 4 +- client/Makefile | 1 + client/dictionaries/mfc_default_keys.dic | 36 ++++ client/src/cmdhfmf.c | 212 +++++++++++++++++++++++ client/src/cmdlfem4x50.c | 7 +- client/src/pm3line_vocabulary.h | 3 +- common/bruteforce.c | 83 +++++++-- common/bruteforce.h | 3 +- doc/commands.md | 11 +- 9 files changed, 323 insertions(+), 37 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 6b90b079b..ff68b724b 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -643,9 +643,9 @@ static bool brute(const em4x50_data_t *etd, uint32_t *pwd) { bf_generator_init(&ctx, etd->bruteforce_mode, BF_KEY_SIZE_32); - if (etd->bruteforce_mode == BF_MODE_CHARSET){ + if (etd->bruteforce_mode == BF_MODE_CHARSET) { bf_generator_set_charset(&ctx, etd->bruteforce_charset); - } else if (etd->bruteforce_mode == BF_MODE_RANGE){ + } else if (etd->bruteforce_mode == BF_MODE_RANGE) { ctx.range_low = etd->password1; ctx.range_high = etd->password2; } diff --git a/client/Makefile b/client/Makefile index adbd2c0ce..8b36f85f6 100644 --- a/client/Makefile +++ b/client/Makefile @@ -749,6 +749,7 @@ SRCS = mifare/aiddesfire.c \ # common SRCS += bucketsort.c \ + bruteforce.c \ cardhelper.c \ crapto1/crapto1.c \ crapto1/crypto1.c \ diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 6616a92ee..795d8fe0a 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2337,3 +2337,39 @@ EA0CA627FD06 # Hotel key CE0F4F15E909 D60DE9436219 + +# ATM Arena de Girona, spanish transport card + +A00000000000 +A01000000000 +A02000000000 +A03000000000 +A04000000000 +A05000000000 +A06000000000 +A07000000000 +A08000000000 +A09000000000 +A10000000000 +A11000000000 +A12000000000 +A13000000000 +A14000000000 +A15000000000 + +B00000000000 +B01000000000 +B02000000000 +B03000000000 +B04000000000 +B05000000000 +B06000000000 +B07000000000 +B08000000000 +B09000000000 +B10000000000 +B11000000000 +B12000000000 +B13000000000 +B14000000000 +B15000000000 diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 8a158f3b8..6673491b5 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -16,6 +16,7 @@ // High frequency MIFARE commands //----------------------------------------------------------------------------- +#include "bruteforce.h" #include "cmdhfmf.h" #include #include "cmdparser.h" // command_t @@ -3369,6 +3370,216 @@ out: return PM3_SUCCESS; } +static int CmdHF14AMfSmartBrute(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf brute", + "This is a smart bruteforce, exploiting common patterns, bugs and bad designs in key generators.", + "hf mf brute --mini --> Key recovery against MIFARE Mini\n" + "hf mf brute --1k --> Key recovery against MIFARE Classic 1k\n" + "hf mf brute --2k --> Key recovery against MIFARE 2k\n" + "hf mf brute --4k --> Key recovery against MIFARE 4k\n" + "hf mf brute --1k --emu --> Target 1K, write keys to emulator memory\n" + "hf mf brute --1k --dump --> Target 1K, write keys to file\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), + arg_lit0(NULL, "dump", "Dump found keys to binary file"), + arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + + bool m0 = arg_get_lit(ctx, 2); + bool m1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); + + bool transferToEml = arg_get_lit(ctx, 6); + bool createDumpFile = arg_get_lit(ctx, 7); + bool use_flashmemory = arg_get_lit(ctx, 8); + + CLIParserFree(ctx); + + //validations + + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; + if (m0) { + sectorsCnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + sectorsCnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + sectorsCnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + sectorsCnt = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + uint32_t chunksize = 100 > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : 100; + uint8_t *keyBlock = calloc(MIFARE_KEY_SIZE, chunksize); + + if (keyBlock == NULL) + return PM3_EMALLOC; + + // create/initialize key storage structure + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, sectorsCnt) != PM3_SUCCESS) { + free(keyBlock); + return PM3_EMALLOC; + } + + // initialize bruteforce engine + generator_context_t bctx; + bf_generator_init(&bctx, BF_MODE_SMART, BF_KEY_SIZE_48); + + int i = 0, ret; + int smart_mode_stage = -1; + uint64_t generator_key; + + // time + uint64_t t0 = msclock(); + uint64_t t1 = msclock(); + uint64_t keys_checked = 0; + uint64_t total_keys_checked = 0; + + uint32_t keycnt = 0; + bool firstChunk = true, lastChunk = false; + + while (!lastChunk) { + keycnt = 0; + + // generate block of keys from generator + memset(keyBlock, 0, MIFARE_KEY_SIZE * chunksize); + for (i = 0; i < chunksize; i++) { + ret = bf_generate(&bctx); + if (ret == BF_GENERATOR_ERROR) { + PrintAndLogEx(ERR, "Internal bruteforce generator error"); + free(keyBlock); + return PM3_EFAILED; + } else if (ret == BF_GENERATOR_END) { + lastChunk = true; + break; + } else if (ret == BF_GENERATOR_NEXT) { + generator_key = bf_get_key48(&bctx); + num_to_bytes(generator_key, MIFARE_KEY_SIZE, keyBlock + (i * MIFARE_KEY_SIZE)); + keycnt++; + + if (smart_mode_stage != bctx.smart_mode_stage) { + smart_mode_stage = bctx.smart_mode_stage; + PrintAndLogEx(INFO, "Running bruteforce stage %d", smart_mode_stage); + + if (msclock() - t1 > 0 && keys_checked > 0) { + PrintAndLogEx(INFO, "Current cracking speed (keys/s): %d", + keys_checked / ((msclock() - t1) / 1000)); + + t1 = msclock(); + keys_checked = 0; + } + } + } + } + + int strategy = 2; // width first on all sectors + ret = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, keycnt, keyBlock, e_sector, false, false); + + keys_checked += keycnt; + total_keys_checked += keycnt; + + if (firstChunk) + firstChunk = false; + + if (ret == PM3_SUCCESS || ret == 2) + goto out; + + } + +out: + PrintAndLogEx(INFO, "Time in brute mode: " _YELLOW_("%.1fs") "\n", (float)((msclock() - t0) / 1000.0)); + PrintAndLogEx(INFO, "Total keys checked: " _YELLOW_("%d") "\n", total_keys_checked); + // check.. + uint8_t found_keys = 0; + for (i = 0; i < sectorsCnt; ++i) { + + if (e_sector[i].foundKey[0]) + found_keys++; + + if (e_sector[i].foundKey[1]) + found_keys++; + } + + if (found_keys == 0) { + PrintAndLogEx(WARNING, "No keys found"); + } else { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); + + printKeyTable(sectorsCnt, e_sector); + + if (use_flashmemory && found_keys == (sectorsCnt << 1)) { + PrintAndLogEx(SUCCESS, "Card dumped as well. run " _YELLOW_("`%s %c`"), + "hf mf esave", + GetFormatFromSector(sectorsCnt) + ); + } + + if (transferToEml) { + // fast push mode + g_conn.block_after_ACK = true; + uint8_t block[MFBLOCK_SIZE] = {0x00}; + for (i = 0; i < sectorsCnt; ++i) { + uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; + mfEmlGetMem(block, b, 1); + + if (e_sector[i].foundKey[0]) + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); + + if (e_sector[i].foundKey[1]) + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); + + if (i == sectorsCnt - 1) { + // Disable fast mode on last packet + g_conn.block_after_ACK = false; + } + mfEmlSetMem(block, b, 1); + } + PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); + + if (found_keys == (sectorsCnt << 1)) { + FastDumpWithEcFill(sectorsCnt); + } + } + + if (createDumpFile) { + + char *fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (createMfcKeyDump(fptr, sectorsCnt, e_sector) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to save keys to file"); + } + free(fptr); + } + } + + free(keyBlock); + free(e_sector); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static int CmdHF14AMfChk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf chk", @@ -9054,6 +9265,7 @@ static command_t CommandTable[] = { {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"}, {"staticnested", CmdHF14AMfNestedStatic, IfPm3Iso14443a, "Nested attack against static nonce MIFARE Classic cards"}, + {"brute", CmdHF14AMfSmartBrute, IfPm3Iso14443a, "Smart bruteforce to exploit weak key generators"}, {"autopwn", CmdHF14AMfAutoPWN, IfPm3Iso14443a, "Automatic key recovery tool for MIFARE Classic"}, // {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, {"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for MIFARE NACK bug"}, diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 2efaa1f0b..39510c27b 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -386,10 +386,9 @@ int CmdEM4x50Brute(const char *Cmd) { etd.bruteforce_mode = BF_MODE_RANGE; } else if (strcmp(mode, "charset") == 0) { etd.bruteforce_mode = BF_MODE_CHARSET; - } else if (strcmp(mode, "smart") == 0){ + } else if (strcmp(mode, "smart") == 0) { etd.bruteforce_mode = BF_MODE_SMART; - } else - { + } else { PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode); CLIParserFree(ctx); return PM3_EINVARG; @@ -471,7 +470,7 @@ int CmdEM4x50Brute(const char *Cmd) { dur_s -= dur_h * 3600 + dur_m * 60; - if ( no_iter > 0 ) + if (no_iter > 0) PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s); else PrintAndLogEx(INFO, "Estimated duration: unknown"); diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 17c9c03e7..aa845203d 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -327,6 +327,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mf nested" }, { 1, "hf mf hardnested" }, { 0, "hf mf staticnested" }, + { 0, "hf mf brute" }, { 0, "hf mf autopwn" }, { 0, "hf mf nack" }, { 0, "hf mf chk" }, @@ -501,8 +502,6 @@ const static vocabulary_t vocabulary[] = { { 1, "hf vas help" }, { 0, "hf vas reader" }, { 1, "hf vas decrypt" }, - { 1, "hf waveshare help" }, - { 0, "hf waveshare loadbmp" }, { 1, "hf xerox help" }, { 0, "hf xerox info" }, { 0, "hf xerox reader" }, diff --git a/common/bruteforce.c b/common/bruteforce.c index 4ac8834c8..5946a689b 100644 --- a/common/bruteforce.c +++ b/common/bruteforce.c @@ -30,6 +30,8 @@ uint8_t charset_uppercase[] = { smart_generator_t *smart_generators[] = { smart_generator_byte_repeat, + smart_generator_msb_byte_only, + smart_generator_nibble_sequence, NULL }; @@ -68,7 +70,7 @@ int bf_generate(generator_context_t *ctx) { case BF_MODE_SMART: return _bf_generate_mode_smart(ctx); - } + } return BF_GENERATOR_ERROR; } @@ -106,16 +108,16 @@ int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) { } // get current key casted to 32 bit -uint32_t bf_get_key32(generator_context_t *ctx){ +uint32_t bf_get_key32(generator_context_t *ctx) { return ctx->current_key & 0xFFFFFFFF; } // get current key casted to 48 bit -uint64_t bf_get_key48(generator_context_t *ctx){ +uint64_t bf_get_key48(generator_context_t *ctx) { return ctx->current_key & 0xFFFFFFFFFFFF; } -void bf_generator_clear(generator_context_t *ctx){ +void bf_generator_clear(generator_context_t *ctx) { ctx->flag1 = 0; ctx->flag2 = 0; ctx->flag3 = 0; @@ -126,7 +128,7 @@ void bf_generator_clear(generator_context_t *ctx){ int _bf_generate_mode_range(generator_context_t *ctx) { if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) - return BF_GENERATOR_ERROR; + return BF_GENERATOR_ERROR; if (ctx->current_key >= ctx->range_high) { return BF_GENERATOR_END; @@ -146,7 +148,7 @@ int _bf_generate_mode_range(generator_context_t *ctx) { int _bf_generate_mode_charset(generator_context_t *ctx) { - if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48){ + if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) { return BF_GENERATOR_ERROR; } @@ -156,9 +158,8 @@ int _bf_generate_mode_charset(generator_context_t *ctx) { uint8_t key_byte = 0; ctx->current_key = 0; - for (key_byte = 0; key_byte < ctx->key_length; key_byte++) - { - ctx->current_key |= (uint64_t) ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte - 1) * 8); + for (key_byte = 0; key_byte < ctx->key_length; key_byte++) { + ctx->current_key |= (uint64_t) ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte - 1) * 8); } if (bf_array_increment(ctx->pos, ctx->key_length, ctx->charset_length) == -1) @@ -168,17 +169,17 @@ int _bf_generate_mode_charset(generator_context_t *ctx) { return BF_GENERATOR_NEXT; } -int _bf_generate_mode_smart(generator_context_t *ctx){ +int _bf_generate_mode_smart(generator_context_t *ctx) { int ret; - while(1){ + while (1) { if (smart_generators[ctx->smart_mode_stage] == NULL) - return BF_GENERATOR_END; + return BF_GENERATOR_END; ret = smart_generators[ctx->smart_mode_stage](ctx); - switch (ret){ + switch (ret) { case BF_GENERATOR_NEXT: return ret; case BF_GENERATOR_ERROR: @@ -192,7 +193,7 @@ int _bf_generate_mode_smart(generator_context_t *ctx){ } -int smart_generator_byte_repeat(generator_context_t *ctx){ +int smart_generator_byte_repeat(generator_context_t *ctx) { // key consists of repeated single byte uint32_t current_byte = ctx->counter1; @@ -201,13 +202,59 @@ int smart_generator_byte_repeat(generator_context_t *ctx){ ctx->current_key = 0; - for (uint8_t key_byte = 0; key_byte < ctx->key_length;key_byte++){ - ctx->current_key |= (uint64_t)current_byte << ((ctx->key_length - key_byte - 1) * 8); + for (uint8_t key_byte = 0; key_byte < ctx->key_length; key_byte++) { + ctx->current_key |= (uint64_t)current_byte << ((ctx->key_length - key_byte - 1) * 8); } ctx->counter1++; return BF_GENERATOR_NEXT; } -int smart_generator_test2(generator_context_t *ctx){ - return 0; +int smart_generator_msb_byte_only(generator_context_t *ctx) { + // key of one byte (most significant one) and all others being zero + uint32_t current_byte = ctx->counter1; + + if (current_byte > 0xFF) + return BF_GENERATOR_END; + + ctx->current_key = (uint64_t)current_byte << ((ctx->key_length - 1) * 8); + + ctx->counter1++; + return BF_GENERATOR_NEXT; } + + +int smart_generator_nibble_sequence(generator_context_t *ctx) { + // patterns like A0A1A2A3...F0F1F2F3 + // also with offsets - A1A2A3, A2A3A4, etc + // counter1 is high nibble (A, B, C), counter2 is low nibble (0,1, etc) + + if(ctx->counter1 == 0){ // init values on first generator call + ctx->counter1 = 0x0A; + } + + uint8_t key_byte; + + // we substract %2 value because max_offset must be even number + uint8_t max_offset = 10 - (ctx->key_length / 2) - (ctx->key_length/2) % 2; + + if(ctx->counter1 == 0x10){ + return BF_GENERATOR_END; + } + + ctx->current_key = 0; + + for (key_byte = 0; key_byte < ctx->key_length; key_byte++) { + ctx->current_key |= (uint64_t) ctx->counter1 << (((ctx->key_length - key_byte - 1) * 8) + 4); + ctx->current_key |= (uint64_t) (key_byte + ctx->counter2) %10 << ((ctx->key_length - key_byte - 1) * 8); + } + + // counter 2 is the offset + ctx->counter2++; + + if(ctx->counter2 == max_offset){ + ctx->counter2 = 0; + ctx->counter1++; + } + + return BF_GENERATOR_NEXT; +} \ No newline at end of file diff --git a/common/bruteforce.h b/common/bruteforce.h index 16258465e..61d1107b3 100644 --- a/common/bruteforce.h +++ b/common/bruteforce.h @@ -96,7 +96,8 @@ typedef int (smart_generator_t)(generator_context_t *ctx); int bf_generate_mode_smart(generator_context_t *ctx); int smart_generator_byte_repeat(generator_context_t *ctx); -int smart_generator_test2(generator_context_t *ctx); +int smart_generator_msb_byte_only(generator_context_t *ctx); +int smart_generator_nibble_sequence(generator_context_t *ctx); extern smart_generator_t *smart_generators[]; // array of smart cracking functions diff --git a/doc/commands.md b/doc/commands.md index 8478e3205..1820a55f1 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -490,6 +490,7 @@ Check column "offline" for their availability. |`hf mf nested `|N |`Nested attack` |`hf mf hardnested `|Y |`Nested attack for hardened MIFARE Classic cards` |`hf mf staticnested `|N |`Nested attack against static nonce MIFARE Classic cards` +|`hf mf brute `|N |`Smart bruteforce to exploit weak key generators` |`hf mf autopwn `|N |`Automatic key recovery tool for MIFARE Classic` |`hf mf nack `|N |`Test for MIFARE NACK bug` |`hf mf chk `|N |`Check keys` @@ -754,16 +755,6 @@ Check column "offline" for their availability. |`hf vas decrypt `|Y |`Decrypt a previously captured VAS cryptogram` -### hf waveshare - - { Waveshare NFC ePaper... } - -|command |offline |description -|------- |------- |----------- -|`hf waveshare help `|Y |`This help` -|`hf waveshare loadbmp `|N |`Load BMP file to Waveshare NFC ePaper` - - ### hf xerox { Fuji/Xerox cartridge RFIDs... } From fe5691997d2746c98ddb25a09b63359bad4d5f81 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Tue, 16 Jan 2024 21:14:40 +0000 Subject: [PATCH 06/11] fix typo + missing free() --- client/dictionaries/mfc_default_keys.dic | 8 +++++++- client/src/cmdhfmf.c | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 795d8fe0a..eeea4270a 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2338,7 +2338,7 @@ EA0CA627FD06 CE0F4F15E909 D60DE9436219 -# ATM Arena de Girona, spanish transport card +# ATM Area de Girona, spanish transport card A00000000000 A01000000000 @@ -2373,3 +2373,9 @@ B12000000000 B13000000000 B14000000000 B15000000000 + +# TAM - Transporte Alicante Metropolitano (spanish transport card) +# Sectors 9-15 + +C0C1C2C3C4C5 +B0B1B2B3B4B5 diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 96607d4db..b57a1562a 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3469,6 +3469,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { if (ret == BF_GENERATOR_ERROR) { PrintAndLogEx(ERR, "Internal bruteforce generator error"); free(keyBlock); + free(e_sector); return PM3_EFAILED; } else if (ret == BF_GENERATOR_END) { lastChunk = true; @@ -6208,6 +6209,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { } uint8_t ndefkey[6] = {0}; + printf("%d", keylen) memcpy(ndefkey, g_mifare_ndef_key, 6); if (keylen == 6) { memcpy(ndefkey, key, 6); From 1e1f1fcae4e28ed0b15263d0a8fc4494dd1f0046 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Tue, 16 Jan 2024 21:30:02 +0000 Subject: [PATCH 07/11] remove invalid change and duplicate keys --- client/dictionaries/mfc_default_keys.dic | 7 ------- client/src/cmdhfmf.c | 18 ++++++++---------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index eeea4270a..038f9480b 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2340,7 +2340,6 @@ D60DE9436219 # ATM Area de Girona, spanish transport card -A00000000000 A01000000000 A02000000000 A03000000000 @@ -2357,7 +2356,6 @@ A13000000000 A14000000000 A15000000000 -B00000000000 B01000000000 B02000000000 B03000000000 @@ -2374,8 +2372,3 @@ B13000000000 B14000000000 B15000000000 -# TAM - Transporte Alicante Metropolitano (spanish transport card) -# Sectors 9-15 - -C0C1C2C3C4C5 -B0B1B2B3B4B5 diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index b57a1562a..f35613817 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3389,7 +3389,6 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to binary file"), - arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3402,7 +3401,6 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { bool transferToEml = arg_get_lit(ctx, 6); bool createDumpFile = arg_get_lit(ctx, 7); - bool use_flashmemory = arg_get_lit(ctx, 8); CLIParserFree(ctx); @@ -3531,13 +3529,6 @@ out: printKeyTable(sectorsCnt, e_sector); - if (use_flashmemory && found_keys == (sectorsCnt << 1)) { - PrintAndLogEx(SUCCESS, "Card dumped as well. run " _YELLOW_("`%s %c`"), - "hf mf esave", - GetFormatFromSector(sectorsCnt) - ); - } - if (transferToEml) { // fast push mode g_conn.block_after_ACK = true; @@ -3572,6 +3563,14 @@ out: PrintAndLogEx(ERR, "Failed to save keys to file"); } free(fptr); + + if (found_keys == (sectorsCnt << 1)) { + PrintAndLogEx(SUCCESS, "Card dumped as well. run " _YELLOW_("`%s %c`"), + "hf mf esave", + GetFormatFromSector(sectorsCnt) + ); + } + } } @@ -6209,7 +6208,6 @@ int CmdHFMFNDEFRead(const char *Cmd) { } uint8_t ndefkey[6] = {0}; - printf("%d", keylen) memcpy(ndefkey, g_mifare_ndef_key, 6); if (keylen == 6) { memcpy(ndefkey, key, 6); From 6ab52d55fa6cf6f8bedc92097544b9b5754e7e04 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Tue, 16 Jan 2024 21:44:44 +0000 Subject: [PATCH 08/11] fix type casting --- client/src/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index f35613817..1f629028e 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3482,7 +3482,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { PrintAndLogEx(INFO, "Running bruteforce stage %d", smart_mode_stage); if (msclock() - t1 > 0 && keys_checked > 0) { - PrintAndLogEx(INFO, "Current cracking speed (keys/s): %d", + PrintAndLogEx(INFO, "Current cracking speed (keys/s): %u", keys_checked / ((msclock() - t1) / 1000)); t1 = msclock(); @@ -3508,7 +3508,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { out: PrintAndLogEx(INFO, "Time in brute mode: " _YELLOW_("%.1fs") "\n", (float)((msclock() - t0) / 1000.0)); - PrintAndLogEx(INFO, "Total keys checked: " _YELLOW_("%d") "\n", total_keys_checked); + PrintAndLogEx(INFO, "Total keys checked: " _YELLOW_("%u") "\n", total_keys_checked); // check.. uint8_t found_keys = 0; for (i = 0; i < sectorsCnt; ++i) { From d5070b34f4fa18fb74b72d5484b5991a7a726fa4 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Tue, 16 Jan 2024 22:31:04 +0000 Subject: [PATCH 09/11] fix cmake dependency --- client/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 88bbb9ead..b223adee9 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -260,6 +260,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/common/iso15693tools.c ${PM3_ROOT}/common/cardhelper.c ${PM3_ROOT}/common/generator.c + ${PM3_ROOT}/common/bruteforce.c ${PM3_ROOT}/client/src/crypto/asn1dump.c ${PM3_ROOT}/client/src/crypto/asn1utils.c ${PM3_ROOT}/client/src/crypto/libpcrypto.c From be374ec2afec6a77727cdb056820f0522d438048 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Tue, 16 Jan 2024 22:40:17 +0000 Subject: [PATCH 10/11] remove unused message log --- client/src/cmdhfmf.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 1f629028e..5a4ff3e86 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3564,13 +3564,6 @@ out: } free(fptr); - if (found_keys == (sectorsCnt << 1)) { - PrintAndLogEx(SUCCESS, "Card dumped as well. run " _YELLOW_("`%s %c`"), - "hf mf esave", - GetFormatFromSector(sectorsCnt) - ); - } - } } From b317d34cfd7249bc2ecf9848e505f1b07a0bd221 Mon Sep 17 00:00:00 2001 From: PhaseLoop Date: Tue, 16 Jan 2024 22:48:58 +0000 Subject: [PATCH 11/11] fix printf types --- client/src/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 5a4ff3e86..d2aadadab 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3482,7 +3482,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { PrintAndLogEx(INFO, "Running bruteforce stage %d", smart_mode_stage); if (msclock() - t1 > 0 && keys_checked > 0) { - PrintAndLogEx(INFO, "Current cracking speed (keys/s): %u", + PrintAndLogEx(INFO, "Current cracking speed (keys/s): %lu", keys_checked / ((msclock() - t1) / 1000)); t1 = msclock(); @@ -3508,7 +3508,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { out: PrintAndLogEx(INFO, "Time in brute mode: " _YELLOW_("%.1fs") "\n", (float)((msclock() - t0) / 1000.0)); - PrintAndLogEx(INFO, "Total keys checked: " _YELLOW_("%u") "\n", total_keys_checked); + PrintAndLogEx(INFO, "Total keys checked: " _YELLOW_("%lu") "\n", total_keys_checked); // check.. uint8_t found_keys = 0; for (i = 0; i < sectorsCnt; ++i) {