mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -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);
|
em4x70_write_key((em4x70_data_t *)packet->data.asBytes, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CMD_LF_EM4X70_BRUTE: {
|
||||||
|
em4x70_brute((em4x70_data_t *)packet->data.asBytes, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_ZX8211
|
#ifdef WITH_ZX8211
|
||||||
|
|
106
armsrc/em4x70.c
106
armsrc/em4x70.c
|
@ -21,6 +21,7 @@
|
||||||
#include "dbprint.h"
|
#include "dbprint.h"
|
||||||
#include "lfadc.h"
|
#include "lfadc.h"
|
||||||
#include "commonutil.h"
|
#include "commonutil.h"
|
||||||
|
#include "optimized_cipherutils.h"
|
||||||
#include "em4x70.h"
|
#include "em4x70.h"
|
||||||
#include "appmain.h" // tear
|
#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 bool find_listen_window(bool command);
|
||||||
|
|
||||||
static void init_tag(void) {
|
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) {
|
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
|
// returns true if signal structue corresponds to ACK, anything else is
|
||||||
// counted as NAK (-> false)
|
// counted as NAK (-> false)
|
||||||
// ACK 64 + 64
|
// ACK 64 + 64
|
||||||
// NACK 64 + 48
|
// NAK 64 + 48
|
||||||
if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) &&
|
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)) {
|
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
|
||||||
// ACK
|
// ACK
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Othewise it was a NACK or Listen Window
|
// Otherwise it was a NAK or Listen Window
|
||||||
return false;
|
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};
|
uint8_t grnd[EM4X70_MAX_RECEIVE_LENGTH] = {0};
|
||||||
int num = em4x70_receive(grnd, 20);
|
int num = em4x70_receive(grnd, 20);
|
||||||
if (num < 20) {
|
if (num < 20) {
|
||||||
Dbprintf("Auth failed");
|
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed");
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
bits2bytes(grnd, 24, response);
|
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;
|
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) {
|
static int send_pin(const uint32_t pin) {
|
||||||
|
|
||||||
// sends pin code for unlocking
|
// sends pin code for unlocking
|
||||||
|
@ -576,7 +651,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundheader) {
|
if (!foundheader) {
|
||||||
Dbprintf("Failed to find read header");
|
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Failed to find read header");
|
||||||
return 0;
|
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));
|
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) {
|
void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol) {
|
||||||
|
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef enum {
|
||||||
|
|
||||||
void em4x70_info(em4x70_data_t *etd, bool ledcontrol);
|
void em4x70_info(em4x70_data_t *etd, bool ledcontrol);
|
||||||
void em4x70_write(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_unlock(em4x70_data_t *etd, bool ledcontrol);
|
||||||
void em4x70_auth(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);
|
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.address = (uint8_t) addr;
|
||||||
etd.word = BYTES2UINT16(word);;
|
etd.word = BYTES2UINT16(word);
|
||||||
|
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd));
|
SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd));
|
||||||
|
@ -220,6 +220,104 @@ int CmdEM4x70Write(const char *Cmd) {
|
||||||
return PM3_ESOFT;
|
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) {
|
int CmdEM4x70Unlock(const char *Cmd) {
|
||||||
|
|
||||||
// send pin code to device, unlocking it for writing
|
// send pin code to device, unlocking it for writing
|
||||||
|
@ -452,6 +550,7 @@ int CmdEM4x70WriteKey(const char *Cmd) {
|
||||||
|
|
||||||
static command_t CommandTable[] = {
|
static command_t CommandTable[] = {
|
||||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||||
|
{"brute", CmdEM4x70Brute, IfPm3EM4x70, "Bruteforce EM4X70 to find partial Crypt Key"},
|
||||||
{"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"},
|
{"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"},
|
||||||
{"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"},
|
{"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"},
|
||||||
{"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"},
|
{"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"},
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
int CmdLFEM4X70(const char *Cmd);
|
int CmdLFEM4X70(const char *Cmd);
|
||||||
int CmdEM4x70Info(const char *Cmd);
|
int CmdEM4x70Info(const char *Cmd);
|
||||||
int CmdEM4x70Write(const char *Cmd);
|
int CmdEM4x70Write(const char *Cmd);
|
||||||
|
int CmdEM4x70Brute(const char *Cmd);
|
||||||
int CmdEM4x70Unlock(const char *Cmd);
|
int CmdEM4x70Unlock(const char *Cmd);
|
||||||
int CmdEM4x70Auth(const char *Cmd);
|
int CmdEM4x70Auth(const char *Cmd);
|
||||||
int CmdEM4x70WritePIN(const char *Cmd);
|
int CmdEM4x70WritePIN(const char *Cmd);
|
||||||
|
|
|
@ -42,6 +42,9 @@ typedef struct {
|
||||||
// Used to write new key
|
// Used to write new key
|
||||||
uint8_t crypt_key[12];
|
uint8_t crypt_key[12];
|
||||||
|
|
||||||
|
// used for bruteforce the partial key
|
||||||
|
uint16_t start_key;
|
||||||
|
|
||||||
} em4x70_data_t;
|
} em4x70_data_t;
|
||||||
|
|
||||||
#endif /* EM4X70_H__ */
|
#endif /* EM4X70_H__ */
|
||||||
|
|
|
@ -484,6 +484,7 @@ typedef struct {
|
||||||
#define CMD_LF_EM4X70_AUTH 0x0263
|
#define CMD_LF_EM4X70_AUTH 0x0263
|
||||||
#define CMD_LF_EM4X70_WRITEPIN 0x0264
|
#define CMD_LF_EM4X70_WRITEPIN 0x0264
|
||||||
#define CMD_LF_EM4X70_WRITEKEY 0x0265
|
#define CMD_LF_EM4X70_WRITEKEY 0x0265
|
||||||
|
#define CMD_LF_EM4X70_BRUTE 0x0266
|
||||||
// Sampling configuration for LF reader/sniffer
|
// Sampling configuration for LF reader/sniffer
|
||||||
#define CMD_LF_SAMPLING_SET_CONFIG 0x021D
|
#define CMD_LF_SAMPLING_SET_CONFIG 0x021D
|
||||||
#define CMD_LF_FSK_SIMULATE 0x021E
|
#define CMD_LF_FSK_SIMULATE 0x021E
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue