mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 21:33:47 -07:00
enhanced the hf iclass info command to try legacy aa1 and if found read out and decode
This commit is contained in:
parent
70581e3bfa
commit
c8fe7205b4
2 changed files with 136 additions and 23 deletions
|
@ -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...
|
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 tries default keys and decode if legacy (@iceman1001)
|
||||||
- Changed `hf iclass chk` - now loads dictionary file by default (@iceman1001)
|
- Changed `hf iclass chk` - now loads dictionary file by default (@iceman1001)
|
||||||
- Added an Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package
|
- Added an Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package
|
||||||
- Auto detect whether compile option `march=native` is supported for mfd_aes_brute Makefile
|
- Auto detect whether compile option `march=native` is supported for mfd_aes_brute Makefile
|
||||||
|
|
|
@ -126,6 +126,70 @@ typedef enum {
|
||||||
TRIPLEDES
|
TRIPLEDES
|
||||||
} BLOCK79ENCRYPTION;
|
} BLOCK79ENCRYPTION;
|
||||||
|
|
||||||
|
// 16 bytes key
|
||||||
|
static int iclass_load_transport(uint8_t *key, uint8_t n) {
|
||||||
|
size_t keylen = 0;
|
||||||
|
uint8_t *keyptr = NULL;
|
||||||
|
int res = loadFile_safeEx(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen, false);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(INFO, "Couldn't find any decryption methods");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keylen != 16) {
|
||||||
|
PrintAndLogEx(ERR, "Failed to load transport key from file");
|
||||||
|
free(keyptr);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keylen != n) {
|
||||||
|
PrintAndLogEx(ERR, "Array size mismatch");
|
||||||
|
free(keyptr);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key, keyptr, n);
|
||||||
|
free(keyptr);
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iclass_decrypt_transport(uint8_t *key, uint8_t limit, uint8_t *enc_data, uint8_t *dec_data, BLOCK79ENCRYPTION aa1_encryption) {
|
||||||
|
|
||||||
|
// tripledes
|
||||||
|
mbedtls_des3_context ctx;
|
||||||
|
mbedtls_des3_set2key_dec(&ctx, key);
|
||||||
|
|
||||||
|
bool decrypted_block789 = false;
|
||||||
|
for (uint8_t i = 0; i < limit; ++i) {
|
||||||
|
|
||||||
|
uint16_t idx = i * PICOPASS_BLOCK_SIZE;
|
||||||
|
|
||||||
|
switch (aa1_encryption) {
|
||||||
|
// Right now, only 3DES is supported
|
||||||
|
case TRIPLEDES:
|
||||||
|
// Decrypt block 7,8,9 if configured.
|
||||||
|
if (i > 6 && i <= 9 && memcmp(enc_data + idx, empty, PICOPASS_BLOCK_SIZE) != 0) {
|
||||||
|
mbedtls_des3_crypt_ecb(&ctx, enc_data + idx, dec_data + idx);
|
||||||
|
decrypted_block789 = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DES:
|
||||||
|
case RFU:
|
||||||
|
case None:
|
||||||
|
// Nothing to do for None anyway...
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decrypted_block789) {
|
||||||
|
// Set the 2 last bits of block6 to 0 to mark the data as decrypted
|
||||||
|
dec_data[(6 * PICOPASS_BLOCK_SIZE) + 7] &= 0xFC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_des3_free(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static inline uint32_t leadingzeros(uint64_t a) {
|
static inline uint32_t leadingzeros(uint64_t a) {
|
||||||
#if defined __GNUC__
|
#if defined __GNUC__
|
||||||
return __builtin_clzll(a);
|
return __builtin_clzll(a);
|
||||||
|
@ -1471,7 +1535,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
CLIParamStrToBuf(arg_get_str(clictx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
CLIParamStrToBuf(arg_get_str(clictx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||||
|
|
||||||
int enc_data_len = 0;
|
int enc_data_len = 0;
|
||||||
uint8_t enc_data[8] = {0};
|
uint8_t enc_data[PICOPASS_BLOCK_SIZE] = {0};
|
||||||
bool have_data = false;
|
bool have_data = false;
|
||||||
|
|
||||||
CLIGetHexWithReturn(clictx, 2, enc_data, &enc_data_len);
|
CLIGetHexWithReturn(clictx, 2, enc_data, &enc_data_len);
|
||||||
|
@ -1491,7 +1555,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (enc_data_len > 0) {
|
if (enc_data_len > 0) {
|
||||||
if (enc_data_len != 8) {
|
if (enc_data_len != PICOPASS_BLOCK_SIZE) {
|
||||||
PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 HEX symbols)");
|
PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 HEX symbols)");
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
|
@ -1554,7 +1618,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
// decrypt user supplied data
|
// decrypt user supplied data
|
||||||
if (have_data) {
|
if (have_data) {
|
||||||
|
|
||||||
uint8_t dec_data[8] = {0};
|
uint8_t dec_data[PICOPASS_BLOCK_SIZE] = {0};
|
||||||
if (use_sc) {
|
if (use_sc) {
|
||||||
Decrypt(enc_data, dec_data);
|
Decrypt(enc_data, dec_data);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1564,9 +1628,10 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
PrintAndLogEx(SUCCESS, "encrypted... %s", sprint_hex_inrow(enc_data, sizeof(enc_data)));
|
PrintAndLogEx(SUCCESS, "encrypted... %s", sprint_hex_inrow(enc_data, sizeof(enc_data)));
|
||||||
PrintAndLogEx(SUCCESS, "plain....... " _YELLOW_("%s"), sprint_hex_inrow(dec_data, sizeof(dec_data)));
|
PrintAndLogEx(SUCCESS, "plain....... " _YELLOW_("%s"), sprint_hex_inrow(dec_data, sizeof(dec_data)));
|
||||||
|
|
||||||
if (use_sc && use_decode6)
|
if (use_sc && use_decode6) {
|
||||||
DecodeBlock6(dec_data);
|
DecodeBlock6(dec_data);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// decrypt dump file data
|
// decrypt dump file data
|
||||||
if (have_file) {
|
if (have_file) {
|
||||||
|
@ -1582,13 +1647,13 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
uint8_t pages = 1;
|
uint8_t pages = 1;
|
||||||
getMemConfig(mem, chip, &app_areas, &kb, &books, &pages);
|
getMemConfig(mem, chip, &app_areas, &kb, &books, &pages);
|
||||||
|
|
||||||
BLOCK79ENCRYPTION aa1_encryption = (decrypted[(6 * 8) + 7] & 0x03);
|
BLOCK79ENCRYPTION aa1_encryption = (decrypted[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03);
|
||||||
|
|
||||||
uint8_t limit = MIN(applimit, decryptedlen / 8);
|
uint8_t limit = MIN(applimit, decryptedlen / 8);
|
||||||
|
|
||||||
if (decryptedlen / 8 != applimit) {
|
if (decryptedlen / PICOPASS_BLOCK_SIZE != applimit) {
|
||||||
PrintAndLogEx(WARNING, "Actual file len " _YELLOW_("%zu") " vs HID app-limit len " _YELLOW_("%u"), decryptedlen, applimit * 8);
|
PrintAndLogEx(WARNING, "Actual file len " _YELLOW_("%zu") " vs HID app-limit len " _YELLOW_("%u"), decryptedlen, applimit * PICOPASS_BLOCK_SIZE);
|
||||||
PrintAndLogEx(INFO, "Setting limit to " _GREEN_("%u"), limit * 8);
|
PrintAndLogEx(INFO, "Setting limit to " _GREEN_("%u"), limit * PICOPASS_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
//uint8_t numblocks4userid = GetNumberBlocksForUserId(decrypted + (6 * 8));
|
//uint8_t numblocks4userid = GetNumberBlocksForUserId(decrypted + (6 * 8));
|
||||||
|
@ -1596,14 +1661,14 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
bool decrypted_block789 = false;
|
bool decrypted_block789 = false;
|
||||||
for (uint8_t blocknum = 0; blocknum < limit; ++blocknum) {
|
for (uint8_t blocknum = 0; blocknum < limit; ++blocknum) {
|
||||||
|
|
||||||
uint16_t idx = blocknum * 8;
|
uint16_t idx = blocknum * PICOPASS_BLOCK_SIZE;
|
||||||
memcpy(enc_data, decrypted + idx, 8);
|
memcpy(enc_data, decrypted + idx, PICOPASS_BLOCK_SIZE);
|
||||||
|
|
||||||
switch (aa1_encryption) {
|
switch (aa1_encryption) {
|
||||||
// Right now, only 3DES is supported
|
// Right now, only 3DES is supported
|
||||||
case TRIPLEDES:
|
case TRIPLEDES:
|
||||||
// Decrypt block 7,8,9 if configured.
|
// Decrypt block 7,8,9 if configured.
|
||||||
if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, 8) != 0) {
|
if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, PICOPASS_BLOCK_SIZE) != 0) {
|
||||||
if (use_sc) {
|
if (use_sc) {
|
||||||
Decrypt(enc_data, decrypted + idx);
|
Decrypt(enc_data, decrypted + idx);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1622,7 +1687,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
|
|
||||||
if (decrypted_block789) {
|
if (decrypted_block789) {
|
||||||
// Set the 2 last bits of block6 to 0 to mark the data as decrypted
|
// Set the 2 last bits of block6 to 0 to mark the data as decrypted
|
||||||
decrypted[(6 * 8) + 7] &= 0xFC;
|
decrypted[(6 * PICOPASS_BLOCK_SIZE) + 7] &= 0xFC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1655,26 +1720,26 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
|
||||||
// decode block 6
|
// decode block 6
|
||||||
bool has_values = (memcmp(decrypted + (8 * 6), empty, 8) != 0) && (memcmp(decrypted + (8 * 6), zeros, 8) != 0);
|
bool has_values = (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 6), empty, 8) != 0) && (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 6), zeros, PICOPASS_BLOCK_SIZE) != 0);
|
||||||
if (has_values && use_sc) {
|
if (has_values && use_sc) {
|
||||||
DecodeBlock6(decrypted + (8 * 6));
|
DecodeBlock6(decrypted + (PICOPASS_BLOCK_SIZE * 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode block 7-8-9
|
// decode block 7-8-9
|
||||||
iclass_decode_credentials(decrypted);
|
iclass_decode_credentials(decrypted);
|
||||||
|
|
||||||
// decode block 9
|
// decode block 9
|
||||||
has_values = (memcmp(decrypted + (8 * 9), empty, 8) != 0) && (memcmp(decrypted + (8 * 9), zeros, 8) != 0);
|
has_values = (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 9), empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 9), zeros, PICOPASS_BLOCK_SIZE) != 0);
|
||||||
if (has_values && use_sc) {
|
if (has_values && use_sc) {
|
||||||
uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (8 * 6));
|
uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (PICOPASS_BLOCK_SIZE * 6));
|
||||||
if (usr_blk_len < 3) {
|
if (usr_blk_len < 3) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(INFO, "Block 9 decoder");
|
PrintAndLogEx(INFO, "Block 9 decoder");
|
||||||
|
|
||||||
uint8_t pinsize = GetPinSize(decrypted + (8 * 6));
|
uint8_t pinsize = GetPinSize(decrypted + (PICOPASS_BLOCK_SIZE * 6));
|
||||||
if (pinsize > 0) {
|
if (pinsize > 0) {
|
||||||
|
|
||||||
uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5);
|
uint64_t pin = bytes_to_num(decrypted + (PICOPASS_BLOCK_SIZE * 9), 5);
|
||||||
char tmp[17] = {0};
|
char tmp[17] = {0};
|
||||||
snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin));
|
snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin));
|
||||||
PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp);
|
PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp);
|
||||||
|
@ -2645,7 +2710,8 @@ static int CmdHFiClassRestore(const char *Cmd) {
|
||||||
return resp.status;
|
return resp.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, bool auth, bool shallow_mod, uint8_t *out) {
|
static int iclass_read_block_ex(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose,
|
||||||
|
bool auth, bool shallow_mod, uint8_t *out, bool print) {
|
||||||
|
|
||||||
iclass_auth_req_t payload = {
|
iclass_auth_req_t payload = {
|
||||||
.use_raw = rawkey,
|
.use_raw = rawkey,
|
||||||
|
@ -2681,16 +2747,24 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (print) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(SUCCESS, " block %3d/0x%02X : " _GREEN_("%s"), blockno, blockno, sprint_hex(packet->data, sizeof(packet->data)));
|
PrintAndLogEx(SUCCESS, " block %3d/0x%02X : " _GREEN_("%s"), blockno, blockno, sprint_hex(packet->data, sizeof(packet->data)));
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
}
|
||||||
|
|
||||||
if (out)
|
if (out) {
|
||||||
memcpy(out, packet->data, sizeof(packet->data));
|
memcpy(out, packet->data, sizeof(packet->data));
|
||||||
|
}
|
||||||
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose,
|
||||||
|
bool auth, bool shallow_mod, uint8_t *out) {
|
||||||
|
return iclass_read_block_ex(KEY, blockno, keyType, elite, rawkey, replay, verbose, auth, shallow_mod, out, true);
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHFiClass_ReadBlock(const char *Cmd) {
|
static int CmdHFiClass_ReadBlock(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf iclass rdbl",
|
CLIParserInit(&ctx, "hf iclass rdbl",
|
||||||
|
@ -5220,6 +5294,7 @@ int info_iclass(bool shallow_mod) {
|
||||||
|
|
||||||
iclass_card_select_resp_t *r = (iclass_card_select_resp_t *)resp.data.asBytes;
|
iclass_card_select_resp_t *r = (iclass_card_select_resp_t *)resp.data.asBytes;
|
||||||
|
|
||||||
|
uint8_t *p_response = (uint8_t*)&r->header.hdr;
|
||||||
// no tag found or button pressed
|
// no tag found or button pressed
|
||||||
if (r->status == FLAG_ICLASS_NULL || resp.status == PM3_ERFTRANS) {
|
if (r->status == FLAG_ICLASS_NULL || resp.status == PM3_ERFTRANS) {
|
||||||
return PM3_EOPABORTED;
|
return PM3_EOPABORTED;
|
||||||
|
@ -5304,5 +5379,42 @@ 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 (legacy) {
|
||||||
|
|
||||||
|
int res = PM3_ESOFT;
|
||||||
|
uint8_t key_type = 0x88; // debit key
|
||||||
|
|
||||||
|
uint8_t dump[PICOPASS_BLOCK_SIZE * 8] = {0};
|
||||||
|
// we take all raw bytes from response
|
||||||
|
memcpy(dump, p_response, sizeof(picopass_hdr_t));
|
||||||
|
|
||||||
|
uint8_t key[8] = {0};
|
||||||
|
for (uint8_t i = 0; i < ARRAYLEN(iClass_Key_Table); i++) {
|
||||||
|
|
||||||
|
memcpy(key, iClass_Key_Table[i], sizeof(key));
|
||||||
|
res = iclass_read_block_ex(key, 6, key_type, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 6), false);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, " AA1 Key...... " _GREEN_("%s"), sprint_hex_inrow(key, sizeof(key)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
res = iclass_read_block_ex(key, 7, key_type, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 7), false);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
|
||||||
|
BLOCK79ENCRYPTION aa1_encryption = (dump[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03);
|
||||||
|
|
||||||
|
uint8_t decrypted[PICOPASS_BLOCK_SIZE * 8] = {0};
|
||||||
|
memcpy(decrypted, dump, 7 * PICOPASS_BLOCK_SIZE);
|
||||||
|
|
||||||
|
uint8_t transport[16] = {0};
|
||||||
|
iclass_load_transport(transport, sizeof(transport));
|
||||||
|
iclass_decrypt_transport(transport, 8, dump, decrypted, aa1_encryption);
|
||||||
|
iclass_decode_credentials(decrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue