mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 22:03:42 -07:00
Merge pull request #2251 from phaseloop/bruteforce-smart-mode
Add smart bruteforce mode to Mifare Classic and EM4x50
This commit is contained in:
commit
a62556a9b3
10 changed files with 493 additions and 83 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -749,6 +749,7 @@ SRCS = mifare/aiddesfire.c \
|
|||
|
||||
# common
|
||||
SRCS += bucketsort.c \
|
||||
bruteforce.c \
|
||||
cardhelper.c \
|
||||
crapto1/crapto1.c \
|
||||
crapto1/crypto1.c \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
||||
|
|
|
@ -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`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue