From d9ec99f9033eeadc9f862e4a27fd947df092ecac Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 16 May 2024 22:49:24 +0200 Subject: [PATCH] found the bug in a call to hex2binarray() fct which overwrote first 16 bytes of keystream. Fixed loops. Crack2 now generates same data as RFIDLer impl. --- CHANGELOG.md | 1 + armsrc/hitag2.c | 16 +-- armsrc/hitag2_crack.c | 246 ++++++++-------------------------------- client/src/cmdlfhitag.c | 25 ++-- 4 files changed, 73 insertions(+), 215 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a88f1896a..3ca56628b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Fixed `lf hitag crack2` - now works. (@iceman1001) - Fixed wrong use of free() in desfire crypto on arm src, thanks @jlitewski! (@iceman1001) - Added `lf em 4x70 calc` - calculate `frn`/`grn` for a given `key` + `rnd` - Fixed `hf 15 dump` memory leaks (@jlitewski) diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index e9e8ef6c6..28982043d 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -120,7 +120,7 @@ static void hitag2_init(void) { #define HITAG_FRAME_LEN 20 #define HITAG_FRAME_BIT_COUNT (8 * HITAG_FRAME_LEN) #define HITAG_T_STOP 36 /* T_EOF should be > 36 */ -#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ +#define HITAG_T_LOW 6 /* T_LOW should be 4..10 */ #define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ #define HITAG_T_0 20 /* T[0] should be 18..22 */ #define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */ @@ -322,8 +322,6 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ // reader/writer // returns how long it took static uint32_t hitag_reader_send_bit(int bit) { - uint32_t wait = 0; - // Binary pulse length modulation (BPLM) is used to encode the data stream // This means that a transmission of a one takes longer than that of a zero @@ -331,8 +329,8 @@ static uint32_t hitag_reader_send_bit(int bit) { lf_modulation(true); // Wait for 4-10 times the carrier period - lf_wait_periods(8); // wait for 4-10 times the carrier period - wait += 8; + lf_wait_periods(HITAG_T_LOW); // wait for 4-10 times the carrier period + uint32_t wait = HITAG_T_LOW; // Disable modulation, just activates the field again lf_modulation(false); @@ -353,6 +351,7 @@ static uint32_t hitag_reader_send_bit(int bit) { // reader / writer commands // frame_len is in number of bits? static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) { + WDT_HIT(); uint32_t wait = 0; // Send the content of the frame @@ -360,6 +359,7 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); } + // Send EOF // Enable modulation, which means, drop the field lf_modulation(true); @@ -373,6 +373,7 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) // t_stop, high field for stop condition (> 36) lf_wait_periods(HITAG_T_STOP); wait += HITAG_T_STOP; + WDT_HIT(); return wait; } @@ -388,7 +389,7 @@ static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_l wait += hitag_reader_send_bit(frame[i]); } - // EOF + // Send EOF // Enable modulation, which means, drop the field // set GPIO_SSC_DOUT to HIGH lf_modulation(true); @@ -406,7 +407,6 @@ static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_l wait += HITAG_T_STOP; WDT_HIT(); - return wait; } @@ -2418,7 +2418,7 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start , uint8_t *tx, size_t txlen, bool send_bits) { // Tag specific configuration settings (sof, timings, etc.) HITAG2 Settings -#define T_WAIT_1_GUARD 8 +#define T_WAIT_1_GUARD 7 if (turn_on) { // Wait 50ms with field off to be sure the transponder gets reset diff --git a/armsrc/hitag2_crack.c b/armsrc/hitag2_crack.c index fa14faa2a..2c95dffa3 100644 --- a/armsrc/hitag2_crack.c +++ b/armsrc/hitag2_crack.c @@ -30,6 +30,7 @@ #include "cmd.h" #include "lfadc.h" +// iceman: I have never seen the tag respond with this. Might be something buffer in rfidler. const static uint8_t ERROR_RESPONSE[] = { 0xF4, 0x02, 0x88, 0x9C }; // #define READP0CMD "1100000111" @@ -37,8 +38,8 @@ const static uint8_t read_p0_cmd[] = {1, 1, 0, 0, 0, 0, 0, 1, 1, 1}; // hitag2crack_xor XORs the source with the pad to produce the target. // source, target and pad are binarrays of length len. -static void hitag2crack_xor(uint8_t *target, const uint8_t *source, const uint8_t *pad, uint16_t len) { - for (uint16_t i = 0; i < len; i++) { +static void hitag2crack_xor(uint8_t *target, const uint8_t *source, const uint8_t *pad, size_t len) { + for (size_t i = 0; i < len; i++) { target[i] = source[i] ^ pad[i]; } } @@ -168,14 +169,7 @@ static bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e // send extended encrypted cmd uint8_t resp[4] = {0}; if (hitag2crack_send_e_cmd(resp, nrar, e_ext_cmd, 40)) { - - // test if it was valid - if (memcmp(resp, ERROR_RESPONSE, 4)) { - return true; - } else { - DbpString("test enc-page0 cmd. got error-response"); - Dbhexdump(4, resp, false); - } + return true; } return false; } @@ -296,114 +290,13 @@ static bool hitag2crack_find_valid_e_cmd(uint8_t *e_cmd, uint8_t *nrar) { return false; } - - typedef struct { uint8_t keybits[2080]; uint8_t uid[32]; uint8_t nrar[64]; uint8_t e_ext_cmd[2080]; - uint8_t ext_cmd[2080]; } PACKED lf_hitag_crack2_t; -// hitag2crack_consume_keystream sends an extended command (up to 510 bits in -// length) to consume keystream. -// keybits is the binarray of keystream bits; -// kslen is the length of keystream; -// ksoffset is a pointer to the current keystream offset (updated by this fn); -// nrar is the 64 bit binarray of the nR aR pair. -//static bool ht2crack_consume_keystream(uint8_t *keybits, int kslen, int *ksoffset) { -/* -static bool ht2crack_consume_keystream(lf_hitag_crack2_t *c2, int kslen, int *ksoffset) { - - // calculate the length of keybits to consume with the extended command. - // 42 = 32 bit response + 10 bit command reserved for next command. conlen - // cannot be longer than 510 bits to fit into the small RWD buffer. - int conlen = kslen - *ksoffset - 42; - if (conlen < 10) { - DbpString("ht2crack_consume_keystream: conlen < 10"); - return false; - } - - // calculate how many repeated commands to send in this extended command. - int numcmds = conlen / 10; - - // xor extended cmd with keybits - hitag2crack_xor(c2->e_ext_cmd, c2->ext_cmd, c2->keybits + *ksoffset, (numcmds * 10)); - - // send encrypted command - size_t n = 0; - uint8_t resp[4]; - if (ht2_tx_rx(c2->e_ext_cmd, numcmds * 10, resp, &n, true, true) != PM3_SUCCESS) { - Dbprintf("ht2crack_consume_keystream: tx/rx cmd failed, got %zu", n); - return false; - } - - // test response - if (memcmp(resp, ERROR_RESPONSE, 4) == 0) { - DbpString("ht2crack_consume_keystream: got error response from card"); - return false; - } - - // dont bother decrypting the response - we already know the keybits - - // update ksoffset with command length and response - *ksoffset += (numcmds * 10) + 32; - - return true; -} -*/ - -// hitag2crack_extend_keystream sends an extended command to retrieve more keybits. -// keybits is the binarray of the keystream bits; -// kslen is a pointer to the current keybits length; -// ksoffset is the offset into the keybits array; -// nrar is the 64 bit binarray of the nR aR pair; -// uid is the 32 bit binarray of the UID. -//static bool ht2crack_extend_keystream(uint8_t *keybits, int *kslen, int ksoffset, uint8_t *nrar, uint8_t *uid) { -/* -static bool ht2crack_extend_keystream(lf_hitag_crack2_t *c2, int *kslen, int ksoffset) { - - // calc number of command iterations to send - int cmdlen = *kslen - ksoffset; - if (cmdlen < 10) { - DbpString("extend_keystream: cmdlen < 10"); - return false; - } - - int numcmds = cmdlen / 10; - - // xor extended cmd with keybits - hitag2crack_xor(c2->e_ext_cmd, c2->ext_cmd, c2->keybits + ksoffset, numcmds * 10); - - // send extended encrypted cmd - size_t n = 0; - uint8_t resp[4]; - if (ht2_tx_rx(c2->e_ext_cmd, numcmds * 10, resp, &n, true, true) != PM3_SUCCESS) { - DbpString("extend_keystream: tx/rx cmd failed"); - Dbhexdump(numcmds * 10, c2->e_ext_cmd, false); - return false; - } - - // test response - if (memcmp(resp, ERROR_RESPONSE, 4) == 0) { - return false; - } - - // convert response to binarray - uint8_t e_response[32]; - hex2binarray((char*)e_response, (char*)resp); - - // recover keystream from encrypted response - hitag2crack_xor(c2->keybits + ksoffset + (numcmds * 10), e_response, c2->uid, 32); - - // update kslen - *kslen = ksoffset + (numcmds * 10) + 32; - - return true; -} -*/ - // hitag2_crack implements the first crack algorithm described in the paper, // Gone In 360 Seconds by Verdult, Garcia and Balasch. // response is a multi-line text response containing the 8 pages of the cracked tag @@ -468,148 +361,103 @@ out: // nrar_hex is the 32 bit nR and aR in hex void ht2_crack2(uint8_t *nrar_hex) { + BigBuf_free(); + uint8_t *e_response = BigBuf_calloc(32); lf_hitag_crack2_t *c2 = (lf_hitag_crack2_t *)BigBuf_calloc(sizeof(lf_hitag_crack2_t)); - lf_hitag_crack_response_t *packet = (lf_hitag_crack_response_t *)BigBuf_calloc(sizeof(lf_hitag_crack_response_t)); g_logging = false; LEDsoff(); set_tracing(false); clear_trace(); - int res = PM3_SUCCESS; - // find the 'read page 0' command and recover key stream // get uid as hexstring uint8_t uid_hex[4]; if (ht2_read_uid(uid_hex, false, false, false) != PM3_SUCCESS) { - res = PM3_EFAILED; - goto out; + BigBuf_free(); + reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0); + return; } hex2binarray_n((char *)c2->uid, (char *)uid_hex, 4); hex2binarray_n((char *)c2->nrar, (char *)nrar_hex, 8); + DbpString("looking for encrypted command"); + // find a valid encrypted command uint8_t e_firstcmd[10]; if (hitag2crack_find_valid_e_cmd(e_firstcmd, c2->nrar) == false) { - res = PM3_EFAILED; - goto out; + BigBuf_free(); + reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0); + return; } + DbpString("looking for encrypted page 0"); + // find encrypted page0 commnd if (hitag2crack_find_e_page0_cmd(c2->keybits, e_firstcmd, c2->nrar, c2->uid) == false) { - res = PM3_EFAILED; - goto out; + BigBuf_free(); + reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0); + return; } - // Now we got 40 bits of keystream in c2->keybits. - + // We got 42 bits of keystream in c2->keybits. // using the 40 bits of keystream in keybits, sending commands with ever - // increasing lengths to acquire 2048 bits of key stream. + // increasing lengths to acquire 2048 bits of key stream. int kslen = 40; + int res = PM3_SUCCESS; - // build extended command - for (int i = 0; i < 208 ; i++) { - memcpy(c2->ext_cmd + (i * 10), read_p0_cmd, 10); - } + while (kslen < 2048 && BUTTON_PRESS() == false) { - DbpString("enter main keystream rec"); - Dbhexdump(160, c2->ext_cmd, false); + hitag2crack_xor(c2->e_ext_cmd, read_p0_cmd, c2->keybits, 10); + hitag2crack_xor(c2->e_ext_cmd + 10, read_p0_cmd, c2->keybits + 10, 10); + hitag2crack_xor(c2->e_ext_cmd + 20, read_p0_cmd, c2->keybits + 20, 10); + hitag2crack_xor(c2->e_ext_cmd + 30, read_p0_cmd, c2->keybits + 30, 10); - DbpString("enter main keystream recover loop"); - - while (kslen < 2048) { - - //int ksoffset = 0; + Dbprintf("Recovered " _YELLOW_("%i") " bits of keystream", kslen); // Get UID if (ht2_read_uid(NULL, true, false, true) != PM3_SUCCESS) { res = PM3_EFAILED; - goto out; + break; } // send nrar and receive (useless) encrypted page 3 value size_t n = 0; if (ht2_tx_rx(c2->nrar, 64, NULL, &n, true, true) != PM3_SUCCESS) { res = PM3_EFAILED; - goto out; - } - - // while we have at least 52 bits of keystream, consume it with - // extended read page 0 commands. - // 52 = 10 (min command len) + 32 (response) + 10 (min command len we'll send) - /* - while ((kslen - ksoffset) >= 52) { - // consume the keystream, updating ksoffset as we go - //if (ht2crack_consume_keystream(c2->keybits, kslen, &ksoffset, c2->nrar) == false) { - if (ht2crack_consume_keystream(c2, kslen, &ksoffset) == false) { - DbpString("ht2crack_consume_keystream failed"); - res = PM3_EFAILED; - goto out; - } - } - // send an extended command to retrieve more keystream, - // updating kslen as we go - if (ht2crack_extend_keystream(c2, &kslen, ksoffset) == false) { - DbpString("ht2crack_extend_keystream failed"); - res = PM3_EFAILED; - goto out; - } - - */ - - // xor extended cmd with keybits - hitag2crack_xor(c2->e_ext_cmd, c2->ext_cmd, c2->keybits, kslen); - - // send extended encrypted cmd - uint8_t resp[4]; - if (ht2_tx_rx(c2->e_ext_cmd, kslen, resp, &n, true, false) != PM3_SUCCESS) { - DbpString("extend_keystream: tx/rx cmd failed"); break; } - // test response - if (memcmp(resp, ERROR_RESPONSE, 4) == 0) { + uint8_t resp[4] = {0}; + res = ht2_tx_rx(c2->e_ext_cmd, kslen, resp, &n, true, false); + if (res != PM3_SUCCESS) { + Dbprintf("tx/rx failed, got %zu (res... %i)", n, res); break; } - // convert response to binarray - uint8_t e_response[32]; - hex2binarray((char *)e_response, (char *)resp); + // convert response to binarray + hex2binarray_n((char *)e_response, (char *)resp, 4); // recover keystream from encrypted response - hitag2crack_xor(c2->keybits + kslen + 40, e_response, c2->uid, 32); + hitag2crack_xor(c2->keybits + kslen, e_response, c2->uid, 32); - // update kslen - kslen += (40 + 32); - - Dbprintf("Recovered " _YELLOW_("%i") " bits of keystream", kslen); + // extented with 30 bits or 3 * 10 read_p0_cmds + hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10); + kslen += 10; + hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10); + kslen += 10; + hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10); + kslen += 10; } - /* - uint8_t *keybitshex = BigBuf_calloc(64); - for (int i = 0; i < 2048; i += 256) { - binarray2hex(c2->keybits + i, 256, keybitshex); - Dbhexdump(256, keybitshex, false); - } - */ - BigBuf_free(); + lf_hitag_crack_response_t *packet = (lf_hitag_crack_response_t *)BigBuf_calloc(sizeof(lf_hitag_crack_response_t)); - // copy UID since we already have it... - memcpy(packet->data, uid_hex, 4); packet->status = 1; - -out: - - /* - DbpString("keybits:"); - Dbhexdump(2080, c2->keybits, false); - DbpString("uid:"); - Dbhexdump(32, c2->uid, false); - DbpString("nrar:"); - Dbhexdump(64, c2->nrar, false); - */ + binarray2hex(c2->keybits, kslen, packet->data); reply_ng(CMD_LF_HITAG2_CRACK_2, res, (uint8_t *)packet, sizeof(lf_hitag_crack_response_t)); + BigBuf_free(); + return; } diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 193699b71..5f86d7643 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -2169,7 +2169,7 @@ static int CmdLFHitag2Lookup(const char *Cmd) { static int CmdLFHitag2Crack2(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf hitag lookup", + CLIParserInit(&ctx, "lf hitag crack2", "This command tries to recover 2048 bits of Hitag2 crypto stream data.\n", "lf hitag crack2 --nrar 73AA5A62EAB8529C" ); @@ -2196,7 +2196,7 @@ static int CmdLFHitag2Crack2(const char *Cmd) { memset(&packet, 0, sizeof(packet)); memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); - PrintAndLogEx(INFO, _YELLOW_("Hitag 2") " - Crack2 (NrAR)"); + PrintAndLogEx(INFO, _YELLOW_("Hitag 2") " - Nonce replay and length extension attack ( Crack2 )"); uint64_t t1 = msclock(); @@ -2205,23 +2205,32 @@ static int CmdLFHitag2Crack2(const char *Cmd) { SendCommandNG(CMD_LF_HITAG2_CRACK_2, (uint8_t *) &packet, sizeof(packet)); // loop - uint8_t attempt = 30; + uint8_t attempt = 50; do { - PrintAndLogEx(INPLACE, "Attack 2 running..."); - fflush(stdout); +// PrintAndLogEx(INPLACE, "Attack 2 running..."); +// fflush(stdout); if (WaitForResponseTimeout(CMD_LF_HITAG2_CRACK_2, &resp, 1000) == false) { attempt--; continue; } -// lf_hitag_crack_response_t *payload = (lf_hitag_crack_response_t *)resp.data.asBytes; if (resp.status == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, " ( %s )", _GREEN_("ok")); + + PrintAndLogEx(SUCCESS, "--------------------- " _CYAN_("Recovered Keystream") " ----------------------"); + lf_hitag_crack_response_t *payload = (lf_hitag_crack_response_t *)resp.data.asBytes; + + for (int i = 0; i < 256; i += 32) { + PrintAndLogEx(SUCCESS, "%s", sprint_hex_inrow(payload->data + i, 32)); + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Nonce replay and length extension attack ( %s )", _GREEN_("ok")); + PrintAndLogEx(HINT, "try running `tools/hitag2crack/crack2/ht2crack2search "); break; } else { - PrintAndLogEx(NORMAL, " ( %s )", _RED_("fail")); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Nonce replay and length extension attack ( %s )", _RED_("fail")); break; }