mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-08-29 05:28:22 +00:00
Merge branch 'master' into fix-readable-mfc-keyb
Signed-off-by: oSPANNERo <25675033+oSPANNERo@users.noreply.github.com>
This commit is contained in:
@@ -107,6 +107,11 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# TODO workaround: "error: linker script file 'xxxx' appears multiple times"
|
||||
# This should be reverted once MSYS2 ships a non-broken version of the linker.
|
||||
- name: Downgrade ARM linker (Windows, temporary)
|
||||
run: pacman -U --noconfirm https://repo.msys2.org/mingw/ucrt64/mingw-w64-ucrt-x86_64-arm-none-eabi-binutils-2.46.1-1-any.pkg.tar.zst
|
||||
|
||||
- name: make clean
|
||||
run: make clean
|
||||
|
||||
|
||||
+3
-1
@@ -3,7 +3,9 @@ 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]
|
||||
- Fixed `hf mf dump` replacing readable sector trailer Key B values with values from the key file (@oSPANNERo)
|
||||
- Fixed `hf mf dump` preserving readable sector trailer Key B data instead of overwriting it with values from the supplied key file (@oSPANNERo)
|
||||
- Changed `magic_cards_notes.md` - documented the USCUID-UL helper scripts (`hf_mfu_uscuid` / `hf_mf_uscuid_prog`) and how to set the tag signature, replacing the outdated "No implemented commands" note (@c-barron)
|
||||
- Fixed `hf_mf_uscuid_prog.lua` - corrected the script name shown in its usage text (@c-barron)
|
||||
- Added `hf felica seacauth1` command
|
||||
- Added `lf trovan` commands to support Trovan Animal ID (@iceman1001)
|
||||
- Added `hf mf gdmgetblk/gdmgethidblk/gdmsethidblk/gdmsetuid/gdmwipe/gdmsetsig` (@0x6r1an0y)
|
||||
|
||||
@@ -2259,6 +2259,7 @@ FC9418BF788B
|
||||
# Guest Cashless Prepaid Arcade Payment Cards
|
||||
168168168168
|
||||
198407157610
|
||||
248B4520504D
|
||||
4E4F584D2101
|
||||
4E4F584D2105
|
||||
686B35333376
|
||||
|
||||
@@ -44,7 +44,7 @@ example = [[
|
||||
3. script run hf_mf_uscuid_prog -S 0
|
||||
]]
|
||||
usage = [[
|
||||
script run hf_mf_uscuid_uid_prog [-h] [-u <uid>] [-t] [-3] [-s <signature>] [-w 1] [-R -B <blk>] [-S -E <sec>] [-g -c -b -2 -7 -d -a -n -r <0/1>]
|
||||
script run hf_mf_uscuid_prog [-h] [-u <uid>] [-t] [-3] [-s <signature>] [-w 1] [-R -B <blk>] [-S -E <sec>] [-g -c -b -2 -7 -d -a -n -r <0/1>]
|
||||
]]
|
||||
arguments = [[
|
||||
-h this help
|
||||
|
||||
@@ -251,7 +251,7 @@
|
||||
"AID": "F47300",
|
||||
"Vendor": "Inner Range Pty Ltd",
|
||||
"Country": "AU",
|
||||
"Name": "SIFER-P / SIFER-U Credential",
|
||||
"Name": "SIFER-P / SIFER-U / SIFER-C Credential",
|
||||
"Description": "Inner Range Access Control",
|
||||
"Type": "pacs"
|
||||
},
|
||||
|
||||
@@ -3715,6 +3715,17 @@
|
||||
"android://eu.sharry.swp.sharryworkplace"
|
||||
]
|
||||
},
|
||||
{
|
||||
"AID": "A0000007800007",
|
||||
"Vendor": "Translink systems",
|
||||
"Country": "The Netherlands",
|
||||
"Name": "OV Pas",
|
||||
"Description": "New card for Dutch transit system, closed-loop EMV replacement of the MIFARE Classic 4K OV-Chipkaart",
|
||||
"Type": "transport",
|
||||
"Sources": [
|
||||
"https://blog.mobielstraat.nl/quick-look-at-the-new-ov-pas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"AID": "A0000000791000",
|
||||
"Vendor": "HID Global",
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
"id": "250091",
|
||||
"name": "CTS Strasbourg"
|
||||
},
|
||||
{
|
||||
"id": "250303",
|
||||
"name": "TAO / Orléans"
|
||||
},
|
||||
{
|
||||
"id": "250502",
|
||||
"name": "Oura",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+115
-4
@@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "comms.h"
|
||||
#include "cmdtrace.h"
|
||||
@@ -82,6 +83,7 @@
|
||||
#define FELICA_POLLING_REQUEST_SYSTEM_CODE 0x01U
|
||||
#define FELICA_SYSTEM_LIST_JSON "felica/felica_system_code_list"
|
||||
#define FELICA_IC_CODE_LIST_JSON "felica/felica_ic_code_list"
|
||||
#define FELICA_CONTAINER_ISSUE_LIST_JSON "felica/felica_container_issue_information_list"
|
||||
|
||||
#define FELICA_REQUEST_SERVICE_DISCOVERY_BATCH_SIZE 32U
|
||||
#define FELICA_MAX_NODE_NUMBER 0x03FFU
|
||||
@@ -457,6 +459,8 @@ static json_t *felica_system_list = NULL;
|
||||
static bool felica_system_list_loaded = false;
|
||||
static json_t *felica_ic_code_list = NULL;
|
||||
static bool felica_ic_code_list_loaded = false;
|
||||
static json_t *felica_container_issue_list = NULL;
|
||||
static bool felica_container_issue_list_loaded = false;
|
||||
|
||||
static void set_last_known_card(felica_card_select_t card) {
|
||||
last_known_card = card;
|
||||
@@ -771,6 +775,100 @@ static json_t *felica_get_ic_code_list(void) {
|
||||
return felica_ic_code_list;
|
||||
}
|
||||
|
||||
static json_t *felica_get_container_issue_list(void) {
|
||||
if (felica_container_issue_list_loaded) {
|
||||
return felica_container_issue_list;
|
||||
}
|
||||
|
||||
felica_container_issue_list_loaded = true;
|
||||
|
||||
char *path = NULL;
|
||||
if (searchFile(&path, RESOURCES_SUBDIR, FELICA_CONTAINER_ISSUE_LIST_JSON, ".json", true) != PM3_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_error_t error;
|
||||
json_t *root = json_load_file(path, 0, &error);
|
||||
if (root == NULL) {
|
||||
PrintAndLogEx(WARNING, "Failed to parse `%s` line %d: %s", path, error.line, error.text);
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (json_is_array(root) == false) {
|
||||
PrintAndLogEx(WARNING, "Invalid `%s` format, expected array root", path);
|
||||
json_decref(root);
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
felica_container_issue_list = root;
|
||||
free(path);
|
||||
return felica_container_issue_list;
|
||||
}
|
||||
|
||||
static bool felica_container_issue_matches(const felica_get_container_issue_info_response_t *response,
|
||||
const json_t *entry) {
|
||||
if (response == NULL || json_is_object(entry) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *format_hex = felica_get_json_string(entry, "format_version_carrier_info");
|
||||
const char *model = felica_get_json_string(entry, "mobile_phone_model_info");
|
||||
if (format_hex == NULL || model == NULL ||
|
||||
strlen(format_hex) != (sizeof(response->format_version_carrier_information) * 2U)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t expected_format[sizeof(response->format_version_carrier_information)] = {0};
|
||||
size_t expected_format_len = 0;
|
||||
if (hexstr_to_byte_array(format_hex, expected_format, &expected_format_len) == false ||
|
||||
expected_format_len != sizeof(expected_format) ||
|
||||
memcmp(response->format_version_carrier_information, expected_format, sizeof(expected_format)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t model_len = strlen(model);
|
||||
if (model_len == 0 || model_len > sizeof(response->mobile_phone_model_information) ||
|
||||
memcmp(response->mobile_phone_model_information, model, model_len) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = model_len; i < sizeof(response->mobile_phone_model_information); i++) {
|
||||
if (response->mobile_phone_model_information[i] != 0x00) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void felica_print_container_issue_annotations(const felica_get_container_issue_info_response_t *response) {
|
||||
json_t *list = felica_get_container_issue_list();
|
||||
if (response == NULL || list == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
json_t *entry = NULL;
|
||||
json_array_foreach(list, index, entry) {
|
||||
if (felica_container_issue_matches(response, entry) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *name = felica_get_json_string(entry, "name");
|
||||
if (name == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *carrier = felica_get_json_string(entry, "carrier");
|
||||
PrintAndLogEx(INFO, " Device Model.......... " _GREEN_("%s"), name);
|
||||
if (carrier) {
|
||||
PrintAndLogEx(INFO, " Carrier............... " _GREEN_("%s"), carrier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const json_t *felica_find_ic_annotation(uint8_t rom_type, uint8_t ic_type) {
|
||||
json_t *ic_code_list = felica_get_ic_code_list();
|
||||
if (ic_code_list == NULL) {
|
||||
@@ -2397,6 +2495,18 @@ static int info_felica(bool verbose) {
|
||||
PrintAndLogEx(INFO, " Code......... " _GREEN_("%s"), sprint_hex_inrow(card.code, sizeof(card.code)));
|
||||
}
|
||||
PrintAndLogEx(INFO, " NFCID2....... " _GREEN_("%s"), sprint_hex_inrow(card.uid, sizeof(card.uid)));
|
||||
const int16_t days_since_2000 = (int16_t)(((uint16_t)card.IDm[5] << 8) | card.IDm[4]);
|
||||
const time_t felica_epoch = (time_t)946684800; // 2000-01-01 UTC
|
||||
const time_t now = time(NULL);
|
||||
if (days_since_2000 >= 0 && now != (time_t)-1 && now >= felica_epoch &&
|
||||
(time_t)days_since_2000 <= (now - felica_epoch) / 86400) {
|
||||
const time_t manufacturing_time = felica_epoch + ((time_t)days_since_2000 * 86400);
|
||||
const struct tm *date = gmtime(&manufacturing_time);
|
||||
char date_string[11] = {0};
|
||||
if (date != NULL && strftime(date_string, sizeof(date_string), "%Y-%m-%d", date) != 0) {
|
||||
PrintAndLogEx(INFO, " Mfg date..... " _GREEN_("%s"), date_string);
|
||||
}
|
||||
}
|
||||
PrintAndLogEx(INFO, "PMM............ " _YELLOW_("%s"), sprint_hex_inrow(card.PMm, sizeof(card.PMm)));
|
||||
PrintAndLogEx(INFO, " IC code...... " _GREEN_("%s") " ( %s )",
|
||||
sprint_hex_inrow(card.iccode, sizeof(card.iccode)),
|
||||
@@ -2469,18 +2579,19 @@ static int info_felica(bool verbose) {
|
||||
sizeof(container_issue_info_response.mobile_phone_model_information),
|
||||
model_ascii,
|
||||
sizeof(model_ascii)
|
||||
);
|
||||
);
|
||||
PrintAndLogEx(INFO, "Container issue info:");
|
||||
PrintAndLogEx(INFO, " Format/Carrier... " _YELLOW_("%s"),
|
||||
PrintAndLogEx(INFO, " Format/Carrier info... " _YELLOW_("%s"),
|
||||
sprint_hex_inrow(container_issue_info_response.format_version_carrier_information,
|
||||
sizeof(container_issue_info_response.format_version_carrier_information)));
|
||||
if (model_is_ascii) {
|
||||
PrintAndLogEx(INFO, " Model............ " _GREEN_("%s") " (ASCII)", model_ascii);
|
||||
PrintAndLogEx(INFO, " Model info............ " _GREEN_("%s") " (ASCII)", model_ascii);
|
||||
} else {
|
||||
PrintAndLogEx(INFO, " Model............ " _YELLOW_("%s") " (HEX)",
|
||||
PrintAndLogEx(INFO, " Model info............ " _YELLOW_("%s") " (HEX)",
|
||||
sprint_hex_inrow(container_issue_info_response.mobile_phone_model_information,
|
||||
sizeof(container_issue_info_response.mobile_phone_model_information)));
|
||||
}
|
||||
felica_print_container_issue_annotations(&container_issue_info_response);
|
||||
}
|
||||
|
||||
const uint16_t container_properties[] = {0x0000, 0x0001};
|
||||
|
||||
@@ -1937,8 +1937,12 @@ static int CmdHFiClassELoad(const char *Cmd) {
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
print_picopass_header((picopass_hdr_t *) dump);
|
||||
print_picopass_info((picopass_hdr_t *) dump);
|
||||
if (bytes_read < sizeof(picopass_hdr_t)) {
|
||||
PrintAndLogEx(FAILED, "Error, dump file is too small to be a valid iCLASS dump - bytes: %zu, expected at least: %zu", bytes_read, sizeof(picopass_hdr_t));
|
||||
} else {
|
||||
print_picopass_header((picopass_hdr_t *) dump);
|
||||
print_picopass_info((picopass_hdr_t *) dump);
|
||||
}
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
@@ -2059,8 +2063,12 @@ static int CmdHFiClassEView(const char *Cmd) {
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
print_picopass_header((picopass_hdr_t *) dump);
|
||||
print_picopass_info((picopass_hdr_t *) dump);
|
||||
if (bytes < sizeof(picopass_hdr_t)) {
|
||||
PrintAndLogEx(FAILED, "Error, only %u bytes downloaded, too small to be a valid iCLASS dump - expected at least: %zu", bytes, sizeof(picopass_hdr_t));
|
||||
} else {
|
||||
print_picopass_header((picopass_hdr_t *) dump);
|
||||
print_picopass_info((picopass_hdr_t *) dump);
|
||||
}
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
@@ -2326,6 +2334,18 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Below (and in iclass_decode_credentials()) we unconditionally probe
|
||||
// fixed offsets up through block 9 -- app_issuer_area (block 5), the
|
||||
// aa1_encryption flag and block 7 in iclass_decode_credentials(), and
|
||||
// the block 9 PACS/PIN check here -- regardless of what applimit or
|
||||
// decryptedlen/8 say the "real" block count is. All 10 blocks (0-9)
|
||||
// must actually be present in the loaded file before any of that runs.
|
||||
if (decryptedlen < 10 * PICOPASS_BLOCK_SIZE) {
|
||||
PrintAndLogEx(FAILED, "Error, dump file is too small - bytes: %zu, expected at least: %d", decryptedlen, 10 * PICOPASS_BLOCK_SIZE);
|
||||
free(decrypted);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
have_file = true;
|
||||
}
|
||||
|
||||
|
||||
+21
-3
@@ -639,7 +639,7 @@ static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext_t *d
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int DesfirePCRun(DesfireContext_t *dctx, const uint8_t proximity_key[MFDES_PC_KEY_LEN], uint8_t rounds, bool verbose) {
|
||||
static int DesfirePCRun(DesfireContext_t *dctx, const uint8_t proximity_key[MFDES_PC_KEY_LEN], uint8_t rounds, bool activate_field, bool verbose) {
|
||||
if (dctx == NULL || proximity_key == NULL || rounds < 1 || rounds > MFDES_PC_MAX_ROUNDS) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -655,7 +655,7 @@ static int DesfirePCRun(DesfireContext_t *dctx, const uint8_t proximity_key[MFDE
|
||||
uint8_t prepare_resp[APDU_RES_LEN] = {0};
|
||||
size_t prepare_resp_len = 0;
|
||||
uint8_t respcode = 0xFF;
|
||||
res = DesfireExchangeEx(true, dctx, MFDES_PREPARE_PC, NULL, 0, &respcode, prepare_resp, &prepare_resp_len, true, 0);
|
||||
res = DesfireExchangeEx(activate_field, dctx, MFDES_PREPARE_PC, NULL, 0, &respcode, prepare_resp, &prepare_resp_len, true, 0);
|
||||
if (res != PM3_SUCCESS) {
|
||||
uint16_t sw = status(respcode);
|
||||
PrintAndLogEx(ERR, "Prepare proximity check command failed. Result: %d %s", res, DesfireGetErrorString(res, &sw));
|
||||
@@ -4591,6 +4591,7 @@ static int CmdHF14ADesPC(const char *Cmd) {
|
||||
"This command uses plain communication with a dedicated 16-byte AES proximity key.",
|
||||
"hf mfdes pc --key 00000000000000000000000000000000\n"
|
||||
"hf mfdes pc --key 00112233445566778899aabbccddeeff --rounds 4\n"
|
||||
"hf mfdes pc --aid 123456 --key 00112233445566778899aabbccddeeff\n"
|
||||
"hf mfdes pc --key 00112233445566778899aabbccddeeff -c native -a");
|
||||
|
||||
void *argtable[] = {
|
||||
@@ -4600,6 +4601,7 @@ static int CmdHF14ADesPC(const char *Cmd) {
|
||||
arg_str1("k", "key", "<hex>", "Key (AES-128, exactly 16 bytes)"),
|
||||
arg_int0("r", "rounds", "<dec>", "Number of rounds (1..8), default 8"),
|
||||
arg_str0("c", "ccset", "<native|niso>", "Communication command set (default from `hf mfdes default`)"),
|
||||
arg_str0(NULL, "aid", "<hex>", "Application ID (3 hex bytes, big endian)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
@@ -4635,6 +4637,13 @@ static int CmdHF14ADesPC(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint32_t appid = 0;
|
||||
bool appid_present = false;
|
||||
if (CLIGetUint32Hex(ctx, 6, 0, &appid, &appid_present, 3, "AID must have 3 bytes length")) {
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
SetAPDULogging(APDULogging);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
@@ -4643,7 +4652,16 @@ static int CmdHF14ADesPC(const char *Cmd) {
|
||||
DesfireSetCommMode(&dctx, DCMPlain);
|
||||
DesfireSetSecureChannel(&dctx, DACNone);
|
||||
|
||||
int res = DesfirePCRun(&dctx, proximity_key, rounds, verbose);
|
||||
int res = PM3_SUCCESS;
|
||||
if (appid_present) {
|
||||
res = DesfireSelectAndAuthenticateAppW(&dctx, DACNone, ISW6bAID, appid, true, verbose);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
res = DesfirePCRun(&dctx, proximity_key, rounds, !appid_present, verbose);
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -783,6 +783,7 @@ Here is how the IC can be configured:
|
||||
* MF-8 (RU)
|
||||
* MF-3 (RU) - not susceptible to "field reset bug", a way to detect [OTP](#fuid) chips.
|
||||
* MF-3.2 (RU) - static nonce `01200145`, potentially fixed chip which can bypass Iron Logic's filters.
|
||||
* M+ (CopyKEY) - chips presented as a universal alternative to more advanced Chinese ICs; only real difference is presence of rewritable sectors 16+17 and block 255 (readable after any auth)
|
||||
`
|
||||
### Identify
|
||||
|
||||
@@ -2233,7 +2234,26 @@ hf 14a raw -akb 7 40; hf 14a raw -k 43; hf 14a raw -ck A2F2000000BD; hf 14a raw
|
||||
|
||||
^[Top](#top)
|
||||
|
||||
No implemented commands at time of writing
|
||||
Two helper scripts drive USCUID-UL cards:
|
||||
|
||||
* `script run hf_mfu_uscuid` (Python) - read/parse config, change emulated type, set UID, set signature, and raw backdoor read/write.
|
||||
* `script run hf_mf_uscuid_prog` (Lua) - equivalent functionality. See `-h` for its options.
|
||||
|
||||
Backdoor operations (set UID, set signature, raw hidden-block read/write) require the gen1a backdoor to be enabled - a config block starting with `7AFF`, see the [USCUID-UL configuration guide](#uscuid-ul-configuration-guide) - and a magic wakeup to be selected: for the Python script, `--gen1a` (`40`/`43`) or `--gdm` (`20`/`23`).
|
||||
|
||||
Example - write the tag signature (32 bytes / 64 hexsymbols) using the `40:43` wakeup:
|
||||
|
||||
```
|
||||
script run hf_mfu_uscuid -s <signature, 64 hexsymbols> --gen1a
|
||||
```
|
||||
|
||||
Verify with `hf mfu info` and look for `Signature verification: successful`.
|
||||
|
||||
The same result can be achieved manually with raw commands (magic wakeup, then write the eight signature pages `F8`-`FF`):
|
||||
|
||||
```
|
||||
hf 14a raw -akb 7 40; hf 14a raw -k 43; hf 14a raw -ck A2F8<4 bytes>; ...; hf 14a raw -c A2FF<4 bytes>
|
||||
```
|
||||
|
||||
### libnfc commands
|
||||
|
||||
|
||||
Reference in New Issue
Block a user