From 7d8c9f3f6e0e213792c743de6e789f2d3ec404ff Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 27 Mar 2026 20:32:41 +0800 Subject: [PATCH] iClass SIO Parser Improved SIO Parsing capabilities based on @iceman1001 black hat talk --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 102 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80af9b1c0..f29158a7e 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] +- Improved SIO parsing for `hf iclass view` based on Iceman's "Dismantling the SEOS Protocol" talk (@antiklesys) - Added live fc/cn update to `hf iclass tagsim` refreshing the csn with each update (@antiklesys) - Added `--live` option to `hf iclass lookup` command to perform a live recovery of the reader's key by simulating a tag and running the lookup command against both standard and elite dictionaries (@antiklesys) - Added `hf iclass tagsim` command to quickly simulate an iclass card based on facility code and card number(@antiklesys) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index c457cdaec..7fab409e3 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4942,7 +4942,105 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le *sio_length = sio_start[1] + 2; } -// print ASN1 decoded array in TLV view +static void print_iclass_sio_decoded(uint8_t *sio, size_t sio_len) { + // Outer SEQUENCE already validated by detect_credential (short-form length, 0x30 tag) + if (sio_len < 4 || sio[0] != 0x30) { + return; + } + + uint8_t *p = sio + 2; + uint8_t *end = sio + sio_len; + + PrintAndLogEx(INFO, "-------------------- " _CYAN_("SIO - Insights") " -------------------------"); + + while (p + 2 <= end) { + uint8_t tag = *p++; + uint8_t len = *p++; + if (p + len > end) { + break; + } + uint8_t *val = p; + p += len; + + switch (tag) { + case 0x81: { + // [1] IMPLICIT - Credential Template OID + // For iClass SE / Seos, the first byte is a preamble (0x01) and the actual OID is the remaining 4 bytes + if (len == 5 && val[0] == 0x01) { + PrintAndLogEx(INFO, " Relative OID : " _YELLOW_("%s") " ( Detected preamble: %02X - SE/SEOS )", sprint_hex_inrow(val, 5), val[0]); + } else { + PrintAndLogEx(INFO, " Relative OID : " _YELLOW_("%s"), sprint_hex_inrow(val, len)); + } + break; + } + case 0x83: { + // [3] IMPLICIT - Tail present in SR and custom-keyed SE credentials + PrintAndLogEx(INFO, " OID Tail : " _YELLOW_("%s")" (SR / Custom-Keyed SE)", sprint_hex_inrow(val, len)); + break; + } + case 0xA6: { + // [6] CONSTRUCTED - Key Info + uint8_t *ip = val; + uint8_t *ie = val + len; + while (ip + 2 <= ie) { + uint8_t itag = *ip++; + uint8_t ilen = *ip++; + if (ip + ilen > ie) break; + uint8_t *ival = ip; + ip += ilen; + if (itag == 0x80) { + // Custom Key OID (used instead of Key Reference ID for custom-keyed credentials) + PrintAndLogEx(INFO, " Custom Key OID : " _YELLOW_("%s"), sprint_hex_inrow(ival, ilen)); + } else if (itag == 0x81 && ilen == 1) { + const char *ktype; + switch (ival[0]) { + case 0x00: ktype = "Elite keyed"; break; + case 0x01: ktype = "Standard keyed"; break; + default: ktype = "Custom keyed"; break; + } + PrintAndLogEx(INFO, " Key Reference ID : " _YELLOW_("0x%02X") " ( %s )", ival[0], ktype); + } else if (itag == 0x04 && ilen >= 1) { + uint8_t ctype = ival[ilen - 1]; + const char *cname; + switch (ctype) { + case 0x08: cname = "EAX"; break; + case 0x09: cname = "EAX'"; break; + default: cname = "Unknown"; break; + } + PrintAndLogEx(INFO, " Crypto Suite : " _YELLOW_("%s") " ( %s )", sprint_hex_inrow(ival, ilen), cname); + } + } + break; + } + case 0xA7: { + // [7] CONSTRUCTED - PACS Encrypted Payload + uint8_t *ip = val; + uint8_t *ie = val + len; + while (ip + 2 <= ie) { + uint8_t itag = *ip++; + uint8_t ilen = *ip++; + if (ip + ilen > ie) break; + uint8_t *ival = ip; + ip += ilen; + if (itag == 0x85) { + if (ilen > 16) { + size_t data_len = ilen - 16; + PrintAndLogEx(INFO, " PACS Encrypted Data: " _YELLOW_("%s"), sprint_hex_inrow(ival, data_len)); + PrintAndLogEx(INFO, " PACS MAC Signature : " _YELLOW_("%s"), sprint_hex_inrow(ival + data_len, 16)); + } else if (ilen > 0) { + PrintAndLogEx(INFO, " PACS Payload : " _YELLOW_("%s"), sprint_hex_inrow(ival, ilen)); + } + } + } + break; + } + default: + break; + } + } +} + +// print SIO decoded fields and optionally raw ASN1 TLV void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) { bool is_legacy, is_se, is_sr; @@ -4964,6 +5062,8 @@ void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) { PrintAndLogEx(INFO, "--------------------------- " _CYAN_("SIO - RAW") " -----------------------------"); print_hex_noascii_break(sio_start, sio_length, 32); PrintAndLogEx(NORMAL, ""); + print_iclass_sio_decoded(sio_start, sio_length); + PrintAndLogEx(NORMAL, ""); if (verbose) { PrintAndLogEx(INFO, "----------------------- " _CYAN_("SIO - ASN1 TLV") " ---------------------------"); asn1_print(sio_start, sio_length, " ");