diff --git a/CHANGELOG.md b/CHANGELOG.md index 135277e7..0fbd447a 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] + - Add per-slot fuzzing-mode setting to randomize data at each read for MF - Added command to check keys of multiple sectors at once (@taichunmin) - Fixed unused target key type parameter for nested (@petepriority) - Skip already used items `hf mf elog --decrypt` (@p-l-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 8a7e9ab1..f4fe81e9 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -756,6 +756,19 @@ static data_frame_tx_t *cmd_processor_mf1_get_detection_log(uint16_t cmd, uint16 return data_frame_make(cmd, STATUS_SUCCESS, length, resp); } +static data_frame_tx_t *cmd_processor_mf1_set_mode_fuzzing(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length != 1 || data[0] > 1) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); + } + nfc_tag_mf1_set_mode_fuzzing(data[0]); + return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL); +} + +static data_frame_tx_t *cmd_processor_mf1_get_mode_fuzzing(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + uint8_t is_fuzzing = nfc_tag_mf1_is_mode_fuzzing(); + return data_frame_make(cmd, STATUS_SUCCESS, 1, (uint8_t *)(&is_fuzzing)); +} + static data_frame_tx_t *cmd_processor_mf1_write_emu_block_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { if (length == 0 || (((length - 1) % NFC_TAG_MF1_DATA_SIZE) != 0)) { return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); @@ -880,13 +893,14 @@ static data_frame_tx_t *cmd_processor_delete_slot_tag_nick(uint16_t cmd, uint16_ } static data_frame_tx_t *cmd_processor_mf1_get_emulator_config(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { - uint8_t mf1_info[5] = {}; + uint8_t mf1_info[6] = {}; mf1_info[0] = nfc_tag_mf1_is_detection_enable(); mf1_info[1] = nfc_tag_mf1_is_gen1a_magic_mode(); mf1_info[2] = nfc_tag_mf1_is_gen2_magic_mode(); mf1_info[3] = nfc_tag_mf1_is_use_mf1_coll_res(); mf1_info[4] = nfc_tag_mf1_get_write_mode(); - return data_frame_make(cmd, STATUS_SUCCESS, 5, mf1_info); + mf1_info[5] = nfc_tag_mf1_is_mode_fuzzing(); + return data_frame_make(cmd, STATUS_SUCCESS, 6, mf1_info); } static data_frame_tx_t *cmd_processor_mf1_get_gen1a_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { @@ -1109,6 +1123,8 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_MF1_GET_WRITE_MODE, NULL, cmd_processor_mf1_get_write_mode, NULL }, { DATA_CMD_MF1_SET_WRITE_MODE, NULL, cmd_processor_mf1_set_write_mode, NULL }, { DATA_CMD_HF14A_GET_ANTI_COLL_DATA, NULL, cmd_processor_hf14a_get_anti_coll_data, NULL }, + { DATA_CMD_MF1_SET_MODE_FUZZING, NULL, cmd_processor_mf1_set_mode_fuzzing, NULL }, + { DATA_CMD_MF1_GET_MODE_FUZZING, NULL, cmd_processor_mf1_get_mode_fuzzing, NULL }, { DATA_CMD_EM410X_SET_EMU_ID, NULL, cmd_processor_em410x_set_emu_id, NULL }, { DATA_CMD_EM410X_GET_EMU_ID, NULL, cmd_processor_em410x_get_emu_id, NULL }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index 10313b16..b2518321 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -106,6 +106,8 @@ #define DATA_CMD_MF1_GET_WRITE_MODE (4016) #define DATA_CMD_MF1_SET_WRITE_MODE (4017) #define DATA_CMD_HF14A_GET_ANTI_COLL_DATA (4018) +#define DATA_CMD_MF1_SET_MODE_FUZZING (4019) +#define DATA_CMD_MF1_GET_MODE_FUZZING (4020) // // ****************************************************************** diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c index 02776923..d3fa9220 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c @@ -716,6 +716,12 @@ void nfc_tag_mf1_state_handler(uint8_t *p_data, uint16_t szDataBits) { memcpy(respTrailerInfo->key_b, m_tag_trailer_info->key_b, 6); } } else { + /* If tag is marked as fuzzer: randomize data for next read */ + if (m_tag_information->config.mode_fuzzing) { + for (uint8_t i = 0; i < 16; i++) { + m_tag_information->memory[CurrentAddress][i] = (uint8_t) rand(); + } + } // For data, just return to the corresponding location sector memcpy(m_tag_tx_buffer.tx_raw_buffer, m_tag_information->memory[CurrentAddress], 16); } @@ -1157,6 +1163,7 @@ bool nfc_tag_mf1_data_factory(uint8_t slot, tag_specific_type_t tag_type) { p_mf1_information->config.use_mf1_coll_res = false; p_mf1_information->config.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL; p_mf1_information->config.detection_enable = false; + p_mf1_information->config.mode_fuzzing = false; // save data to flash tag_sense_type_t sense_type = get_sense_type_from_tag_type(tag_type); @@ -1193,6 +1200,16 @@ uint32_t nfc_tag_mf1_detection_log_count(void) { return m_auth_log.count; } +// Settling whether mode fuzzing is on or off +void nfc_tag_mf1_set_mode_fuzzing(bool fuzzing) { + m_tag_information->config.mode_fuzzing = fuzzing; +} + +// Whether mode fuzzing is on or off +bool nfc_tag_mf1_is_mode_fuzzing(void) { + return m_tag_information->config.mode_fuzzing; +} + // Set gen1a magic mode void nfc_tag_mf1_set_gen1a_magic_mode(bool enable) { m_tag_information->config.mode_gen1a_magic = enable; diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h index 35217149..1fd6ad3a 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h @@ -71,8 +71,10 @@ typedef struct { uint8_t detection_enable: 1; // Allow to write block 0 (CUID/gen2 mode) uint8_t mode_gen2_magic: 1; + // Change content of tag each time it has been read + uint8_t mode_fuzzing: 1; // reserve - uint8_t reserved1: 4; + uint8_t reserved1: 3; uint8_t reserved2; uint8_t reserved3; } nfc_tag_mf1_configure_t; @@ -147,6 +149,8 @@ void nfc_tag_mf1_set_detection_enable(bool enable); bool nfc_tag_mf1_is_detection_enable(void); void nfc_tag_mf1_detection_log_clear(void); uint32_t nfc_tag_mf1_detection_log_count(void); +void nfc_tag_mf1_set_mode_fuzzing(bool fuzzing); +bool nfc_tag_mf1_is_mode_fuzzing(void); nfc_tag_14a_coll_res_reference_t *get_mifare_coll_res(void); nfc_tag_14a_coll_res_reference_t *get_saved_mifare_coll_res(void); void nfc_tag_mf1_set_gen1a_magic_mode(bool enable); diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index db198b49..49e37547 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -852,6 +852,41 @@ def on_exec(self, args: argparse.Namespace): return +@hf_mf.command('fuzz') +class HFMFFuzz(ReaderRequiredUnit): + + key_re = re.compile(r"^(?P([0-9]|1[0-5])):(?P(A|B)):(?P([0-9A-Fa-f]{12}))$") + + def args_parse_key(self, arg): + m = self.key_re.match(arg) + if not m: + raise argparse.ArgumentTypeError("Expected format: :: where is between 0-15 and is 6 bytes long") + return (int(m.group('idx')), m.group('ab'), bytes.fromhex(m.group('key'))) + + def args_parser(self) -> ArgumentParserNoExit: + parser = ArgumentParserNoExit() + parser.description = 'Mifare Classic fuzzer tag' + parser.add_argument('-k', '--key', type=self.args_parse_key, required=False, action="extend", nargs="+", + help="Key to use for a given sector") + return parser + + def on_exec(self, args: argparse.Namespace): + keys = [{'a': b'\xff\xff\xff\xff\xff\xff', 'b': b'\x00\x00\x00\x00\x00\x00'} for _ in range(16)] + for keyarg in args.key: + keys[keyarg[0]][keyarg[1].lower()] = keyarg[2] + # generate and write blocks to emulate + for block_idx in range(64): + # key block + if block_idx % 4 == 3: + data = keys[block_idx // 4]['a'] + b'\xff' * 4 + keys[block_idx // 4]['b'] + else: + data = b'\x00' * 16 + self.cmd.mf1_write_emu_block_data(block_idx, data) + # set fuzzer mode + self.cmd.mf1_set_mode_fuzzing(True) + return + + @hf_mf.command('fchk') class HFMFFCHK(ReaderRequiredUnit): def args_parser(self) -> ArgumentParserNoExit: @@ -1444,6 +1479,8 @@ def args_parser(self) -> ArgumentParserNoExit: log_group = parser.add_mutually_exclusive_group() log_group.add_argument('--enable-log', action='store_true', help="Enable logging of MFC authentication data") log_group.add_argument('--disable-log', action='store_true', help="Disable logging of MFC authentication data") + log_group.add_argument('--enable-fuzzing', action='store_true', help="Enable fuzzing mode for slot (i.e. changing data at each read)") + log_group.add_argument('--disable-fuzzing', action='store_true', help="Disable fuzzing mode for slot (i.e. changing data at each read)") return parser def on_exec(self, args: argparse.Namespace): @@ -1473,6 +1510,7 @@ def on_exec(self, args: argparse.Namespace): block_anti_coll_mode = mfc_config["block_anti_coll_mode"] write_mode = MifareClassicWriteMode(mfc_config["write_mode"]) detection = mfc_config["detection"] + fuzzing = mfc_config["fuzzing"] change_requested, change_done, uid, atqa, sak, ats = self.update_hf14a_anticoll(args, uid, atqa, sak, ats) if args.enable_gen1a: change_requested = True @@ -1547,6 +1585,22 @@ def on_exec(self, args: argparse.Namespace): change_done = True else: print(f'{CY}Requested logging of MFC authentication data already disabled{C0}') + if args.enable_fuzzing: + change_requested = True + if not fuzzing: + fuzzing = True + self.cmd.mf1_set_mode_fuzzing(fuzzing) + change_done = True + else: + print(f'{CY}Requested fuzzing mode of MFC authentication data already enabled{C0}') + elif args.disable_fuzzing: + change_requested = True + if fuzzing: + fuzzing = False + self.cmd.mf1_set_mode_fuzzing(fuzzing) + change_done = True + else: + print(f'{CY}Requested logging of MFC authentication data already disabled{C0}') if change_done: print(' - MF1 Emulator settings updated') @@ -1571,6 +1625,8 @@ def on_exec(self, args: argparse.Namespace): print(f'- {"Write mode:":40}{CR}invalid value!{C0}') print( f'- {"Log (mfkey32) mode:":40}{f"{CG}enabled{C0}" if detection else f"{CR}disabled{C0}"}') + print( + f'- {"Fuzzing mode:":40}{f"{CG}enabled{C0}" if fuzzing else f"{CR}disabled{C0}"}') @hf_mfu.command('rdpg') @@ -1822,6 +1878,8 @@ def on_exec(self, args: argparse.Namespace): print( f' {"Log (mfkey32) mode:":40}' f'{f"{CG}enabled{C0}" if config["detection"] else f"{CR}disabled{C0}"}') + print( + f' {"Fuzzing mode:":40}{f"{CG}enabled{C0}" if config["fuzzing"] else f"{CR}disabled{C0}"}') # LF field_length = maxnamelength+slotnames[fwslot]["lf"]["metalen"]+1 diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 2ef363dd..be3b19a9 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -550,6 +550,17 @@ def mf1_get_detection_log(self, index: int): resp.parsed = result_list return resp + @expect_response(Status.SUCCESS) + def mf1_set_mode_fuzzing(self, fuzzing: bool): + """ + Set whether to enable the detection of the current card slot. + + :param enable: Whether to enable + :return: + """ + data = struct.pack('!B', fuzzing) + return self.device.send_cmd_sync(Command.MF1_SET_MODE_FUZZING, data) + @expect_response(Status.SUCCESS) def mf1_write_emu_block_data(self, block_start: int, block_data: bytes): """ @@ -641,17 +652,19 @@ def mf1_get_emulator_config(self): [2] - mf1_is_gen2_magic_mode [3] - mf1_is_use_mf1_coll_res (use UID/BCC/SAK/ATQA from 0 block) [4] - mf1_get_write_mode + [5] - mf1_is_mode_fuzzing :return: """ resp = self.device.send_cmd_sync(Command.MF1_GET_EMULATOR_CONFIG) if resp.status == Status.SUCCESS: - b1, b2, b3, b4, b5 = struct.unpack('!????B', resp.data) + b1, b2, b3, b4, b5, b6 = struct.unpack('!?????B', resp.data) resp.parsed = {'detection': b1, 'gen1a_mode': b2, 'gen2_mode': b3, 'block_anti_coll_mode': b4, - 'write_mode': b5} + 'write_mode': b5, + 'fuzzing': b6} return resp @expect_response(Status.SUCCESS) diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index 2c13b8fe..0499d4d3 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -95,6 +95,8 @@ class Command(enum.IntEnum): MF1_GET_WRITE_MODE = 4016 MF1_SET_WRITE_MODE = 4017 HF14A_GET_ANTI_COLL_DATA = 4018 + MF1_SET_MODE_FUZZING = 4019 + MF1_GET_MODE_FUZZING = 4020 EM410X_SET_EMU_ID = 5000 EM410X_GET_EMU_ID = 5001