Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mode fuzzing for MF1 emulation #193

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-)
Expand Down
20 changes: 18 additions & 2 deletions firmware/application/src/app_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 },
Expand Down
2 changes: 2 additions & 0 deletions firmware/application/src/data_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
//
// ******************************************************************

Expand Down
17 changes: 17 additions & 0 deletions firmware/application/src/rfid/nfctag/hf/nfc_mf1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion firmware/application/src/rfid/nfctag/hf/nfc_mf1.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
58 changes: 58 additions & 0 deletions software/script/chameleon_cli_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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<idx>([0-9]|1[0-5])):(?P<ab>(A|B)):(?P<key>([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: <sector>:<A|B>:<key> where <sector> is between 0-15 and <key> 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:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand All @@ -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')
Expand Down Expand Up @@ -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
Expand Down
17 changes: 15 additions & 2 deletions software/script/chameleon_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions software/script/chameleon_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading