Merge pull request #1072 from tcprst/iclass_cliparser

hf iclass chk, managekeys, calcnewkey, lookup, sim - now use cliparser
This commit is contained in:
Iceman 2020-11-28 12:01:34 +01:00 committed by GitHub
commit 4f55b349df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 335 additions and 384 deletions

View file

@ -51,103 +51,6 @@ static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
}; };
static int usage_hf_iclass_sim(void) {
PrintAndLogEx(NORMAL, "Simulate a iCLASS legacy/standard tag\n");
PrintAndLogEx(NORMAL, "Usage: hf iCLASS sim [h] <option> [CSN]\n");
PrintAndLogEx(NORMAL, "Options");
PrintAndLogEx(NORMAL, " h : Show this help");
PrintAndLogEx(NORMAL, " 0 <CSN> : simulate the given CSN");
PrintAndLogEx(NORMAL, " 1 : simulate default CSN");
PrintAndLogEx(NORMAL, " 2 : Reader-attack, gather reader responses to extract elite key");
PrintAndLogEx(NORMAL, " 3 : Full simulation using emulator memory (see 'hf iclass eload')");
PrintAndLogEx(NORMAL, " 4 : Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass sim 0 031FEC8AF7FF12E0"));
PrintAndLogEx(NORMAL, " -- execute loclass attack online part");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass sim 2"));
PrintAndLogEx(NORMAL, " -- simulate full iCLASS 2k tag");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin"));
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass sim 3"));
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int usage_hf_iclass_calc_newkey(void) {
PrintAndLogEx(NORMAL, "Calculate new key for updating\n");
PrintAndLogEx(NORMAL, "Usage: hf iclass calc_newkey o <old key> n <new key> s [csn] e\n");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h : Show this help");
PrintAndLogEx(NORMAL, " o <old key> : *specify a key as 16 hex symbols or a key number as 1 symbol");
PrintAndLogEx(NORMAL, " n <new key> : *specify a key as 16 hex symbols or a key number as 1 symbol");
PrintAndLogEx(NORMAL, " s <csn> : specify a card Serial number to diversify the key (if omitted will attempt to read a csn)");
PrintAndLogEx(NORMAL, " e : specify new key as elite calc");
PrintAndLogEx(NORMAL, " ee : specify old and new key as elite calc");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " -- e key to e key given csn");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass calcnewkey o 1122334455667788 n 2233445566778899 s deadbeafdeadbeaf ee"));
PrintAndLogEx(NORMAL, " -- std key to e key read csn");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass calcnewkey o 1122334455667788 n 2233445566778899 e"));
PrintAndLogEx(NORMAL, " -- std to std read csn");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass calcnewkey o 1122334455667788 n 2233445566778899"));
PrintAndLogEx(NORMAL, "\nNOTE: * = required");
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int usage_hf_iclass_managekeys(void) {
PrintAndLogEx(NORMAL, "Manage iCLASS Keys in client memory:\n");
PrintAndLogEx(NORMAL, "Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p\n");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h : Show this help");
PrintAndLogEx(NORMAL, " n <keynbr> : specify the keyNbr to set in memory");
PrintAndLogEx(NORMAL, " k <key> : set a key in memory");
PrintAndLogEx(NORMAL, " f <filename> : specify a filename to use with load or save operations");
PrintAndLogEx(NORMAL, " s : save keys in memory to file specified by filename");
PrintAndLogEx(NORMAL, " l : load keys to memory from file specified by filename");
PrintAndLogEx(NORMAL, " p : print keys loaded into memory\n");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " -- set key");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys n 0 k 1122334455667788"));
PrintAndLogEx(NORMAL, " -- save key file");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys f mykeys.bin s"));
PrintAndLogEx(NORMAL, " -- load key file");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys f mykeys.bin l"));
PrintAndLogEx(NORMAL, " -- print keys");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass managekeys p"));
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int usage_hf_iclass_chk(void) {
PrintAndLogEx(NORMAL, "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag\n");
PrintAndLogEx(NORMAL, "Usage: hf iclass chk [h|e|r] [f (*.dic)]\n");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h Show this help");
PrintAndLogEx(NORMAL, " f <filename> Dictionary file with default iclass keys");
PrintAndLogEx(NORMAL, " r raw");
PrintAndLogEx(NORMAL, " e elite");
PrintAndLogEx(NORMAL, " c credit key (if not use, default is debit)");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass chk f dictionaries/iclass_default_keys.dic"));
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass chk f dictionaries/iclass_default_keys.dic e"));
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int usage_hf_iclass_lookup(void) {
PrintAndLogEx(NORMAL, "Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file\n");
PrintAndLogEx(NORMAL, "Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u <csn>] [p <epurse>] [m <macs>]\n");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h Show this help");
PrintAndLogEx(NORMAL, " f <filename> Dictionary file with default iclass keys");
PrintAndLogEx(NORMAL, " u CSN");
PrintAndLogEx(NORMAL, " p EPURSE");
PrintAndLogEx(NORMAL, " m macs");
PrintAndLogEx(NORMAL, " r raw");
PrintAndLogEx(NORMAL, " e elite");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f dictionaries/iclass_default_keys.dic"));
PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f dictionaries/iclass_default_keys.dic e"));
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int cmp_uint32(const void *a, const void *b) { static int cmp_uint32(const void *a, const void *b) {
const iclass_prekey_t *x = (const iclass_prekey_t *)a; const iclass_prekey_t *x = (const iclass_prekey_t *)a;
@ -455,24 +358,48 @@ static int CmdHFiClassSniff(const char *Cmd) {
} }
static int CmdHFiClassSim(const char *Cmd) { static int CmdHFiClassSim(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass sim",
"Simulate a iCLASS legacy/standard tag",
"hf iclass sim -t 0 --csn 031FEC8AF7FF12E0 -> simulate with specficied CSN\n"
"hf iclass sim -t 1 -> simulate with default CSN\n"
"hf iclass sim -t 2 -> execute loclass attack online part\n"
"hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -> simulate full iCLASS 2k tag\n"
"hf iclass sim -t 3 -> simulate full iCLASS 2k tag\n"
"hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key");
char cmdp = tolower(param_getchar(Cmd, 0)); void *argtable[] = {
if (strlen(Cmd) < 1 || cmdp == 'h') return usage_hf_iclass_sim(); arg_param_begin,
arg_int1("t", "type", NULL, "Simulation type to use"),
arg_str0(NULL, "csn", "<hex>", "Specify CSN as 8 bytes (16 hex symbols) to use with sim type 0"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int sim_type = arg_get_int(ctx, 1);
uint8_t sim_type = param_get8ex(Cmd, 0, 0, 10);
if (sim_type == 0) { int csn_len = 0;
if (param_gethex(Cmd, 1, CSN, 16)) { uint8_t csn[8] = {0};
PrintAndLogEx(ERR, "A CSN should consist of 16 HEX symbols"); CLIGetHexWithReturn(ctx, 2, csn, &csn_len);
return usage_hf_iclass_sim();
if (sim_type == 0 && csn_len > 0) {
if (csn_len != 8) {
PrintAndLogEx(ERR, "CSN is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
} }
PrintAndLogEx(INFO, " simtype: %02x CSN: %s", sim_type, sprint_hex(CSN, 8)); PrintAndLogEx(INFO, " simtype: %02x CSN: %s", sim_type, sprint_hex(csn, 8));
} else if (sim_type == 0 && csn_len == 0) {
PrintAndLogEx(ERR, "Simtype 0 requires CSN argument (--csn)");
CLIParserFree(ctx);
return PM3_EINVARG;
} }
CLIParserFree(ctx);
if (sim_type > 4) { if (sim_type > 4) {
PrintAndLogEx(ERR, "Undefined simtype %d", sim_type); PrintAndLogEx(ERR, "Undefined simtype %d", sim_type);
return usage_hf_iclass_sim(); return PM3_EINVARG;
} }
// remember to change the define NUM_CSNS to match. // remember to change the define NUM_CSNS to match.
@ -626,10 +553,10 @@ static int CmdHFiClassSim(const char *Cmd) {
case ICLASS_SIM_MODE_FULL: case ICLASS_SIM_MODE_FULL:
default: { default: {
PrintAndLogEx(INFO, "Starting iCLASS simulation"); PrintAndLogEx(INFO, "Starting iCLASS simulation");
PrintAndLogEx(INFO, "press " _YELLOW_("`enter`") " to cancel"); PrintAndLogEx(INFO, "press " _YELLOW_("`button`") " to cancel");
uint8_t numberOfCSNs = 0; uint8_t numberOfCSNs = 0;
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, numberOfCSNs, 1, CSN, 8); SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, numberOfCSNs, 1, csn, 8);
if (sim_type == ICLASS_SIM_MODE_FULL) if (sim_type == ICLASS_SIM_MODE_FULL)
PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass esave -h") "` to save the emulator memory to file"); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass esave -h") "` to save the emulator memory to file");
@ -1359,7 +1286,7 @@ static int CmdHFiClassDump(const char *Cmd) {
if (credit_key_len > 0) { if (credit_key_len > 0) {
auth = true; auth = true;
have_credit_key = true; have_credit_key = true;
if (key_len != 8) { if (credit_key_len != 8) {
PrintAndLogEx(ERR, "Credit key is incorrect length"); PrintAndLogEx(ERR, "Credit key is incorrect length");
CLIParserFree(ctx); CLIParserFree(ctx);
return PM3_EINVARG; return PM3_EINVARG;
@ -2377,89 +2304,127 @@ static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, u
} }
static int CmdHFiClassCalcNewKey(const char *Cmd) { static int CmdHFiClassCalcNewKey(const char *Cmd) {
uint8_t OLDKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; CLIParserContext *ctx;
uint8_t NEWKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; CLIParserInit(&ctx, "hf iclass calcnewkey",
uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; "Calculate new keys for updating (blocks 3 & 4)",
uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; "hf iclass calcnewkey --old 1122334455667788 --new 2233445566778899 --csn deadbeafdeadbeaf --elite2 -> e key to e key given csn\n"
uint8_t keyNbr = 0; "hf iclass calcnewkey --old 1122334455667788 --new 2233445566778899 --elite -> std key to e key read csn\n"
uint8_t dataLen = 0; "hf iclass calcnewkey --old 1122334455667788 --new 2233445566778899 -> std to std read csn");
char tempStr[50] = {0};
bool givenCSN = false;
bool old_elite = false;
bool elite = false;
bool errors = false;
uint8_t cmdp = 0;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { void *argtable[] = {
switch (tolower(param_getchar(Cmd, cmdp))) { arg_param_begin,
case 'h': arg_str0(NULL, "old", "<hex>", "Specify key as 8 bytes (16 hex symbols)"),
return usage_hf_iclass_calc_newkey(); arg_int0(NULL, "oki", "<dec>", "Old key index to select key from memory 'hf iclass managekeys'"),
case 'e': arg_str0(NULL, "new", "<hex>", "Specify key as 8 bytes (16 hex symbols)"),
dataLen = param_getstr(Cmd, cmdp, tempStr, sizeof(tempStr)); arg_int0(NULL, "nki", "<dec>", "New key index to select key from memory 'hf iclass managekeys'"),
if (dataLen == 2) arg_str0(NULL, "csn", "<hex>", "Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)"),
old_elite = true; arg_lit0(NULL, "elite", "Elite computations applied to new key"),
elite = true; arg_lit0(NULL, "elite2", "Elite computations applied to both old and new key"),
cmdp++; arg_param_end
break; };
case 'n': CLIExecWithReturn(ctx, Cmd, argtable, false);
dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr));
if (dataLen == 16) { int old_key_len = 0;
errors = param_gethex(tempStr, 0, NEWKEY, dataLen); uint8_t old_key[8] = {0};
} else if (dataLen == 1) { CLIGetHexWithReturn(ctx, 1, old_key, &old_key_len);
keyNbr = param_get8(Cmd, cmdp + 1);
if (keyNbr < ICLASS_KEYS_MAX) { int old_key_nr = arg_get_int_def(ctx, 2, -1);
memcpy(NEWKEY, iClass_Key_Table[keyNbr], 8);
if (old_key_len > 0 && old_key_nr >= 0) {
PrintAndLogEx(ERR, "Please specify old key or index, not both");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (old_key_len > 0) {
if (old_key_len != 8) {
PrintAndLogEx(ERR, "Old key is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
} else if (old_key_nr >= 0) {
if (old_key_nr < ICLASS_KEYS_MAX) {
memcpy(old_key, iClass_Key_Table[old_key_nr], 8);
PrintAndLogEx(SUCCESS, "Using old key[%d] " _GREEN_("%s"), old_key_nr, sprint_hex(iClass_Key_Table[old_key_nr], 8));
} else { } else {
PrintAndLogEx(WARNING, "\nERROR: NewKey Nbr is invalid\n"); PrintAndLogEx(ERR, "Key number is invalid");
errors = true; CLIParserFree(ctx);
return PM3_EINVARG;
} }
} else { } else {
PrintAndLogEx(WARNING, "\nERROR: NewKey is incorrect length\n"); PrintAndLogEx(ERR, "Please specify an old key or old key index");
errors = true; CLIParserFree(ctx);
return PM3_EINVARG;
} }
cmdp += 2;
break; int new_key_len = 0;
case 'o': uint8_t new_key[8] = {0};
dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); CLIGetHexWithReturn(ctx, 3, new_key, &new_key_len);
if (dataLen == 16) {
errors = param_gethex(tempStr, 0, OLDKEY, dataLen); int new_key_nr = arg_get_int_def(ctx, 4, -1);
} else if (dataLen == 1) {
keyNbr = param_get8(Cmd, cmdp + 1); if (new_key_len > 0 && new_key_nr >= 0) {
if (keyNbr < ICLASS_KEYS_MAX) { PrintAndLogEx(ERR, "Please specify new key or index, not both");
memcpy(OLDKEY, iClass_Key_Table[keyNbr], 8); CLIParserFree(ctx);
return PM3_EINVARG;
}
if (new_key_len > 0) {
if (new_key_len != 8) {
PrintAndLogEx(ERR, "New key is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
} else if (new_key_nr >= 0) {
if (new_key_nr < ICLASS_KEYS_MAX) {
memcpy(new_key, iClass_Key_Table[new_key_nr], 8);
PrintAndLogEx(SUCCESS, "Using new key[%d] " _GREEN_("%s"), new_key_nr, sprint_hex(iClass_Key_Table[new_key_nr], 8));
} else { } else {
PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); PrintAndLogEx(ERR, "Key number is invalid");
errors = true; CLIParserFree(ctx);
return PM3_EINVARG;
} }
} else { } else {
PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); PrintAndLogEx(ERR, "Please specify an new key or old key index");
errors = true; CLIParserFree(ctx);
return PM3_EINVARG;
} }
cmdp += 2;
break; int csn_len = 0;
case 's': uint8_t csn[8] = {0};
CLIGetHexWithReturn(ctx, 5, csn, &csn_len);
bool givenCSN = false;
if (csn_len > 0) {
givenCSN = true; givenCSN = true;
if (param_gethex(Cmd, cmdp + 1, CSN, 16)) if (csn_len != 8) {
return usage_hf_iclass_calc_newkey(); PrintAndLogEx(ERR, "CSN is incorrect length");
cmdp += 2; CLIParserFree(ctx);
break; return PM3_EINVARG;
default:
PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
errors = true;
break;
} }
} }
if (errors || cmdp < 4) return usage_hf_iclass_calc_newkey();
bool elite = arg_get_lit(ctx, 6);
bool old_elite = false;
if (arg_get_lit(ctx, 7)) {
elite = true;
old_elite = true;
}
CLIParserFree(ctx);
uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (givenCSN == false) { if (givenCSN == false) {
uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (select_only(CSN, CCNR, true) == false) { if (select_only(csn, CCNR, true) == false) {
DropField(); DropField();
return PM3_ESOFT; return PM3_ESOFT;
} }
} }
HFiClassCalcNewKey(CSN, OLDKEY, NEWKEY, xor_div_key, elite, old_elite, true); HFiClassCalcNewKey(csn, old_key, new_key, xor_div_key, elite, old_elite, true);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -2520,83 +2485,88 @@ static int printKeys(void) {
} }
static int CmdHFiClassManageKeys(const char *Cmd) { static int CmdHFiClassManageKeys(const char *Cmd) {
uint8_t keyNbr = 0; CLIParserContext *ctx;
uint8_t dataLen = 0; CLIParserInit(&ctx, "hf iclass managekeys",
uint8_t KEY[8] = {0}; "Manage iCLASS Keys in client memory",
char filename[FILE_PATH_SIZE]; "hf iclass managekeys --ki 0 -k 1122334455667788 -> set key\n"
uint8_t fileNameLen = 0; "hf iclass managekeys -f mykeys.bin --save -> save key file\n"
bool errors = false; "hf iclass managekeys -f mykeys.bin --load -> load key file\n"
uint8_t operation = 0; "hf iclass managekeys -p -> print keys");
char tempStr[20];
uint8_t cmdp = 0;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { void *argtable[] = {
switch (tolower(param_getchar(Cmd, cmdp))) { arg_param_begin,
case 'h': arg_str0("f", "file", "<filename>", "Specify a filename to use with load or save operations"),
return usage_hf_iclass_managekeys(); arg_str0("k", "key", "<hex>", "Access key as 16 hex symbols"),
case 'f': arg_int0(NULL, "ki", "<dec>", "Specify key index to set key in memory"),
fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); arg_lit0(NULL, "save", "Save keys in memory to file specified by filename"),
if (fileNameLen < 1) { arg_lit0(NULL, "load", "Load keys to memory from file specified by filename"),
PrintAndLogEx(ERR, "No filename found"); arg_lit0("p", "print", "Print keys loaded into memory"),
errors = true; arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
int key_len = 0;
uint8_t key[8] = {0};
CLIGetHexWithReturn(ctx, 2, key, &key_len);
uint8_t operation = 0;
if (key_len > 0) {
operation += 3;
if (key_len != 8) {
PrintAndLogEx(ERR, "Key is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
} }
cmdp += 2;
break;
case 'n':
keyNbr = param_get8(Cmd, cmdp + 1);
if (keyNbr >= ICLASS_KEYS_MAX) {
PrintAndLogEx(ERR, "Invalid block number, MAX is " _YELLOW_("%d"), ICLASS_KEYS_MAX);
errors = true;
} }
cmdp += 2;
break; int key_nr = arg_get_int_def(ctx, 3, -1);
case 'k':
operation += 3; //set key if (key_nr >= 0) {
dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); if (key_nr < ICLASS_KEYS_MAX) {
if (dataLen == 16) { //ul-c or ev1/ntag key length PrintAndLogEx(SUCCESS, "Setting key[%d] " _GREEN_("%s"), key_nr);
errors = param_gethex(tempStr, 0, KEY, dataLen);
} else { } else {
PrintAndLogEx(WARNING, "\nERROR: Key is incorrect length\n"); PrintAndLogEx(ERR, "Key number is invalid");
errors = true; CLIParserFree(ctx);
} return PM3_EINVARG;
cmdp += 2;
break;
case 'p':
operation += 4; //print keys in memory
cmdp++;
break;
case 'l':
operation += 5; //load keys from file
cmdp++;
break;
case 's':
operation += 6; //save keys to file
cmdp++;
break;
default:
PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
errors = true;
break;
} }
} }
if (errors) return usage_hf_iclass_managekeys();
if (arg_get_lit(ctx, 4)) { //save
operation += 6;
}
if (arg_get_lit(ctx, 5)) { //load
operation += 5;
}
if (arg_get_lit(ctx, 6)) { //print
operation += 4;
}
CLIParserFree(ctx);
if (operation == 0) { if (operation == 0) {
PrintAndLogEx(WARNING, "no operation specified (load, save, or print)\n"); PrintAndLogEx(ERR, "No operation specified (load, save, or print)\n");
return usage_hf_iclass_managekeys(); return PM3_EINVARG;
} }
if (operation > 6) { if (operation > 6) {
PrintAndLogEx(WARNING, "Too many operations specified\n"); PrintAndLogEx(ERR, "Too many operations specified\n");
return usage_hf_iclass_managekeys(); return PM3_EINVARG;
} }
if (operation > 4 && fileNameLen == 0) { if (operation > 4 && fnlen == 0) {
PrintAndLogEx(WARNING, "You must enter a filename when loading or saving\n"); PrintAndLogEx(ERR, "You must enter a filename when loading or saving\n");
return usage_hf_iclass_managekeys(); return PM3_EINVARG;
}
if (key_len > 0 && key_nr == -1) {
PrintAndLogEx(ERR, "Please specify key index when specifying key");
return PM3_EINVARG;
} }
switch (operation) { switch (operation) {
case 3: case 3:
memcpy(iClass_Key_Table[keyNbr], KEY, 8); memcpy(iClass_Key_Table[key_nr], key, 8);
return PM3_SUCCESS; return PM3_SUCCESS;
case 4: case 4:
return printKeys(); return printKeys();
@ -2628,7 +2598,7 @@ static void add_key(uint8_t *key) {
if (i == ICLASS_KEYS_MAX) { if (i == ICLASS_KEYS_MAX) {
PrintAndLogEx(INFO, "Couldn't find an empty keyslot"); PrintAndLogEx(INFO, "Couldn't find an empty keyslot");
} else { } else {
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf iclass managekeys p`") " to view keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf iclass managekeys -p`") " to view keys");
} }
} }
@ -2730,60 +2700,41 @@ static int iclass_chk_keys(uint8_t *keyBlock, uint32_t keycount) {
*/ */
static int CmdHFiClassCheckKeys(const char *Cmd) { static int CmdHFiClassCheckKeys(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass chk",
"Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag",
"hf iclass chk -f dictionaries/iclass_default_keys.dic\n"
"hf iclass chk -f dictionaries/iclass_default_keys.dic --elite");
// empty string void *argtable[] = {
if (strlen(Cmd) == 0) return usage_hf_iclass_chk(); arg_param_begin,
arg_str1("f", "file", "<filename>", "Dictionary file with default iclass keys"),
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 (raw)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool use_credit_key = arg_get_lit(ctx, 2);
bool use_elite = arg_get_lit(ctx, 3);
bool use_raw = arg_get_lit(ctx, 4);
CLIParserFree(ctx);
uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// elite key, raw key, standard key
bool use_elite = false;
bool use_raw = false;
bool use_credit_key = false;
bool found_key = false; bool found_key = false;
//bool found_credit = false; //bool found_credit = false;
bool got_csn = false; bool got_csn = false;
bool errors = false;
uint8_t cmdp = 0x00;
char filename[FILE_PATH_SIZE] = {0};
uint8_t fileNameLen = 0;
uint64_t t1 = msclock(); uint64_t t1 = msclock();
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h':
return usage_hf_iclass_chk();
case 'f':
fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename));
if (fileNameLen < 1) {
PrintAndLogEx(WARNING, _RED_("no filename found after f"));
errors = true;
}
cmdp += 2;
break;
case 'e':
use_elite = true;
cmdp++;
break;
case 'c':
use_credit_key = true;
cmdp++;
break;
case 'r':
use_raw = true;
cmdp++;
break;
default:
PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
if (errors) return usage_hf_iclass_chk();
uint8_t *keyBlock = NULL; uint8_t *keyBlock = NULL;
uint32_t keycount = 0; uint32_t keycount = 0;
@ -2935,89 +2886,84 @@ out:
// this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in. // this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in.
// Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work. // Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work.
static int CmdHFiClassLookUp(const char *Cmd) { static int CmdHFiClassLookUp(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass lookup",
"Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file",
"hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f dictionaries/iclass_default_keys.dic\n"
"hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f dictionaries/iclass_default_keys.dic --elite");
void *argtable[] = {
arg_param_begin,
arg_str1("f", "file", "<filename>", "Dictionary file with default iclass keys"),
arg_str1(NULL, "csn", "<hex>", "Specify CSN as 8 bytes (16 hex symbols)"),
arg_str1(NULL, "epurse", "<hex>", "Specify ePurse as 8 bytes (16 hex symbols)"),
arg_str1(NULL, "macs", "<hex>", "MACs"),
arg_lit0(NULL, "raw", "no computations applied to key (raw)"),
arg_lit0(NULL, "elite", "Elite computations applied to key"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
int csn_len = 0;
uint8_t csn[8] = {0};
CLIGetHexWithReturn(ctx, 2, csn, &csn_len);
if (csn_len > 0) {
if (csn_len != 8) {
PrintAndLogEx(ERR, "CSN is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
int epurse_len = 0;
uint8_t epurse[8] = {0};
CLIGetHexWithReturn(ctx, 3, epurse, &epurse_len);
if (epurse_len > 0) {
if (epurse_len != 8) {
PrintAndLogEx(ERR, "ePurse is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
int macs_len = 0;
uint8_t macs[8] = {0};
CLIGetHexWithReturn(ctx, 4, macs, &macs_len);
if (macs_len > 0) {
if (macs_len != 8) {
PrintAndLogEx(ERR, "MAC is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
bool use_elite = arg_get_lit(ctx, 5);
bool use_raw = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
uint8_t CSN[8];
uint8_t EPURSE[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
uint8_t MACS[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
uint8_t CCNR[12]; uint8_t CCNR[12];
uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; uint8_t MAC_TAG[4] = { 0, 0, 0, 0 };
// elite key, raw key, standard key
bool use_elite = false;
bool use_raw = false;
bool errors = false;
uint8_t cmdp = 0x00;
char filename[FILE_PATH_SIZE] = {0};
iclass_prekey_t *prekey = NULL; iclass_prekey_t *prekey = NULL;
int len = 0;
// if empty string
if (strlen(Cmd) == 0) errors = true;
// time // time
uint64_t t1 = msclock(); uint64_t t1 = msclock();
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h':
return usage_hf_iclass_lookup();
case 'f':
if (param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)) < 1) {
PrintAndLogEx(WARNING, "No filename found after f");
errors = true;
}
cmdp += 2;
break;
case 'u':
param_gethex_ex(Cmd, cmdp + 1, CSN, &len);
if (len >> 1 != sizeof(CSN)) {
PrintAndLogEx(WARNING, "Wrong CSN length, expected %zu got [%d]", sizeof(CSN), len >> 1);
errors = true;
}
cmdp += 2;
break;
case 'm':
param_gethex_ex(Cmd, cmdp + 1, MACS, &len);
if (len >> 1 != sizeof(MACS)) {
PrintAndLogEx(WARNING, "Wrong MACS length, expected %zu got [%d] ", sizeof(MACS), len >> 1);
errors = true;
} else {
memcpy(MAC_TAG, MACS + 4, 4);
}
cmdp += 2;
break;
case 'p':
param_gethex_ex(Cmd, cmdp + 1, EPURSE, &len);
if (len >> 1 != sizeof(EPURSE)) {
PrintAndLogEx(WARNING, "Wrong EPURSE length, expected %zu got [%d] ", sizeof(EPURSE), len >> 1);
errors = true;
}
cmdp += 2;
break;
case 'e':
use_elite = true;
cmdp++;
break;
case 'r':
use_raw = true;
cmdp++;
break;
default:
PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
if (errors) return usage_hf_iclass_lookup();
// stupid copy.. CCNR is a combo of epurse and reader nonce // stupid copy.. CCNR is a combo of epurse and reader nonce
memcpy(CCNR, EPURSE, 8); memcpy(CCNR, epurse, 8);
memcpy(CCNR + 8, MACS, 4); memcpy(CCNR + 8, macs, 4);
PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(CSN, sizeof(CSN))); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, sizeof(csn)));
PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(EPURSE, sizeof(EPURSE))); PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, sizeof(epurse)));
PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(MACS, sizeof(MACS))); PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, sizeof(macs)));
PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR)));
PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG)));
@ -3039,7 +2985,7 @@ static int CmdHFiClassLookUp(const char *Cmd) {
} }
PrintAndLogEx(SUCCESS, "Generating diversified keys..."); PrintAndLogEx(SUCCESS, "Generating diversified keys...");
GenerateMacKeyFrom(CSN, CCNR, use_raw, use_elite, keyBlock, keycount, prekey); GenerateMacKeyFrom(csn, CCNR, use_raw, use_elite, keyBlock, keycount, prekey);
if (use_elite) if (use_elite)
PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo"));

View file

@ -515,7 +515,7 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) {
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds", t1 / 1000); PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds", t1 / 1000);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim 2`") " again and collect new data"); PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim -t 2`") " again and collect new data");
return PM3_ESOFT; return PM3_ESOFT;
} }

View file

@ -109,19 +109,20 @@ Print keystore
``` ```
Options Options
--- ---
p : print keys loaded into memory -p, --print Print keys loaded into memory
pm3 --> hf iclass managekeys p
pm3 --> hf iclass managekeys -p
``` ```
Add key to keystore [0-7] Add key to keystore [0-7]
``` ```
Options Options
--- ---
n <keynbr> : specify the keyNbr to set in memory -f, --file <filename> Specify a filename to use with load or save operations
k <key> : set a key in memory --ki <dec> Specify key index to set key in memory
pm3 --> hf iclass managekeys n 3 k AFA785A7DAB33378 pm3 --> hf iclass managekeys --ki 3 -k AFA785A7DAB33378
``` ```
Encrypt iCLASS Block Encrypt iCLASS Block
@ -167,20 +168,23 @@ Simulate iCLASS
``` ```
Options Options
--- ---
0 <CSN> simulate the given CSN -t, --type <int> Simulation type to use
--csn <hex> Specify CSN as 8 bytes (16 hex symbols) to use with sim type 0
Types:
0 simulate the given CSN
1 simulate default CSN 1 simulate default CSN
2 Runs online part of LOCLASS attack 2 Runs online part of LOCLASS attack
3 Full simulation using emulator memory (see 'hf iclass eload') 3 Full simulation using emulator memory (see 'hf iclass eload')
4 Runs online part of LOCLASS attack against reader in keyroll mode 4 Runs online part of LOCLASS attack against reader in keyroll mode
pm3 --> hf iclass sim 3 pm3 --> hf iclass sim -t 3
``` ```
Simulate iCLASS Sequence Simulate iCLASS Sequence
``` ```
pm3 --> hf iclass dump --ki 0 pm3 --> hf iclass dump --ki 0
pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin
pm3 --> hf iclass sim 3 pm3 --> hf iclass sim -t 3
``` ```
Extract custom iCLASS key (loclass attack) Extract custom iCLASS key (loclass attack)
@ -189,11 +193,11 @@ Options
--- ---
f <filename> : specify a filename to clone from f <filename> : specify a filename to clone from
k <key> : Access Key as 16 hex symbols or 1 hex to select key from memory k <key> : Access Key as 16 hex symbols or 1 hex to select key from memory
e : If 'e' is specified, elite computations applied to key --elite : Elite computations applied to key
pm3 --> hf iclass sim 2 pm3 --> hf iclass sim -t 2
pm3 --> hf iclass loclass -f iclass_mac_attack.bin pm3 --> hf iclass loclass -f iclass_mac_attack.bin
pm3 --> hf iclass managekeys n 7 k <Kcus> pm3 --> hf iclass managekeys --ki 7 -k <Kcus>
pm3 --> hf iclass dump --ki 7 --elite pm3 --> hf iclass dump --ki 7 --elite
``` ```
@ -201,13 +205,14 @@ Verify custom iCLASS key
``` ```
Options Options
--- ---
f <filename> : Dictionary file with default iCLASS keys -f, --file <filename> Dictionary file with default iclass keys
u : CSN --csn <hex> Specify CSN as 8 bytes (16 hex symbols)
p : EPURSE --epurse <hex> Specify ePurse as 8 bytes (16 hex symbols)
m : macs --macs <hex> MACs
e : elite --raw no computations applied to key (raw)
--elite Elite computations applied to key
pm3 --> hf iclass lookup u 010a0ffff7ff12e0 p feffffffffffffff m 66348979153c41b9 f iclass_default_keys e pm3 --> hf iclass lookup --csn 010a0ffff7ff12e0 --epurse feffffffffffffff --macs 66348979153c41b9 -f iclass_default_keys --elite
``` ```
## MIFARE ## MIFARE

View file

@ -4,7 +4,7 @@ This document is primarily intended for understanding `hf iclass loclass` and fi
LOCLASS aim is to recover the used masterkey for that specific reader configured in Elite mode / High Security mode. LOCLASS aim is to recover the used masterkey for that specific reader configured in Elite mode / High Security mode.
LOCLASS, is a two part attack. First is the online part where you gather needed information from the reader by presenting a carefully selected CSN and save the responses to file. For the first part you run `hf iclass sim 2` and take notice of the saved filename. LOCLASS, is a two part attack. First is the online part where you gather needed information from the reader by presenting a carefully selected CSN and save the responses to file. For the first part you run `hf iclass sim -t 2` and take notice of the saved filename.
The second part is offline, where the information gathered from the first step is used in a series of DES operations to figure out the used The second part is offline, where the information gathered from the first step is used in a series of DES operations to figure out the used
masterkey. masterkey.
@ -21,6 +21,6 @@ run `hf iclass loclass --test`.
This test mode uses two files. This test mode uses two files.
- `iclass_dump.bin` - `iclass_dump.bin`
this is a sample file from `hf iclass sim 2`, with complete keytable recovery, using 128 carefully selected CSN and the file contains the MAC results from reader. this is a sample file from `hf iclass sim -t 2`, with complete keytable recovery, using 128 carefully selected CSN and the file contains the MAC results from reader.
- `iclass_key.bin` - `iclass_key.bin`
this is file shall contain the legacy masterkey, AA1 key. loclass uses it to verify that permutation / reversing / generation of key is correct. this is file shall contain the legacy masterkey, AA1 key. loclass uses it to verify that permutation / reversing / generation of key is correct.