From 83fccbafac1fd27639ecee387cfe5698ddb95622 Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 08:29:09 -0700 Subject: [PATCH 1/5] Add custom CTF wiegand format --- CHANGELOG.md | 1 + client/src/wiegand_formats.c | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e3a63a3..5c55318a6 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] +- Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) - Fixed `hf mf wipe` to detect properly write errors (@doegox) diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index eddf28621..6f022a2ba 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -20,6 +20,69 @@ #include "commonutil.h" +static bool Pack_Defcon32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + if (card->FacilityCode > 0x00FFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x0fffff) return false; // Can't encode CN. + if (card->IssueLevel > 0x00000F) return false; // Can't encode Issue + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 42; + /* + By implementing this format I hope to make the CTF easier for people to get into next year + //~~The wiegand data consists of 3 32 bit units that we need to split the data between Bottom and Mid since we have a 42 bit format~~ + We can use the set linear field function instead this seems to be easier. + |Mid part| | Bot part of the packed data | + PFFFFFFFFF FFFFFFFIIIICCCCCCCCCCCCCCCCCCCCP + 1111111111 11111111111000000000000000001000 +FC 111111111 1111111 = FF FF +//FC Mid 111111111 0000000 = FF 80 These where used to split data between bot/mid +//FC Bot 000000000 1111111 = 00 7F +Issuance 1111 = 0F +Card Number 11111111111111111111 = 0FFFFF + + */ + + // Referenced from MSB + set_linear_field(packed, card->CardNumber, 21, 20); // 20 bit + set_linear_field(packed, card->IssueLevel, 17, 4); // 4 bit + set_linear_field(packed, card->FacilityCode, 1, 16); // 16 bits + + // Parity calc + //0123456789|0123456789|0123456789|0123456789|01 + //E E E E E |E E E E E |EO O O O O| O O O O O| O + set_bit_by_position(packed, + evenparity32( + get_nonlinear_field(packed, 16, (uint8_t[]) {2, 4, 6, 8, 10, 12, 14, 16, 18, 20})) + , 0); + + set_bit_by_position(packed, + oddparity32( + get_nonlinear_field(packed, 16, (uint8_t[]) {21, 23, 25, 27, 29, 31, 33, 35, 37, 39})) + , 41); + if (preamble) + return add_HID_header(packed); + return true; +} + +static bool Unpack_Defcon32(wiegand_message_t *packed, wiegand_card_t *card) { + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 42) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 16); + card->IssueLevel = get_linear_field(packed, 17, 4); + card->CardNumber = get_linear_field(packed, 21, 20); + + card->ParityValid = + (get_bit_by_position(packed, 41) == oddparity32( + get_nonlinear_field(packed, 16, (uint8_t[]) {21, 23, 25, 27, 29, 31, 33, 35, 37, 39})))&& + (get_bit_by_position(packed, 0) == + evenparity32(get_nonlinear_field(packed, 16, (uint8_t[]) {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}))); + return true; +} + + static bool Pack_H10301(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); @@ -1474,6 +1537,7 @@ static const cardformat_t FormatTable[] = { {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", {1, 1, 0, 0, 1}}, + {"Defcon32", Pack_Defcon32, Unpack_Defcon32, "Custom Defcon RFCTF 42 BIT format", {1, 1, 1, 0, 1}}, // Created by (@micsen) for the CTF {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array }; From 22dc41f4ca5234e12ff83ed50842c21819c60f9d Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 08:29:45 -0700 Subject: [PATCH 2/5] Add issuance level encoding capability to iclass encode command --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c55318a6..beb4f5473 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] +- Added --issue to hf iclass encode command (@micsen) - Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 11b3712b4..5d5a30681 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4595,6 +4595,7 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), + arg_u64_0(NULL, "issue", "", "issue level"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4635,6 +4636,7 @@ static int CmdHFiClassEncode(const char *Cmd) { memset(&card, 0, sizeof(wiegand_card_t)); card.FacilityCode = arg_get_u32_def(ctx, 7, 0); card.CardNumber = arg_get_u32_def(ctx, 8, 0); + card.IssueLevel = arg_get_u32_def(ctx, 12, 0); char format[16] = {0}; int format_len = 0; From 9798071ec9493bc83d7d1df1ff412ed1f365ac1b Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 08:43:28 -0700 Subject: [PATCH 3/5] Move argument to where it belongs in the list of args and not just tacked at the bottom --- client/src/cmdhficlass.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 5d5a30681..6ec4fd487 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4592,10 +4592,10 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_str0(NULL, "enckey", "", "3DES transport key, 16 hex bytes"), arg_u64_0(NULL, "fc", "", "facility code"), arg_u64_0(NULL, "cn", "", "card number"), + arg_u64_0(NULL, "issue", "", "issue level"), arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), - arg_u64_0(NULL, "issue", "", "issue level"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4636,14 +4636,14 @@ static int CmdHFiClassEncode(const char *Cmd) { memset(&card, 0, sizeof(wiegand_card_t)); card.FacilityCode = arg_get_u32_def(ctx, 7, 0); card.CardNumber = arg_get_u32_def(ctx, 8, 0); - card.IssueLevel = arg_get_u32_def(ctx, 12, 0); + card.IssueLevel = arg_get_u32_def(ctx, 9, 0); char format[16] = {0}; int format_len = 0; - CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)format, sizeof(format), &format_len); + CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)format, sizeof(format), &format_len); - bool shallow_mod = arg_get_lit(ctx, 10); - bool verbose = arg_get_lit(ctx, 11); + bool shallow_mod = arg_get_lit(ctx, 11); + bool verbose = arg_get_lit(ctx, 12); CLIParserFree(ctx); From bebd1bb654b47483b8eb949107cc79a8c1ef07bc Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 10:31:01 -0700 Subject: [PATCH 4/5] WIP: Adding --emu functionality to `hf iclass encode` For some reason it wipes the entire emu memory --- client/src/cmdhficlass.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 6ec4fd487..b4e096998 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4594,6 +4594,7 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_u64_0(NULL, "cn", "", "card number"), arg_u64_0(NULL, "issue", "", "issue level"), arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), + arg_lit0(NULL, "emu", "Write to emulation memory instead of card"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), arg_param_end @@ -4640,10 +4641,12 @@ static int CmdHFiClassEncode(const char *Cmd) { char format[16] = {0}; int format_len = 0; + CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)format, sizeof(format), &format_len); - bool shallow_mod = arg_get_lit(ctx, 11); - bool verbose = arg_get_lit(ctx, 12); + bool use_emulator_memory = arg_get_lit(ctx, 11); + bool shallow_mod = arg_get_lit(ctx, 12); + bool verbose = arg_get_lit(ctx, 13); CLIParserFree(ctx); @@ -4776,15 +4779,20 @@ static int CmdHFiClassEncode(const char *Cmd) { int isok = PM3_SUCCESS; // write - for (uint8_t i = 0; i < 4; i++) { - isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); - switch (isok) { - case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); - break; - default: - PrintAndLogEx(INFO, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); - break; + if (use_emulator_memory) { + uint16_t bytes_sent = 0; + iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &bytes_sent); + } else { + for (uint8_t i = 0; i < 4; i++) { + isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); + switch (isok) { + case PM3_SUCCESS: + PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); + break; + default: + PrintAndLogEx(INFO, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); + break; + } } } return isok; From 8c9739d37b9fae45bc9369a5d79219459df1698d Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 11:41:42 -0700 Subject: [PATCH 5/5] Add emulator support for hf iclass encode with --emu flag --- CHANGELOG.md | 2 +- client/src/cmdhficlass.c | 48 +++++++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index beb4f5473..b3163be38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +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] -- Added --issue to hf iclass encode command (@micsen) +- Added --issue and (--emu)lator support to `hf iclass encode` command (@micsen) - Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b4e096998..980b930a9 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4579,13 +4579,15 @@ static int CmdHFiClassEncode(const char *Cmd) { "Use either --bin or --wiegand/--fc/--cn", "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)\n" "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)\n" - "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key" + "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key\n" + "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory\n" + "When using emulator you have to first load a credential into emulator memory" ); void *argtable[] = { arg_param_begin, arg_str0(NULL, "bin", "", "Binary string i.e 0001001001"), - arg_int1(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), @@ -4607,19 +4609,29 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIGetStrWithReturn(ctx, 1, bin, &bin_len); int key_nr = arg_get_int_def(ctx, 2, -1); - bool auth = false; + bool use_emulator_memory = arg_get_lit(ctx, 11); + bool auth = false; uint8_t key[8] = {0}; - if (key_nr >= 0) { - if (key_nr < ICLASS_KEYS_MAX) { - auth = true; - memcpy(key, iClass_Key_Table[key_nr], 8); - PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); - } else { - PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); + + // If we use emulator memory skip key requirement + if (!use_emulator_memory) { + if (key_nr < 0) { + PrintAndLogEx(ERR, "Missing required arg for --ki or --emu"); return PM3_EINVARG; } + + if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } } bool use_credit_key = arg_get_lit(ctx, 3); @@ -4644,7 +4656,6 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)format, sizeof(format), &format_len); - bool use_emulator_memory = arg_get_lit(ctx, 11); bool shallow_mod = arg_get_lit(ctx, 12); bool verbose = arg_get_lit(ctx, 13); @@ -4674,7 +4685,12 @@ static int CmdHFiClassEncode(const char *Cmd) { } if (have_enc_key == false) { - use_sc = IsCardHelperPresent(false); + // The IsCardHelperPresent function clears the emulator memory + if (use_emulator_memory) { + use_sc = false; + } else { + use_sc = IsCardHelperPresent(false); + } if (use_sc == false) { size_t keylen = 0; int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&enckeyptr, &keylen); @@ -4780,8 +4796,10 @@ static int CmdHFiClassEncode(const char *Cmd) { int isok = PM3_SUCCESS; // write if (use_emulator_memory) { - uint16_t bytes_sent = 0; - iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &bytes_sent); + uint16_t byte_sent = 0; + iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &byte_sent); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", byte_sent); + PrintAndLogEx(HINT, "You are now ready to simulate. See " _YELLOW_("`hf iclass sim -h`")); } else { for (uint8_t i = 0; i < 4; i++) { isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod);