mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
Add function to brute force partial key
This commit is contained in:
parent
08930766cb
commit
71389e0b17
7 changed files with 211 additions and 6 deletions
|
@ -1221,6 +1221,10 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
em4x70_write_key((em4x70_data_t *)packet->data.asBytes, true);
|
||||
break;
|
||||
}
|
||||
case CMD_LF_EM4X70_BRUTE: {
|
||||
em4x70_brute((em4x70_data_t *)packet->data.asBytes, true);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ZX8211
|
||||
|
|
106
armsrc/em4x70.c
106
armsrc/em4x70.c
|
@ -21,6 +21,7 @@
|
|||
#include "dbprint.h"
|
||||
#include "lfadc.h"
|
||||
#include "commonutil.h"
|
||||
#include "optimized_cipherutils.h"
|
||||
#include "em4x70.h"
|
||||
#include "appmain.h" // tear
|
||||
|
||||
|
@ -85,7 +86,7 @@ static int em4x70_receive(uint8_t *bits, size_t length);
|
|||
static bool find_listen_window(bool command);
|
||||
|
||||
static void init_tag(void) {
|
||||
memset(tag.data, 0x00, ARRAYLEN(tag.data));
|
||||
memset(tag.data, 0x00, sizeof(tag.data));
|
||||
}
|
||||
|
||||
static void em4x70_setup_read(void) {
|
||||
|
@ -298,14 +299,14 @@ static bool check_ack(void) {
|
|||
// returns true if signal structue corresponds to ACK, anything else is
|
||||
// counted as NAK (-> false)
|
||||
// ACK 64 + 64
|
||||
// NACK 64 + 48
|
||||
// NAK 64 + 48
|
||||
if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) &&
|
||||
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
|
||||
// ACK
|
||||
return true;
|
||||
}
|
||||
|
||||
// Othewise it was a NACK or Listen Window
|
||||
// Otherwise it was a NAK or Listen Window
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -339,7 +340,7 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon
|
|||
uint8_t grnd[EM4X70_MAX_RECEIVE_LENGTH] = {0};
|
||||
int num = em4x70_receive(grnd, 20);
|
||||
if (num < 20) {
|
||||
Dbprintf("Auth failed");
|
||||
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
bits2bytes(grnd, 24, response);
|
||||
|
@ -349,6 +350,80 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon
|
|||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
static int set_byte(uint8_t *target, int value) {
|
||||
int c = value > 0xFF;
|
||||
*target = reflect8(value);
|
||||
return c;
|
||||
}
|
||||
|
||||
static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t *frnd, uint16_t start_key, uint8_t *response) {
|
||||
|
||||
uint8_t auth_resp[3] = {0};
|
||||
uint8_t rev_rnd[7];
|
||||
uint8_t temp_rnd[7];
|
||||
|
||||
reverse_arraycopy((uint8_t *)rnd, rev_rnd, sizeof(rnd));
|
||||
memcpy(temp_rnd, rnd, sizeof(temp_rnd));
|
||||
|
||||
for (int k = start_key; k <= 0xFFFF; ++k) {
|
||||
int c = 0;
|
||||
|
||||
WDT_HIT();
|
||||
|
||||
uint16_t rev_k = reflect16(k);
|
||||
switch (address) {
|
||||
case 9:
|
||||
c = set_byte(&temp_rnd[0], rev_rnd[0] + (rev_k & 0xFF));
|
||||
c = set_byte(&temp_rnd[1], rev_rnd[1] + c + ((rev_k >> 8) & 0xFF));
|
||||
c = set_byte(&temp_rnd[2], rev_rnd[2] + c);
|
||||
c = set_byte(&temp_rnd[3], rev_rnd[3] + c);
|
||||
c = set_byte(&temp_rnd[4], rev_rnd[4] + c);
|
||||
c = set_byte(&temp_rnd[5], rev_rnd[5] + c);
|
||||
set_byte(&temp_rnd[6], rev_rnd[6] + c);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
c = set_byte(&temp_rnd[2], rev_rnd[2] + (rev_k & 0xFF));
|
||||
c = set_byte(&temp_rnd[3], rev_rnd[3] + c + ((rev_k >> 8) & 0xFF));
|
||||
c = set_byte(&temp_rnd[4], rev_rnd[4] + c);
|
||||
c = set_byte(&temp_rnd[5], rev_rnd[5] + c);
|
||||
set_byte(&temp_rnd[6], rev_rnd[6] + c);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
c = set_byte(&temp_rnd[4], rev_rnd[4] + (rev_k & 0xFF));
|
||||
c = set_byte(&temp_rnd[5], rev_rnd[5] + c + ((rev_k >> 8) & 0xFF));
|
||||
set_byte(&temp_rnd[6], rev_rnd[6] + c);
|
||||
break;
|
||||
|
||||
default:
|
||||
Dbprintf("Bad block number given: %d", address);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// Report progress every 256 attempts
|
||||
if ((k % 0x100) == 0) {
|
||||
Dbprintf("Trying: %04X", k);
|
||||
}
|
||||
|
||||
// Due to performance reason, we only try it once. Therefore you need a very stable RFID communcation.
|
||||
if (authenticate(temp_rnd, frnd, auth_resp) == PM3_SUCCESS) {
|
||||
if (g_dbglevel >= DBG_INFO)
|
||||
Dbprintf("Authentication success with rnd: %02X%02X%02X%02X%02X%02X%02X", temp_rnd[0], temp_rnd[1], temp_rnd[2], temp_rnd[3], temp_rnd[4], temp_rnd[5], temp_rnd[6]);
|
||||
response[0] = (k >> 8) & 0xFF;
|
||||
response[1] = k & 0xFF;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS() || data_available()) {
|
||||
Dbprintf("EM4x70 Bruteforce Interrupted");
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
static int send_pin(const uint32_t pin) {
|
||||
|
||||
// sends pin code for unlocking
|
||||
|
@ -576,7 +651,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
|
|||
}
|
||||
|
||||
if (!foundheader) {
|
||||
Dbprintf("Failed to find read header");
|
||||
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Failed to find read header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -738,6 +813,27 @@ void em4x70_auth(em4x70_data_t *etd, bool ledcontrol) {
|
|||
reply_ng(CMD_LF_EM4X70_AUTH, status, response, sizeof(response));
|
||||
}
|
||||
|
||||
void em4x70_brute(em4x70_data_t *etd, bool ledcontrol) {
|
||||
uint8_t status = 0;
|
||||
uint8_t response[2] = {0};
|
||||
|
||||
command_parity = etd->parity;
|
||||
|
||||
init_tag();
|
||||
em4x70_setup_read();
|
||||
|
||||
// Find the Tag
|
||||
if (get_signalproperties() && find_em4x70_tag()) {
|
||||
|
||||
// Bruteforce partial key
|
||||
status = bruteforce(etd->address, etd->rnd, etd->frnd, etd->start_key, response) == PM3_SUCCESS;
|
||||
}
|
||||
|
||||
StopTicks();
|
||||
lf_finalize(ledcontrol);
|
||||
reply_ng(CMD_LF_EM4X70_BRUTE, status, response, sizeof(response));
|
||||
}
|
||||
|
||||
void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol) {
|
||||
|
||||
uint8_t status = 0;
|
||||
|
|
|
@ -32,6 +32,7 @@ typedef enum {
|
|||
|
||||
void em4x70_info(em4x70_data_t *etd, bool ledcontrol);
|
||||
void em4x70_write(em4x70_data_t *etd, bool ledcontrol);
|
||||
void em4x70_brute(em4x70_data_t *etd, bool ledcontrol);
|
||||
void em4x70_unlock(em4x70_data_t *etd, bool ledcontrol);
|
||||
void em4x70_auth(em4x70_data_t *etd, bool ledcontrol);
|
||||
void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol);
|
||||
|
|
|
@ -200,7 +200,7 @@ int CmdEM4x70Write(const char *Cmd) {
|
|||
}
|
||||
|
||||
etd.address = (uint8_t) addr;
|
||||
etd.word = BYTES2UINT16(word);;
|
||||
etd.word = BYTES2UINT16(word);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd));
|
||||
|
@ -220,6 +220,104 @@ int CmdEM4x70Write(const char *Cmd) {
|
|||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
int CmdEM4x70Brute(const char *Cmd) {
|
||||
|
||||
// From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
|
||||
// Partial Key-Update Attack (optimized version)
|
||||
em4x70_data_t etd = {0};
|
||||
|
||||
CLIParserContext *ctx;
|
||||
|
||||
CLIParserInit(&ctx, "lf em 4x70 brute",
|
||||
"Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70\n"
|
||||
"This attack does NOT write anything to the tag.\n"
|
||||
"Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'.\n"
|
||||
"After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'\n",
|
||||
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80\n"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
|
||||
arg_int1("b", "block", "<dec>", "block/word address, dec"),
|
||||
arg_str1(NULL, "rnd", "<hex>", "Random 56-bit"),
|
||||
arg_str1(NULL, "frn", "<hex>", "F(RN) 28-bit as 4 hex bytes"),
|
||||
arg_str0("s", "start", "<hex>", "Start bruteforce enumeration from this key value"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
etd.parity = arg_get_lit(ctx, 1);
|
||||
|
||||
int addr = arg_get_int_def(ctx, 2, 0);
|
||||
if (addr < 7 || addr > 9) {
|
||||
PrintAndLogEx(FAILED, "block has to be within range [7, 9] got: %d", addr);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
etd.address = (uint8_t) addr;
|
||||
|
||||
int rnd_len = 7;
|
||||
CLIGetHexWithReturn(ctx, 3, etd.rnd, &rnd_len);
|
||||
|
||||
int frnd_len = 4;
|
||||
CLIGetHexWithReturn(ctx, 4, etd.frnd, &frnd_len);
|
||||
|
||||
uint32_t start_key = 0;
|
||||
int res = arg_get_u32_hexstr_def_nlen(ctx, 5, 0, &start_key, 2, true);
|
||||
if (res == 2) {
|
||||
CLIParserFree(ctx);
|
||||
PrintAndLogEx(WARNING, "start key parameter must be in range [0, FFFF]");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
etd.start_key = start_key;
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (rnd_len != 7) {
|
||||
PrintAndLogEx(FAILED, "Random number length must be 7 bytes instead of %d", rnd_len);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (frnd_len != 4) {
|
||||
PrintAndLogEx(FAILED, "F(RN) length must be 4 bytes instead of %d", frnd_len);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit");
|
||||
clearCommandBuffer();
|
||||
PacketResponseNG resp;
|
||||
SendCommandNG(CMD_LF_EM4X70_BRUTE, (uint8_t *)&etd, sizeof(etd));
|
||||
|
||||
uint32_t timeout = 0;
|
||||
for (;;) {
|
||||
|
||||
if (kbd_enter_pressed()) {
|
||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||
PrintAndLogEx(DEBUG, "User aborted");
|
||||
break;
|
||||
}
|
||||
|
||||
if (WaitForResponseTimeout(CMD_LF_EM4X70_BRUTE, &resp, TIMEOUT)) {
|
||||
if (resp.status) {
|
||||
// Response is 16-bit partial key
|
||||
PrintAndLogEx(INFO, "Partial Key Response: %02X %02X", resp.data.asBytes[0], resp.data.asBytes[1]);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// should be done in about 30 minutes
|
||||
if (timeout > ((30 * 60000) / TIMEOUT)) {
|
||||
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
|
||||
break;
|
||||
}
|
||||
timeout++;
|
||||
}
|
||||
|
||||
PrintAndLogEx(FAILED, "Bruteforce of partial key " _RED_("failed"));
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
int CmdEM4x70Unlock(const char *Cmd) {
|
||||
|
||||
// send pin code to device, unlocking it for writing
|
||||
|
@ -452,6 +550,7 @@ int CmdEM4x70WriteKey(const char *Cmd) {
|
|||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"brute", CmdEM4x70Brute, IfPm3EM4x70, "Bruteforce EM4X70 to find partial Crypt Key"},
|
||||
{"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"},
|
||||
{"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"},
|
||||
{"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"},
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
int CmdLFEM4X70(const char *Cmd);
|
||||
int CmdEM4x70Info(const char *Cmd);
|
||||
int CmdEM4x70Write(const char *Cmd);
|
||||
int CmdEM4x70Brute(const char *Cmd);
|
||||
int CmdEM4x70Unlock(const char *Cmd);
|
||||
int CmdEM4x70Auth(const char *Cmd);
|
||||
int CmdEM4x70WritePIN(const char *Cmd);
|
||||
|
|
|
@ -42,6 +42,9 @@ typedef struct {
|
|||
// Used to write new key
|
||||
uint8_t crypt_key[12];
|
||||
|
||||
// used for bruteforce the partial key
|
||||
uint16_t start_key;
|
||||
|
||||
} em4x70_data_t;
|
||||
|
||||
#endif /* EM4X70_H__ */
|
||||
|
|
|
@ -484,6 +484,7 @@ typedef struct {
|
|||
#define CMD_LF_EM4X70_AUTH 0x0263
|
||||
#define CMD_LF_EM4X70_WRITEPIN 0x0264
|
||||
#define CMD_LF_EM4X70_WRITEKEY 0x0265
|
||||
#define CMD_LF_EM4X70_BRUTE 0x0266
|
||||
// Sampling configuration for LF reader/sniffer
|
||||
#define CMD_LF_SAMPLING_SET_CONFIG 0x021D
|
||||
#define CMD_LF_FSK_SIMULATE 0x021E
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue