Merge pull request #2251 from phaseloop/bruteforce-smart-mode

Add smart bruteforce mode to Mifare Classic and EM4x50
This commit is contained in:
Iceman 2024-01-17 00:06:07 +01:00 committed by GitHub
commit a62556a9b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 493 additions and 83 deletions

View file

@ -641,13 +641,17 @@ static bool brute(const 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);
} else if (etd->bruteforce_mode == BF_MODE_RANGE) {
ctx.range_low = etd->password1;
ctx.range_high = etd->password2;
}
while ((generator_ret = bf_generate32(&ctx)) == GENERATOR_NEXT) {
*pwd = ctx.current_key32;
while ((generator_ret = bf_generate(&ctx)) == BF_GENERATOR_NEXT) {
*pwd = bf_get_key32(&ctx);
WDT_HIT();

View file

@ -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

View file

@ -749,6 +749,7 @@ SRCS = mifare/aiddesfire.c \
# common
SRCS += bucketsort.c \
bruteforce.c \
cardhelper.c \
crapto1/crapto1.c \
crapto1/crypto1.c \

View file

@ -2337,3 +2337,38 @@ EA0CA627FD06
# Hotel key
CE0F4F15E909
D60DE9436219
# ATM Area de Girona, spanish transport card
A01000000000
A02000000000
A03000000000
A04000000000
A05000000000
A06000000000
A07000000000
A08000000000
A09000000000
A10000000000
A11000000000
A12000000000
A13000000000
A14000000000
A15000000000
B01000000000
B02000000000
B03000000000
B04000000000
B05000000000
B06000000000
B07000000000
B08000000000
B09000000000
B10000000000
B11000000000
B12000000000
B13000000000
B14000000000
B15000000000

View file

@ -16,6 +16,7 @@
// High frequency MIFARE commands
//-----------------------------------------------------------------------------
#include "bruteforce.h"
#include "cmdhfmf.h"
#include <ctype.h>
#include "cmdparser.h" // command_t
@ -3369,6 +3370,209 @@ 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_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);
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);
free(e_sector);
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): %lu",
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_("%lu") "\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 (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 +9258,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"},

View file

@ -359,11 +359,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", "<str>", "Bruteforce mode (range|charset)"),
arg_str1(NULL, "mode", "<str>", "Bruteforce mode (range|charset|smart)"),
arg_str0(NULL, "begin", "<hex>", "Range mode - start of the key range"),
arg_str0(NULL, "end", "<hex>", "Range mode - end of the key range"),
arg_lit0(NULL, "digits", "Charset mode - include ASCII codes for digits"),
@ -382,16 +383,18 @@ 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 if (strcmp(mode, "smart") == 0) {
etd.bruteforce_mode = BF_MODE_SMART;
} else {
PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode);
CLIParserFree(ctx);
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);
@ -414,14 +417,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.");
@ -441,21 +444,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);
}
@ -467,7 +470,10 @@ int CmdEM4x50Brute(const char *Cmd) {
dur_s -= dur_h * 3600 + dur_m * 60;
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();

View file

@ -329,6 +329,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" },

View file

@ -28,22 +28,31 @@ uint8_t charset_uppercase[] = {
'X', 'Y', 'Z'
};
void bf_generator_init(generator_context_t *ctx, uint8_t mode) {
smart_generator_t *smart_generators[] = {
smart_generator_byte_repeat,
smart_generator_msb_byte_only,
smart_generator_nibble_sequence,
NULL
};
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) {
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);
}
@ -51,51 +60,21 @@ 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 BRUTEFORCE_MODE_RANGE:
return _bf_generate_mode_range32(ctx);
case BRUTEFORCE_MODE_CHARSET:
return _bf_generate_mode_charset32(ctx);
case BF_MODE_RANGE:
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 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;
}
// we use flag1 as indicator if value of range_low was already emitted
// so the range generated is <range_low, range_high>
if (ctx->current_key32 <= ctx->range_low && ctx->flag1 == false) {
ctx->current_key32 = ctx->range_low;
ctx->pos[0] = true;
return GENERATOR_NEXT;
}
ctx->current_key32++;
return GENERATOR_NEXT;
}
int _bf_generate_mode_charset32(generator_context_t *ctx) {
if (ctx->flag1)
return 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]];
if (bf_array_increment(ctx->pos, 4, ctx->charset_length) == -1)
// set flag1 to emit value last time and end generation
ctx->flag1 = true;
return 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
@ -127,3 +106,155 @@ 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;
}
// get current key casted to 48 bit
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)
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 <range_low, range_high>
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_key++;
return BF_GENERATOR_NEXT;
}
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;
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);
}
if (bf_array_increment(ctx->pos, ctx->key_length, ctx->charset_length) == -1)
// 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 ret;
while (1) {
if (smart_generators[ctx->smart_mode_stage] == NULL)
return BF_GENERATOR_END;
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++;
bf_generator_clear(ctx);
continue;
}
}
}
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_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;
}

View file

@ -21,59 +21,84 @@
#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 CHARSET_DIGITS_SIZE 10
#define CHARSET_UPPERCASE_SIZE 25
#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 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[4];
uint32_t current_key32;
uint8_t pos[6]; // max supported key is now 48 bit
uint8_t key_length; // bytes
uint64_t current_key; // Use 64 bit and truncate when needed.
uint8_t mode;
uint8_t charset[
CHARSET_DIGITS_SIZE
+ CHARSET_UPPERCASE_SIZE
BF_CHARSET_DIGITS_SIZE
+ BF_CHARSET_UPPERCASE_SIZE
];
uint8_t charset_length;
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);
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_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);
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_byte_repeat(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
#endif // BRUTEFORCE_H__

View file

@ -492,6 +492,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`