From a2a31423555aa5d74a869404005d1ddeb6783fa3 Mon Sep 17 00:00:00 2001 From: nvx Date: Wed, 23 Aug 2023 09:27:05 +1000 Subject: [PATCH] Improve iClass SIO and legacy credential detection to improve reliability. Now relies on the legacy config block for SR detection and the end-of-SIO detection no longer partially cuts off the SIO for any dumps I have. --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 109 ++++++++++++++++++++++----------------- client/src/util.c | 19 +++++++ client/src/util.h | 1 + doc/commands.json | 4 +- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06254cec20..b1bd9deb53 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 iClass SIO and Legacy credential detection to be more reliable (@nvx) - Added `hf iclass esetblk` - set iClass emulator memory block data (@nvx) - Added cryptorf regressiontests (@iceman1001) - Fixed `cryptorf/sma_multi` - local state used in multithread (@iceman1001) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 92df710461..5204fc4881 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1266,6 +1266,11 @@ static int CmdHFiClassESetBlk(const char *Cmd) { } static void iclass_decode_credentials(uint8_t *data) { + if (memcmp(data + (5 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + // Not a Legacy or SR card, nothing to do here. + return; + } + BLOCK79ENCRYPTION encryption = (data[(6 * 8) + 7] & 0x03); bool has_values = (memcmp(data + (8 * 7), empty, 8) != 0) && (memcmp(data + (8 * 7), zeros, 8) != 0); if (has_values && encryption == None) { @@ -1290,7 +1295,7 @@ static void iclass_decode_credentials(uint8_t *data) { wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); HIDTryUnpack(&packed); } else { - PrintAndLogEx(INFO, "No credential found"); + PrintAndLogEx(INFO, "No unencrypted legacy credential found"); } } @@ -1515,23 +1520,19 @@ static int CmdHFiClassDecrypt(const char *Cmd) { // decode block 9 has_values = (memcmp(decrypted + (8 * 9), empty, 8) != 0) && (memcmp(decrypted + (8 * 9), zeros, 8) != 0); - if (has_values) { - + if (has_values && use_sc) { uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (8 * 6)); if (usr_blk_len < 3) { - if (use_sc) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Block 9 decoder"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Block 9 decoder"); - - uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); - if (pinsize > 0) { + uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); + if (pinsize > 0) { - uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); - char tmp[17] = {0}; - snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin)); - PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); - } + uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); + char tmp[17] = {0}; + snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin)); + PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); } } } @@ -2628,50 +2629,60 @@ static int CmdHFiClass_loclass(const char *Cmd) { } static void detect_credential(uint8_t *data, bool *legacy, bool *se, bool *sr) { - bool r1 = !memcmp(data + (5 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); - - uint8_t pattern_se[] = {0x05, 0x00}; - bool r2 = byte_strstr(data + (6 * 8), 6 * 8, pattern_se, sizeof(pattern_se)) != -1; + *legacy = false; + *sr = false; + *se = false; + + // Legacy AIA + if (!memcmp(data + (5 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + *legacy = true; + + // SR bit set in legacy config block + if ((data[6 * PICOPASS_BLOCK_SIZE] & 0xA0) == 0xA0) { + // If the card is blank (all FF's) then we'll reach here too, so check for an empty block 10 + // to avoid false positivies + if (memcmp(data + (10 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + *sr = true; + } + } - uint8_t pattern_sr[] = {0x05, 0x00, 0x05, 0x00}; - bool r3 = byte_strstr(data + (11 * 8), 6 * 8, pattern_sr, sizeof(pattern_sr)) != -1; + return; + } - *legacy = (r1) && (data[6 * 8] != 0x30); - *se = (r2) && (data[6 * 8] == 0x30); - *sr = (r3) && (data[10 * 8] == 0x30); - r1 = NULL, r2 = NULL, r3 = NULL; + // SE AIA + if (!memcmp(data + (5 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + *se = true; + return; + } } // print ASN1 decoded array in TLV view static void printIclassSIO(uint8_t *iclass_dump) { - bool isLegacy, isSE, isSR; detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); - int dlen = 0; uint8_t *sio_start; if (isSE) { - - sio_start = iclass_dump + (6 * 8); - uint8_t pattern_se[] = {0x05, 0x00}; - dlen = byte_strstr(sio_start, 8 * 8, pattern_se, sizeof(pattern_se)); - if (dlen == -1) { - return; - } - dlen += sizeof(pattern_se); + // SE SIO starts at block 6 + sio_start = iclass_dump + (6 * PICOPASS_BLOCK_SIZE); } else if (isSR) { - - sio_start = iclass_dump + (10 * 8); - uint8_t pattern_sr[] = {0x05, 0x00, 0x05, 0x00}; - dlen = byte_strstr(sio_start, 8 * 8, pattern_sr, sizeof(pattern_sr)); - if (dlen == -1) { - return; - } - dlen += sizeof(pattern_sr); + // SR SIO starts at block 10 + sio_start = iclass_dump + (10 * PICOPASS_BLOCK_SIZE); } else { + // No SIO on Legacy credentials + return; + } + + // Readers assume the SIO always fits within 7 blocks (they don't read any further blocks) + // Search backwards to find the last 0x05 0x00 seen at the end of the SIO + const uint8_t pattern_sio_end[] = {0x05, 0x00}; + int dlen = byte_strrstr(sio_start, 7 * PICOPASS_BLOCK_SIZE, pattern_sio_end, 2); + if (dlen == -1) { return; } + dlen += sizeof(pattern_sio_end); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------"); print_hex_noascii_break(sio_start, dlen, 32); @@ -2702,8 +2713,15 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e else maxmemcount = 31; - if (startblock == 0) - startblock = 6; + uint8_t pagemap = get_pagemap(hdr); + + if (startblock == 0) { + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + startblock = 3; + } else { + startblock = 6; + } + } if ((endblock > maxmemcount) || (endblock == 0)) endblock = maxmemcount; @@ -2721,7 +2739,6 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e , filemaxblock ); */ - uint8_t pagemap = get_pagemap(hdr); bool isLegacy = false, isSE = false, isSR = false; if (filemaxblock >= 17) { @@ -2886,7 +2903,7 @@ static int CmdHFiClassView(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), - arg_int0(NULL, "first", "", "Begin printing from this block (default block 6)"), + arg_int0(NULL, "first", "", "Begin printing from this block (default first user block - 6 or 3 on non secured chips)"), arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), arg_lit0("z", "dense", "dense dump output style"), diff --git a/client/src/util.c b/client/src/util.c index f1e58abe02..30a96a3dc1 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1289,6 +1289,25 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ return -1; } +int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { + for (int i = srclen - plen; i >= 0; i--) { + // compare only first byte + if (src[i] != pattern[0]) + continue; + + // try to match rest of the pattern + for (int j = plen - 1; j >= 1; j--) { + + if (src[i + j] != pattern[j]) + break; + + if (j == 1) + return i; + } + } + return -1; +} + void sb_append_char(smartbuf *sb, unsigned char c) { if (sb->idx >= sb->size) { sb->size *= 2; diff --git a/client/src/util.h b/client/src/util.h index fa636ce9b9..cfaf97167a 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -155,6 +155,7 @@ uint32_t leadingzeros32(uint32_t a); uint64_t leadingzeros64(uint64_t a); int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen); +int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen); struct smartbuf { char *ptr; diff --git a/doc/commands.json b/doc/commands.json index 12509b1e78..cf96b6895a 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -3403,7 +3403,7 @@ "options": [ "-h, --help This help", "-f, --file filename of dump (bin/eml/json)", - "--first Begin printing from this block (default block 6)", + "--first Begin printing from this block (default first user block - 6 or 3 on non secured chips)", "--last End printing at this block (default 0, ALL)", "-v, --verbose verbose output", "-z, --dense dense dump output style" @@ -11834,6 +11834,6 @@ "metadata": { "commands_extracted": 686, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2023-08-22T17:13:49" + "extracted_on": "2023-08-22T23:15:58" } } \ No newline at end of file