From c08e6c47c8cc536a807efcb8f383af60f9240b97 Mon Sep 17 00:00:00 2001 From: Jakub Kramarz Date: Sat, 4 Jan 2025 14:49:39 +0100 Subject: [PATCH] sam_seos: add suppport for HID SAM communication with SEOS cards, based on bettse/seader project --- armsrc/appmain.c | 2 +- armsrc/sam_seos.c | 350 +++++++++++++++++++++++++++++++++++++++++ armsrc/sam_seos.h | 2 + client/src/cmdhfseos.c | 123 ++++++++++++++- 4 files changed, 475 insertions(+), 2 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 6d42a0e0c..dbe929159 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -2246,7 +2246,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_SAM_SEOS: { -// sam_seos_get_pacs(); + sam_seos_get_pacs(); break; } diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index 00e4da45b..0fe5c8670 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -14,9 +14,359 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- // Routines to support SEOS <-> SAM communication +// communication and ASN.1 messages based on https://github.com/bettse/seader/blob/main/seader.asn1 //----------------------------------------------------------------------------- #include "sam_seos.h" +#include "sam_common.h" #include "iclass.h" #include "proxmark3_arm.h" +#include "iso14443a.h" + +#include "iclass.h" +#include "crc16.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" #include "cmd.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "protocols.h" +#include "optimized_cipher.h" +#include "fpgaloader.h" + +#include "cmd.h" + + +/** + * @brief Sets the card detected status for the SAM (Secure Access Module). + * + * This function informs that a card has been detected by the reader and + * initializes SAM communication with the card. + * + * @param card_select Pointer to the descriptor of the detected card. + * @return Status code indicating success or failure of the operation. + */ +static int sam_set_card_detected(iso14a_card_select_t * card_select){ + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) + DbpString("start sam_set_card_detected"); + + if(card_select ->uidlen != 4) + return PM3_EFAILED; + + uint8_t * response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { + 0xa0, (2+(2+(4*2 +2+4+2+1))), // <- SAM command + 0xad, (2+(4*2 +2+4+2+1)), // <- set detected card + 0xa0, (4*2 +2+4+2+1), + 0x80, 2, // <- protocol + 0x00, 0x02, // <- ISO14443A + 0x81, card_select->uidlen, // <- CSN + card_select->uid[0], card_select->uid[1], card_select->uid[2], card_select->uid[3], + 0x82, 2, // <- ATQA + card_select->atqa[0], card_select->atqa[1], + 0x83, 1, // <- SAK + card_select->sak + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 02 <- response + // 8a 00 <- empty response (accepted) + // 90 00 + + if(response[5] != 0xbd){ + if (g_dbglevel >= DBG_ERROR) + Dbprintf("Invalid SAM response"); + goto error; + }else{ + // uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + // if(sam_response_an == NULL){ + // if (g_dbglevel >= DBG_ERROR) + // Dbprintf("Invalid SAM response"); + // goto error; + // } + goto out; + } + error: + res = PM3_ESOFT; + + out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) + DbpString("end sam_set_card_detected"); + return res; +} + +/** + * @brief Copies the payload from an NFC buffer to a SAM buffer. + * + * Wraps received data from NFC into an ASN1 tree, so it can be transmitted to the SAM . + * + * @param sam_tx Pointer to the SAM transmit buffer. + * @param nfc_rx Pointer to the NFC receive buffer. + * @param nfc_len Length of the data to be copied from the NFC buffer. + * + * @return Length of SAM APDU to be sent. + */ +inline static uint16_t sam_seos_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t * nfc_rx, uint8_t nfc_len){ + // NFC resp: + // 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3 + + // SAM req: + // bd 1c + // a0 1a + // a0 18 + // 80 12 + // 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3 + // 81 02 + // 00 00 + + sam_tx[0] = 0xBD; + + sam_tx[2] = 0xA0; + + sam_tx[4] = 0xA0; + + sam_tx[6] = 0x80; + sam_tx[7] = nfc_len; + memcpy(sam_tx+8, nfc_rx, nfc_len); + + sam_tx[8+nfc_len] = 0x81; + sam_tx[9+nfc_len] = 0x02; + sam_tx[10+nfc_len] = 0x00; + sam_tx[11+nfc_len] = 0x00; + + // fix lengths + sam_tx[5] = 2 + nfc_len + 4; + sam_tx[3] = sam_tx[5] + 2; + sam_tx[1] = sam_tx[3] + 2; + return sam_tx[1] + 2; +} + +/** + * @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer. + * + * Unpacks data to be transmitted from ASN1 tree in APDU received from SAM. + * + * @param nfc_tx_buf Pointer to the buffer where the NFC transmit data will be stored. + * @param sam_rx_buf Pointer to the buffer containing the data received from the SAM. + * @return Length of NFC APDU to be sent. + */ +inline static uint16_t sam_seos_copy_payload_sam2nfc(uint8_t * nfc_tx_buf, uint8_t * sam_rx_buf){ + // SAM resp: + // c1 61 c1 00 00 + // a1 21 <- nfc command + // a1 1f <- nfc send + // 80 10 <- data + // 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00 + // 81 02 <- protocol + // 02 02 + // 82 02 <- timeout + // 01 2e + // 85 03 <- format + // 06 c0 00 + // 90 00 + + // NFC req: + // 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00 + + // copy data out of c1->a1>->a1->80 node + uint16_t nfc_tx_len = (uint8_t) *(sam_rx_buf + 10); + memcpy(nfc_tx_buf, sam_rx_buf+11, nfc_tx_len); + return nfc_tx_len; +} + +/** + * @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer. + * + * Unpacks data to be transmitted from ASN1 tree in APDU received from SAM. + * + * @param pacs Pointer to the buffer where the decoded PACS data will be stored. + * @param pacs_len Pointer to the variable where the length of the PACS data will be stored. + * @return Status code indicating success or failure of the operation. + */ +static int sam_request_pacs(uint8_t * pacs, uint8_t * pacs_len){ + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) + DbpString("start sam_request_pacs"); + + uint8_t buf1[ISO7816_MAX_FRAME] = {0}; + uint8_t buf2[ISO7816_MAX_FRAME] = {0}; + + uint8_t * sam_tx_buf = buf1; + uint16_t sam_tx_len; + + uint8_t * sam_rx_buf = buf2; + uint16_t sam_rx_len; + + uint8_t * nfc_tx_buf = buf1; + uint16_t nfc_tx_len; + + uint8_t * nfc_rx_buf = buf2; + uint16_t nfc_rx_len; + + // send get pacs + static const uint8_t payload[] = { + 0xa0, 5, // <- SAM command + 0xa1, 3, // <- get PACS + 0x80, 1, + 0x04 + }; + sam_tx_len = sizeof(payload); + memcpy(sam_tx_buf, payload, sam_tx_len); + + sam_send_payload( + 0x44, 0x0a, 0x44, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + // tag <-> SAM exchange starts here + for(int i = 0; i < 20; i++){ + switch_clock_to_countsspclk(); + nfc_tx_len = sam_seos_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + + nfc_rx_len = iso14_apdu( + nfc_tx_buf, + nfc_tx_len, + false, + nfc_rx_buf, + ISO7816_MAX_FRAME, + NULL + ); + + switch_clock_to_ticks(); + sam_tx_len = sam_seos_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len-2); + + sam_send_payload( + 0x14, 0x0a, 0x14, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + // last SAM->TAG + // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 + if(sam_rx_buf[7] == 0x82){ + // tag <-> SAM exchange ends here + break; + } + + } + + static const uint8_t hfack[] = { + 0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00 + }; + + sam_tx_len = sizeof(hfack); + memcpy(sam_tx_buf, hfack, sam_tx_len); + + sam_send_payload( + 0x14, 0x0a, 0x00, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + // resp: + // c1 64 00 00 00 + // bd 09 + // 8a 07 + // 03 05 <- include tag for pm3 client + // 06 85 80 6d c0 <- decoded PACS data + // 90 00 + if(sam_rx_buf[5+2] != 0x8a && sam_rx_buf[5+4] != 0x03){ + if (g_dbglevel >= DBG_ERROR) + Dbprintf("Invalid SAM response"); + goto err; + } + *pacs_len = sam_rx_buf[5+5] +2; + memcpy(pacs, sam_rx_buf+5+4, *pacs_len); + res=PM3_SUCCESS; + + goto out; + + err: + res=PM3_ESOFT; + out: + return res; +} + +/** + * @brief Retrieves PACS data from SEOS card using SAM. + * + * This function is called by appmain.c + * It sends a request to the SAM to get the PACS data from the SEOS card. + * The PACS data is then returned to the PM3 client. + * + * @return Status code indicating success or failure of the operation. + */ +int sam_seos_get_pacs(void){ + int res = PM3_EFAILED; + + clear_trace(); + I2C_Reset_EnterMainProgram(); + + set_tracing(true); + StartTicks(); + + // step 1: ping SAM + sam_get_version(); + + // step 2: get card information + iso14a_card_select_t card_a_info; + + // implicit StartSspClk() happens here + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + if (!iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)){ + goto err; + } + + switch_clock_to_ticks(); + + // step 3: SamCommand CardDetected + sam_set_card_detected(&card_a_info); + + // step 3: SamCommand RequestPACS, relay NFC communication + + uint8_t pacs[10] = { 0x00 }; + uint8_t pacs_len = 0; + res = sam_request_pacs(pacs, &pacs_len); + if(res != PM3_SUCCESS){ + goto err; + } + if (g_dbglevel >= DBG_INFO) + print_result("PACS data", pacs, pacs_len); + + sam_send_ack(); + + goto out; + goto off; + + err: + res = PM3_ENOPACS; + reply_ng(CMD_HF_SAM_SEOS, res, NULL, 0); + goto off; + out: + reply_ng(CMD_HF_SAM_SEOS, PM3_SUCCESS, pacs, pacs_len); + goto off; + off: + switch_off(); + set_tracing(false); + StopTicks(); + BigBuf_free(); + return res; +} \ No newline at end of file diff --git a/armsrc/sam_seos.h b/armsrc/sam_seos.h index c2100f07e..a1b637c80 100644 --- a/armsrc/sam_seos.h +++ b/armsrc/sam_seos.h @@ -18,4 +18,6 @@ #include "common.h" +int sam_seos_get_pacs(void); + #endif diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index fe57b7c76..74837134e 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -30,6 +30,9 @@ #include "ui.h" #include "cmdhf14a.h" // manufacture #include "protocols.h" // definitions of ISO14A/7816 protocol +#include "cardhelper.h" +#include "wiegand_formats.h" +#include "wiegand_formatutils.h" #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "crypto/asn1utils.h" // ASN1 decode / print #include "crypto/libpcrypto.h" // AES decrypt @@ -1632,10 +1635,129 @@ static int CmdHfSeosList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf seos", "seos -c"); } +static int CmdHfSeosSAM(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos sam", + "Extract PACS via a HID SAM\n", + "hf seos sam\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (IsHIDSamPresent(verbose) == false) { + return PM3_ESOFT; + } + + clearCommandBuffer(); + SendCommandNG(CMD_HF_SAM_SEOS, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_SAM_SEOS, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "SAM timeout"); + return PM3_ETIMEOUT; + } + + switch (resp.status) { + case PM3_SUCCESS: + break; + case PM3_ENOPACS: + PrintAndLogEx(SUCCESS, "No PACS data found. Card empty?"); + return resp.status; + default: + PrintAndLogEx(WARNING, "SAM select failed"); + return resp.status; + } + + // CSN, config, epurse, NR/MAC, AIA + // PACS + // first byte skip + // second byte length + // third padded + // fourth .. + uint8_t *d = resp.data.asBytes; + uint8_t n = d[1] - 1; // skip length byte + uint8_t pad = d[2]; + char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return PM3_EMALLOC; + } + + bytes_2_binstr(binstr, d + 3, n); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2)); + PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); + HIDTryUnpack(&packed); + + PrintAndLogEx(NORMAL, ""); + + if (strlen(binstr) >= 26 && verbose) { + + // iCLASS Legacy + PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); + PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // HID Prox II + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); + PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // MIFARE Classic + char mfcbin[28] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, binstr, strlen(binstr)); + binstr_2_bytes(hex, &hexlen, mfcbin); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); + PrintAndLogEx(SUCCESS, " hf mf eclr;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); + PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); + PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + } + free(binstr); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, + {"sam", CmdHfSeosSAM, IfPm3Smartcard, "SAM tests"}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, {"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"}, {"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"}, @@ -1643,7 +1765,6 @@ static command_t CommandTable[] = { {"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Utils") " -----------------------"}, {"managekeys", CmdHfSeosManageKeys, AlwaysAvailable, "Manage keys to use with SEOS commands"}, - {NULL, NULL, NULL, NULL} };