From 722a8aac8c9dec5cdaa2a1a272ba17763a64012f Mon Sep 17 00:00:00 2001 From: "Aaron Tulino (Aaronjamt)" Date: Mon, 22 Dec 2025 11:39:50 -0700 Subject: [PATCH 1/3] [hf seos] Simulate client support --- client/src/cmdhfseos.c | 114 +++++++++++++++++++++++++++++++++++++++++ client/src/cmdhfseos.h | 7 +-- client/src/cmdparser.c | 6 +++ client/src/cmdparser.h | 1 + 4 files changed, 122 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 915b131ae..ee9200c8b 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -1676,6 +1676,118 @@ static int CmdHfSeosADF(const char *Cmd) { return seos_pacs((char *)oid, oid_len, data_tag, data_tag_len, key_index); } +static int CmdHfSeosSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos sim", + "Simulate a SEOS card with the provided keys and data\n\n" + "By default:\n" + " - ADF OID : 2B0601040181E438010102011801010202\n" + " - Diversifier: 01020304050607\n" + " - Key Index : 2\n" + " - Data Tag : FF00\n" + " - Encryption : AES128\n" + " - Hashing : SHA256\n", + "hf seos sim -d 12345678\n" + "hf seos sim --ki 1\n" + "hf seos sim -o 2B0601040181E438010102011801010202 -u 01020304050607 --ki 2 -d 12345678\n" + "hf seos sim -o 2B0601040181E438010102011801010202 --legacy -t FF41 -d 12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("t", "tag", "", "<0-100> hex bytes for tag to simulate (Default: FF00)"), + arg_str0("o", "oid", "", "<0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)"), + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_str0("u", "diversifier", "", "<0-16> hex bytes for diversifier (Equivalent of UID)"), + arg_str0("d", "data", "", "<0-128> hex bytes for data (Must be valid BER-TLV)"), + arg_lit0("l", "legacy", "Use legacy algorithms (3DES/SHA1)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int data_tag_len = 0; + uint8_t data_tag[16] = {0xff, 0x00}; + CLIGetHexWithReturn(ctx, 1, data_tag, &data_tag_len); + + int oid_len = 0; + uint8_t oid[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02}; + CLIGetHexWithReturn(ctx, 2, oid, &oid_len); + + int key_index = arg_get_int_def(ctx, 3, 2); + + int diversifier_len = 0; + uint8_t diversifier[256] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + CLIGetHexWithReturn(ctx, 4, diversifier, &diversifier_len); + + int data_len = 0; + uint8_t data[256] = {}; + CLIGetHexWithReturn(ctx, 5, data, &data_len); + + uint8_t encryption_algorithm = SEOS_ENCRYPTION_AES; + uint8_t hashing_algorithm = SEOS_HASHING_SHA256; + if (arg_get_lit(ctx, 6)) { // legacy algorithms + encryption_algorithm = SEOS_ENCRYPTION_2K3DES, + hashing_algorithm = SEOS_HASHING_SHA1; + } + + CLIParserFree(ctx); + + // Fall back to default values + if (data_tag_len == 0) { + data_tag_len = 2; + } + if (oid_len == 0) { + oid_len = 17; + } + if (diversifier_len == 0) { + diversifier_len = 7; + } + + if (data_len == 0) { + PrintAndLogEx(ERR, "Data to simulate must be supplied"); + return PM3_ESOFT; + } + + srand(time(NULL)); + seos_emulate_req_t request = { + .encr_alg = encryption_algorithm, + .hash_alg = hashing_algorithm, + + .uid = {0x08, rand()%255, rand()%255, rand()%255}, + .uid_len = 4, + .diversifier_len = diversifier_len, + .data_tag_len = data_tag_len, + .data_len = data_len, + .oid_len = oid_len, + }; + + // Copy all the provided values into the request object + memcpy(request.privenc, keys[key_index].privEncKey, 16); + memcpy(request.privmac, keys[key_index].privMacKey, 16); + memcpy(request.authkey, keys[key_index].readKey, 16); + + memcpy(request.diversifier, diversifier, diversifier_len); + memcpy(request.data_tag, data_tag, data_tag_len); + memcpy(request.data, data, data_len); + memcpy(request.oid, oid, oid_len); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_SEOS_SIMULATE, (uint8_t*)&request, sizeof(request)); + + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort simulation"); + while (WaitForResponseTimeout(CMD_HF_SEOS_SIMULATE, &resp, 1000) == false) { + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard."); + // inform device to break the sim loop since client has exited + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_EOPABORTED; + } + } + + return PM3_SUCCESS; +} + static int CmdHfSeosManageKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf seos managekeys", @@ -1988,6 +2100,8 @@ static command_t CommandTable[] = { {"write", CmdHfSeosWrite, IfPm3Iso14443a, "Write an ADF to the card"}, {"adf", CmdHfSeosADF, IfPm3Iso14443a, "Read an ADF from the card"}, {"gdf", CmdHfSeosGDF, IfPm3Iso14443a, "Read an GDF from card"}, + {"-----------", CmdHelp, IfPm3Seos, "---------------------- " _CYAN_("Simulation") " ---------------------"}, + {"sim", CmdHfSeosSim, IfPm3Seos, "Simulate Seos tag"}, {"-----------", CmdHelp, AlwaysAvailable, "------------------------ " _CYAN_("Utils") " ------------------------"}, {"managekeys", CmdHfSeosManageKeys, AlwaysAvailable, "Manage keys to use with SEOS commands"}, {NULL, NULL, NULL, NULL} diff --git a/client/src/cmdhfseos.h b/client/src/cmdhfseos.h index 46ce3d32e..abc7f7239 100644 --- a/client/src/cmdhfseos.h +++ b/client/src/cmdhfseos.h @@ -20,13 +20,8 @@ #define CMDHFSEOS_H__ #include "common.h" +#include "seos_cmd.h" -#define SEOS_ENCRYPTION_2K3DES 0x02 -#define SEOS_ENCRYPTION_3K3DES 0x03 -#define SEOS_ENCRYPTION_AES 0x09 - -#define SEOS_HASHING_SHA1 0x06 -#define SEOS_HASHING_SHA256 0x07 int infoSeos(bool verbose); int CmdHFSeos(const char *Cmd); int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot, diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index fc628b947..16fbeba6c 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -182,6 +182,12 @@ bool IfPm3Iclass(void) { return g_pm3_capabilities.compiled_with_iclass; } +bool IfPm3Seos(void) { + if (IfPm3Present() == false) + return false; + return g_pm3_capabilities.compiled_with_seos; +} + bool IfPm3NfcBarcode(void) { if (IfPm3Present() == false) return false; diff --git a/client/src/cmdparser.h b/client/src/cmdparser.h index 34eee8bd5..4bfe71394 100644 --- a/client/src/cmdparser.h +++ b/client/src/cmdparser.h @@ -54,6 +54,7 @@ bool IfPm3Iso15693(void); bool IfPm3Felica(void); bool IfPm3Legicrf(void); bool IfPm3Iclass(void); +bool IfPm3Seos(void); bool IfPm3NfcBarcode(void); bool IfPm3Lcd(void); bool IfPm3Zx8211(void); From 17dfd50907ab8946312b5b6a9763089002bc33aa Mon Sep 17 00:00:00 2001 From: "Aaron Tulino (Aaronjamt)" Date: Mon, 22 Dec 2025 14:24:54 -0700 Subject: [PATCH 2/3] Update `CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 486a173a3..13aac3dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added ATR fingerprinting to `hf 14a/14b info` (@doegox) - Added `Verkada 40-bit` format (@aaronmaxlevy) - Added `hf seos write` command (@aaronjamt) +- Added `hf seos sim` command (@aaronjamt) ## [Phrack.4.20728][2025-09-11] - Added `unofficial desfire bible` document (@mistial-dev) From f1e6e49484725950f822d3d3b425ac301e0fd533 Mon Sep 17 00:00:00 2001 From: "Aaron Tulino (Aaronjamt)" Date: Mon, 22 Dec 2025 15:15:39 -0700 Subject: [PATCH 3/3] No more `rand()` --- client/src/cmdhfseos.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index ee9200c8b..5f538b763 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -1698,8 +1698,9 @@ static int CmdHfSeosSim(const char *Cmd) { arg_str0("t", "tag", "", "<0-100> hex bytes for tag to simulate (Default: FF00)"), arg_str0("o", "oid", "", "<0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)"), arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), - arg_str0("u", "diversifier", "", "<0-16> hex bytes for diversifier (Equivalent of UID)"), + arg_str0(NULL, "div", "", "<0-16> hex bytes for diversifier (Equivalent of UID)"), arg_str0("d", "data", "", "<0-128> hex bytes for data (Must be valid BER-TLV)"), + arg_str0("u", "uid", "", "<0-10> hex bytes for UID (Must be a RID i.e. [0]=0x08)"), arg_lit0("l", "legacy", "Use legacy algorithms (3DES/SHA1)"), arg_param_end }; @@ -1723,9 +1724,13 @@ static int CmdHfSeosSim(const char *Cmd) { uint8_t data[256] = {}; CLIGetHexWithReturn(ctx, 5, data, &data_len); + int uid_len = 0; + uint8_t uid[10] = {0x08, 0x01, 0x02, 0x03}; + CLIGetHexWithReturn(ctx, 6, uid, &uid_len); + uint8_t encryption_algorithm = SEOS_ENCRYPTION_AES; uint8_t hashing_algorithm = SEOS_HASHING_SHA256; - if (arg_get_lit(ctx, 6)) { // legacy algorithms + if (arg_get_lit(ctx, 7)) { // legacy algorithms encryption_algorithm = SEOS_ENCRYPTION_2K3DES, hashing_algorithm = SEOS_HASHING_SHA1; } @@ -1742,19 +1747,20 @@ static int CmdHfSeosSim(const char *Cmd) { if (diversifier_len == 0) { diversifier_len = 7; } + if (uid_len == 0) { + uid_len = 4; + } if (data_len == 0) { PrintAndLogEx(ERR, "Data to simulate must be supplied"); return PM3_ESOFT; } - srand(time(NULL)); seos_emulate_req_t request = { .encr_alg = encryption_algorithm, .hash_alg = hashing_algorithm, - .uid = {0x08, rand()%255, rand()%255, rand()%255}, - .uid_len = 4, + .uid_len = uid_len, .diversifier_len = diversifier_len, .data_tag_len = data_tag_len, .data_len = data_len, @@ -1766,6 +1772,7 @@ static int CmdHfSeosSim(const char *Cmd) { memcpy(request.privmac, keys[key_index].privMacKey, 16); memcpy(request.authkey, keys[key_index].readKey, 16); + memcpy(request.uid, uid, uid_len); memcpy(request.diversifier, diversifier, diversifier_len); memcpy(request.data_tag, data_tag, data_tag_len); memcpy(request.data, data, data_len);