mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
Add hf iclass creditepurse
command to allow crediting the epurse debit value.
This commit is contained in:
parent
b59fad842b
commit
186308cb4a
10 changed files with 318 additions and 6 deletions
|
@ -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 `hf iclass creditepurse` command to allow crediting the epurse debit value (@nvx)
|
||||
|
||||
## [Raccoon.4.17140][2023-09-09]
|
||||
- Changed text and adjust pm3_test case for mf_aes_brute (@doegox)
|
||||
|
|
|
@ -1940,6 +1940,10 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
iClass_Restore((iclass_restore_req_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_ICLASS_CREDIT_EPURSE: {
|
||||
iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_HFSNIFF
|
||||
|
|
157
armsrc/iclass.c
157
armsrc/iclass.c
|
@ -1902,13 +1902,13 @@ void iClass_WriteBlock(uint8_t *msg) {
|
|||
|
||||
// verify write
|
||||
uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
if (payload->req.blockno == 2) {
|
||||
if (pagemap == PICOPASS_SECURE_PAGEMODE && payload->req.blockno == 2) {
|
||||
// check response. e-purse update swaps first and second half
|
||||
if (memcmp(payload->data + 4, resp, 4) || memcmp(payload->data, resp + 4, 4)) {
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
} else if (payload->req.blockno == 3 || payload->req.blockno == 4) {
|
||||
} else if (pagemap == PICOPASS_SECURE_PAGEMODE && (payload->req.blockno == 3 || payload->req.blockno == 4)) {
|
||||
// check response. Key updates always return 0xffffffffffffffff
|
||||
if (memcmp(all_ff, resp, 8)) {
|
||||
res = false;
|
||||
|
@ -1929,6 +1929,159 @@ out:
|
|||
reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void iclass_credit_epurse(iclass_credit_epurse_t *payload) {
|
||||
|
||||
LED_A_ON();
|
||||
|
||||
bool shallow_mod = payload->req.shallow_mod;
|
||||
|
||||
Iso15693InitReader();
|
||||
|
||||
// select tag.
|
||||
uint32_t eof_time = 0;
|
||||
picopass_hdr_t hdr = {0};
|
||||
uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod);
|
||||
if (res == false) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
|
||||
|
||||
uint8_t mac[4] = {0};
|
||||
|
||||
// authenticate
|
||||
if (payload->req.do_auth) {
|
||||
|
||||
res = authenticate_iclass_tag(&payload->req, &hdr, &start_time, &eof_time, mac);
|
||||
if (res == false) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
|
||||
|
||||
uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, payload->req.blockno, 0x00, 0x00};
|
||||
AddCrc(cmd_read + 1, 1);
|
||||
|
||||
uint8_t epurse[10];
|
||||
res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), epurse, sizeof(epurse), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod);
|
||||
if (!res) {
|
||||
switch_off();
|
||||
if (payload->req.send_reply)
|
||||
reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETIMEOUT, (uint8_t *)&res, sizeof(uint8_t));
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t write[14] = { 0x80 | ICLASS_CMD_UPDATE, payload->req.blockno };
|
||||
uint8_t write_len = 14;
|
||||
|
||||
uint8_t epurse_offset = 0;
|
||||
const uint8_t empty_epurse[] = {0xff, 0xff, 0xff, 0xff};
|
||||
if (!memcmp(epurse, empty_epurse, 4)) {
|
||||
// epurse data in stage 2
|
||||
epurse_offset = 4;
|
||||
}
|
||||
|
||||
memcpy(epurse + epurse_offset, payload->epurse, 4);
|
||||
|
||||
// blank out debiting value as per the first step of the crediting procedure
|
||||
epurse[epurse_offset + 0] = 0xFF;
|
||||
epurse[epurse_offset + 1] = 0xFF;
|
||||
|
||||
// initial epurse write for credit
|
||||
memcpy(write + 2, epurse, 8);
|
||||
|
||||
doMAC_N(write + 1, 9, payload->req.use_credit_key ? hdr.key_c : hdr.key_d, mac);
|
||||
memcpy(write + 10, mac, sizeof(mac));
|
||||
|
||||
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
|
||||
|
||||
uint8_t resp[10] = {0};
|
||||
|
||||
uint8_t tries = 3;
|
||||
while (tries-- > 0) {
|
||||
|
||||
iclass_send_as_reader(write, write_len, &start_time, &eof_time, shallow_mod);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
res = false;
|
||||
switch_off();
|
||||
if (payload->req.send_reply)
|
||||
reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t));
|
||||
return;
|
||||
} else {
|
||||
|
||||
uint16_t resp_len = 0;
|
||||
int res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true, &resp_len);
|
||||
if (res2 == PM3_SUCCESS && resp_len == 10) {
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tries == 0) {
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// check response. e-purse update swaps first and second half
|
||||
if (memcmp(write + 2 + 4, resp, 4) || memcmp(write + 2, resp + 4, 4)) {
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// new epurse write
|
||||
// epurse offset is now flipped after the first write
|
||||
epurse_offset ^= 4;
|
||||
memcpy(resp + epurse_offset, payload->epurse, 4);
|
||||
memcpy(write + 2, resp, 8);
|
||||
|
||||
doMAC_N(write + 1, 9, payload->req.use_credit_key ? hdr.key_c : hdr.key_d, mac);
|
||||
memcpy(write + 10, mac, sizeof(mac));
|
||||
|
||||
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
|
||||
|
||||
tries = 3;
|
||||
while (tries-- > 0) {
|
||||
|
||||
iclass_send_as_reader(write, write_len, &start_time, &eof_time, shallow_mod);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
res = false;
|
||||
switch_off();
|
||||
if (payload->req.send_reply)
|
||||
reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t));
|
||||
return;
|
||||
} else {
|
||||
|
||||
uint16_t resp_len = 0;
|
||||
int res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true, &resp_len);
|
||||
if (res2 == PM3_SUCCESS && resp_len == 10) {
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tries == 0) {
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// check response. e-purse update swaps first and second half
|
||||
if (memcmp(write + 2 + 4, resp, 4) || memcmp(write + 2, resp + 4, 4)) {
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
switch_off();
|
||||
|
||||
if (payload->req.send_reply)
|
||||
reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void iClass_Restore(iclass_restore_req_t *msg) {
|
||||
|
||||
// sanitation
|
||||
|
|
|
@ -25,6 +25,7 @@ void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string);
|
|||
void ReaderIClass(uint8_t flags);
|
||||
|
||||
void iClass_WriteBlock(uint8_t *msg);
|
||||
void iclass_credit_epurse(iclass_credit_epurse_t *payload);
|
||||
void iClass_Dump(uint8_t *msg);
|
||||
|
||||
void iClass_Restore(iclass_restore_req_t *msg);
|
||||
|
|
|
@ -2202,6 +2202,129 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) {
|
|||
return isok;
|
||||
}
|
||||
|
||||
static int CmdHFiClassCreditEpurse(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf iclass creditepurse",
|
||||
"Credit the epurse on an iCLASS tag. The provided key must be the credit key.\n"
|
||||
"The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF.\n"
|
||||
"The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.",
|
||||
"hf iclass creditepurse -d FEFFFFFF -k 001122334455667B\n"
|
||||
"hf iclass creditepurse -d FEFFFFFF --ki 0");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("k", "key", "<hex>", "Credit key as 8 hex bytes"),
|
||||
arg_int0(NULL, "ki", "<dec>", "Key index to select key from memory 'hf iclass managekeys'"),
|
||||
arg_str1("d", "data", "<hex>", "data to write as 8 hex bytes"),
|
||||
arg_lit0(NULL, "elite", "elite computations applied to key"),
|
||||
arg_lit0(NULL, "raw", "no computations applied to key"),
|
||||
arg_lit0("v", "verbose", "verbose output"),
|
||||
arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
int key_len = 0;
|
||||
uint8_t key[8] = {0};
|
||||
|
||||
CLIGetHexWithReturn(ctx, 1, key, &key_len);
|
||||
|
||||
int key_nr = arg_get_int_def(ctx, 2, -1);
|
||||
|
||||
if (key_len > 0 && key_nr >= 0) {
|
||||
PrintAndLogEx(ERR, "Please specify key or index, not both");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (key_len > 0) {
|
||||
if (key_len != 8) {
|
||||
PrintAndLogEx(ERR, "Key is incorrect length");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
} else if (key_nr >= 0) {
|
||||
if (key_nr < ICLASS_KEYS_MAX) {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(ERR, "Key or key number must be provided");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
int blockno = 2;
|
||||
|
||||
int data_len = 0;
|
||||
uint8_t data[4] = {0};
|
||||
CLIGetHexWithReturn(ctx, 3, data, &data_len);
|
||||
|
||||
if (data_len != 4) {
|
||||
PrintAndLogEx(ERR, "Data must be 4 hex bytes (8 hex symbols)");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
bool elite = arg_get_lit(ctx, 4);
|
||||
bool rawkey = arg_get_lit(ctx, 5);
|
||||
bool verbose = arg_get_lit(ctx, 6);
|
||||
bool shallow_mod = arg_get_lit(ctx, 7);
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if ((rawkey + elite) > 1) {
|
||||
PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw'");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
iclass_credit_epurse_t payload = {
|
||||
.req.use_raw = rawkey,
|
||||
.req.use_elite = elite,
|
||||
.req.use_credit_key = true,
|
||||
.req.use_replay = false,
|
||||
.req.blockno = blockno,
|
||||
.req.send_reply = true,
|
||||
.req.do_auth = true,
|
||||
.req.shallow_mod = shallow_mod,
|
||||
};
|
||||
memcpy(payload.req.key, key, 8);
|
||||
memcpy(payload.epurse, data, sizeof(payload.epurse));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_ICLASS_CREDIT_EPURSE, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
|
||||
int isok;
|
||||
if (WaitForResponseTimeout(CMD_HF_ICLASS_CREDIT_EPURSE, &resp, 2000) == 0) {
|
||||
if (verbose) PrintAndLogEx(WARNING, "Command execute timeout");
|
||||
isok = PM3_ETIMEOUT;
|
||||
} else if (resp.status != PM3_SUCCESS) {
|
||||
if (verbose) PrintAndLogEx(ERR, "failed to communicate with card");
|
||||
isok = resp.status;
|
||||
} else {
|
||||
isok = (resp.data.asBytes[0] == 1) ? PM3_SUCCESS : PM3_ESOFT;
|
||||
}
|
||||
|
||||
switch (isok) {
|
||||
case PM3_SUCCESS:
|
||||
PrintAndLogEx(SUCCESS, "Credited epurse successfully");
|
||||
break;
|
||||
case PM3_ETEAROFF:
|
||||
if (verbose)
|
||||
PrintAndLogEx(INFO, "Writing tear off triggered");
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(FAILED, "Writing failed");
|
||||
break;
|
||||
}
|
||||
return isok;
|
||||
}
|
||||
|
||||
static int CmdHFiClassRestore(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf iclass restore",
|
||||
|
@ -4278,6 +4401,7 @@ static command_t CommandTable[] = {
|
|||
{"sniff", CmdHFiClassSniff, IfPm3Iclass, "Eavesdrop Picopass / iCLASS communication"},
|
||||
{"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"},
|
||||
{"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"},
|
||||
{"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"},
|
||||
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " --------------------"},
|
||||
// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"},
|
||||
{"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"},
|
||||
|
|
|
@ -276,6 +276,7 @@ const static vocabulary_t vocabulary[] = {
|
|||
{ 0, "hf iclass sniff" },
|
||||
{ 1, "hf iclass view" },
|
||||
{ 0, "hf iclass wrbl" },
|
||||
{ 0, "hf iclass creditepurse" },
|
||||
{ 0, "hf iclass chk" },
|
||||
{ 1, "hf iclass loclass" },
|
||||
{ 1, "hf iclass lookup" },
|
||||
|
|
|
@ -3029,6 +3029,26 @@
|
|||
],
|
||||
"usage": "hf iclass configcard [-hglp] [--ci <dec>] [--ki <dec>]"
|
||||
},
|
||||
"hf iclass creditepurse": {
|
||||
"command": "hf iclass creditepurse",
|
||||
"description": "Credit the epurse on an iCLASS tag. The provided key must be the credit key. The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF. The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.",
|
||||
"notes": [
|
||||
"hf iclass creditepurse -d FEFFFFFF -k 001122334455667B",
|
||||
"hf iclass creditepurse -d FEFFFFFF --ki 0"
|
||||
],
|
||||
"offline": false,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"-k, --key <hex> Credit key as 8 hex bytes",
|
||||
"--ki <dec> Key index to select key from memory 'hf iclass managekeys'",
|
||||
"-d, --data <hex> data to write as 8 hex bytes",
|
||||
"--elite elite computations applied to key",
|
||||
"--raw no computations applied to key",
|
||||
"-v, --verbose verbose output",
|
||||
"--shallow use shallow (ASK) reader modulation instead of OOK"
|
||||
],
|
||||
"usage": "hf iclass creditepurse [-hv] [-k <hex>] [--ki <dec>] -d <hex> [--elite] [--raw] [--shallow]"
|
||||
},
|
||||
"hf iclass decrypt": {
|
||||
"command": "hf iclass decrypt",
|
||||
"description": "3DES decrypt data This is a naive implementation, it tries to decrypt every block after block 6. Correct behaviour would be to decrypt only the application areas where the key is valid, which is defined by the configuration block. OBS! In order to use this function, the file `iclass_decryptionkey.bin` must reside in the resources directory. The file should be 16 bytes binary data or... make sure your cardhelper is placed in the sim module",
|
||||
|
@ -8786,7 +8806,7 @@
|
|||
"-1, --ht1 Card type Hitag 1",
|
||||
"-2, --ht2 Card type Hitag 2",
|
||||
"-s, --hts Card type Hitag S",
|
||||
"-m, --htm Card type Hitag \u03bc"
|
||||
"-m, --htm Card type Hitag \u00ce\u00bc"
|
||||
],
|
||||
"usage": "lf hitag eload [-h12sm] -f <fn>"
|
||||
},
|
||||
|
@ -11835,8 +11855,8 @@
|
|||
}
|
||||
},
|
||||
"metadata": {
|
||||
"commands_extracted": 686,
|
||||
"commands_extracted": 687,
|
||||
"extracted_by": "PM3Help2JSON v1.00",
|
||||
"extracted_on": "2023-09-07T18:12:46"
|
||||
"extracted_on": "2023-09-10T12:59:25"
|
||||
}
|
||||
}
|
|
@ -415,6 +415,7 @@ Check column "offline" for their availability.
|
|||
|`hf iclass sniff `|N |`Eavesdrop Picopass / iCLASS communication`
|
||||
|`hf iclass view `|Y |`Display content from tag dump file`
|
||||
|`hf iclass wrbl `|N |`Write Picopass / iCLASS block`
|
||||
|`hf iclass creditepurse `|N |`Credit epurse value`
|
||||
|`hf iclass chk `|N |`Check keys`
|
||||
|`hf iclass loclass `|Y |`Use loclass to perform bruteforce reader attack`
|
||||
|`hf iclass lookup `|Y |`Uses authentication trace to check for key in dictionary file`
|
||||
|
|
|
@ -87,6 +87,12 @@ typedef struct {
|
|||
uint8_t mac[4];
|
||||
} PACKED iclass_writeblock_req_t;
|
||||
|
||||
// iCLASS write block request data structure
|
||||
typedef struct {
|
||||
iclass_auth_req_t req;
|
||||
uint8_t epurse[4];
|
||||
} PACKED iclass_credit_epurse_t;
|
||||
|
||||
// iCLASS dump data structure
|
||||
typedef struct {
|
||||
uint8_t blockno;
|
||||
|
|
|
@ -565,7 +565,7 @@ typedef struct {
|
|||
|
||||
#define CMD_HF_EPA_COLLECT_NONCE 0x038A
|
||||
#define CMD_HF_EPA_REPLAY 0x038B
|
||||
#define CMD_HF_EPA_PACE_SIMULATE 0x039C
|
||||
#define CMD_HF_EPA_PACE_SIMULATE 0x038C
|
||||
|
||||
#define CMD_HF_LEGIC_INFO 0x03BC
|
||||
#define CMD_HF_LEGIC_ESET 0x03BD
|
||||
|
@ -581,6 +581,7 @@ typedef struct {
|
|||
#define CMD_HF_ICLASS_EML_MEMSET 0x0398
|
||||
#define CMD_HF_ICLASS_CHKKEYS 0x039A
|
||||
#define CMD_HF_ICLASS_RESTORE 0x039B
|
||||
#define CMD_HF_ICLASS_CREDIT_EPURSE 0x039C
|
||||
|
||||
// For ISO1092 / FeliCa
|
||||
#define CMD_HF_FELICA_SIMULATE 0x03A0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue