mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-22 06:13:51 -07:00
Reworked hf 14a apdufuzz (now apdufind).
* Renamed to apdufind. Fuzzing is something different than this simple enumeration. * Removed Le as parameter. This is just the maximum response bytes expected. Not much to find here with this simple tool. * Sweep through all values, even if a start value was given (don't stop at 0). * Changed sweep oder to INS->P1->P2->CLA. This way instructions are more quickly found. * Show response data, if there are any (hex & ascii). * Retry command with Le = 0x0100 (extended length APDU) if there was a length error return code. * Improved the output with general information. * Fixed bug: It's now possible to run the command without parameters.
This commit is contained in:
parent
5ca0281c03
commit
cd625ca759
1 changed files with 96 additions and 76 deletions
|
@ -2127,128 +2127,148 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) {
|
||||||
n -= 2;
|
n -= 2;
|
||||||
return d[n] * 0x0100 + d[n + 1];
|
return d[n] * 0x0100 + d[n + 1];
|
||||||
}
|
}
|
||||||
static int CmdHf14AFuzzapdu(const char *Cmd) {
|
|
||||||
|
static int CmdHf14AFindapdu(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf 14a apdufuzz",
|
CLIParserInit(&ctx, "hf 14a apdufind",
|
||||||
"Fuzz APDU's of ISO7816 protocol to find valid CLS/INS/P1P2/LE commands.\n"
|
"Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1P2 commands.\n"
|
||||||
"It loops all 256 possible values for each byte.\n"
|
"It loops all 256 possible values for each byte.\n"
|
||||||
|
"The loop oder is INS->P1->P2->CLA\n"
|
||||||
"Tag must be on antenna before running.",
|
"Tag must be on antenna before running.",
|
||||||
"hf 14a apdufuzz\n"
|
"hf 14a apdufind\n"
|
||||||
"hf 14a apdufuzz --cla 80\n"
|
"hf 14a apdufind --cla 80\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
void *argtable[] = {
|
void *argtable[] = {
|
||||||
arg_param_begin,
|
arg_param_begin,
|
||||||
arg_str0(NULL, "cla", "<hex>", "start CLASS value (1 hex byte)"),
|
arg_str0("c", "cla", "<hex>", "start CLASS value (1 hex byte)"),
|
||||||
arg_str0(NULL, "ins", "<hex>", "start INSTRUCTION value (1 hex byte)"),
|
arg_str0("i", "ins", "<hex>", "start INSTRUCTION value (1 hex byte)"),
|
||||||
arg_str0(NULL, "p1", "<hex>", "start P1 value (1 hex byte)"),
|
arg_str0(NULL, "p1", "<hex>", "start P1 value (1 hex byte)"),
|
||||||
arg_str0(NULL, "p2", "<hex>", "start P2 value (1 hex byte)"),
|
arg_str0(NULL, "p2", "<hex>", "start P2 value (1 hex byte)"),
|
||||||
arg_str0(NULL, "le", "<hex>", "start LENGTH value (1 hex byte)"),
|
|
||||||
arg_lit0("v", "verbose", "verbose output"),
|
arg_lit0("v", "verbose", "verbose output"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
};
|
};
|
||||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
int cla_len = 0;
|
int cla_len = 0;
|
||||||
uint8_t cla[1] = {0};
|
uint8_t cla_arg[1] = {0};
|
||||||
CLIGetHexWithReturn(ctx, 1, cla, &cla_len);
|
CLIGetHexWithReturn(ctx, 1, cla_arg, &cla_len);
|
||||||
|
|
||||||
int ins_len = 0;
|
int ins_len = 0;
|
||||||
uint8_t ins[1] = {0};
|
uint8_t ins_arg[1] = {0};
|
||||||
CLIGetHexWithReturn(ctx, 2, ins, &ins_len);
|
CLIGetHexWithReturn(ctx, 2, ins_arg, &ins_len);
|
||||||
|
|
||||||
int p1_len = 0;
|
int p1_len = 0;
|
||||||
uint8_t p1[1] = {0};
|
uint8_t p1_arg[1] = {0};
|
||||||
CLIGetHexWithReturn(ctx, 3, p1, &p1_len);
|
CLIGetHexWithReturn(ctx, 3, p1_arg, &p1_len);
|
||||||
|
|
||||||
int p2_len = 0;
|
int p2_len = 0;
|
||||||
uint8_t p2[1] = {0};
|
uint8_t p2_arg[1] = {0};
|
||||||
CLIGetHexWithReturn(ctx, 4, p2, &p2_len);
|
CLIGetHexWithReturn(ctx, 4, p2_arg, &p2_len);
|
||||||
|
|
||||||
int le_len = 0;
|
bool verbose = arg_get_lit(ctx, 5);
|
||||||
uint8_t le[1] = {0};
|
|
||||||
CLIGetHexWithReturn(ctx, 5, le, &le_len);
|
|
||||||
|
|
||||||
bool verbose = arg_get_lit(ctx, 6);
|
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
bool activate_field = true;
|
bool activate_field = true;
|
||||||
bool keep_field_on = true;
|
bool keep_field_on = true;
|
||||||
|
uint8_t cla = cla_arg[0];
|
||||||
uint8_t a = cla[0];
|
uint8_t ins = ins_arg[0];
|
||||||
uint8_t b = ins[0];
|
uint8_t p1 = p1_arg[0];
|
||||||
uint8_t c = p1[0];
|
uint8_t p2 = p2_arg[0];
|
||||||
uint8_t d = p2[0];
|
|
||||||
uint8_t e = le[0];
|
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Starting the apdu fuzzer [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " LE " _GREEN_("%02x")" ]", a,b,c,d,e);
|
|
||||||
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
|
||||||
|
|
||||||
uint8_t response[PM3_CMD_DATA_SIZE];
|
uint8_t response[PM3_CMD_DATA_SIZE];
|
||||||
int resplen = 0;
|
int response_n = 0;
|
||||||
|
|
||||||
uint8_t aSELECT_AID[80];
|
uint8_t aSELECT_AID[80];
|
||||||
int aSELECT_AID_n = 0;
|
int aSELECT_AID_n = 0;
|
||||||
|
|
||||||
|
// Check if the tag reponde to APDUs.
|
||||||
|
PrintAndLogEx(INFO, "Sending a test APDU (select file command) to check if the tag is responding to APDU");
|
||||||
param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
|
param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
|
||||||
int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &response_n);
|
||||||
if (res) {
|
if (res) {
|
||||||
DropField();
|
DropField();
|
||||||
|
PrintAndLogEx(FAILED, "Tag did not responde to a test APDU (select file command). Aborting");
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
PrintAndLogEx(SUCCESS, "Got response. Starting the APDU finder [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla,ins,p1,p2);
|
||||||
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
||||||
|
|
||||||
if (activate_field)
|
|
||||||
activate_field = false;
|
activate_field = false;
|
||||||
|
|
||||||
uint64_t t1 = msclock();
|
uint64_t t1 = msclock();
|
||||||
|
|
||||||
|
// Enumerate APDUs.
|
||||||
do {
|
do {
|
||||||
do {
|
do {
|
||||||
do {
|
do {
|
||||||
do {
|
do {
|
||||||
do {
|
// Exit (was the Enter key pressed)?
|
||||||
if (kbd_enter_pressed()) {
|
if (kbd_enter_pressed()) {
|
||||||
|
PrintAndLogEx(INFO, "User interrupted detected. Aborting");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t foo[5] = {a, b, c, d, e};
|
|
||||||
int foo_n = sizeof(foo);
|
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
PrintAndLogEx(INFO, "%s", sprint_hex(foo, sizeof(foo)));
|
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla,ins,p1,p2);
|
||||||
}
|
}
|
||||||
res = ExchangeAPDU14a(foo, foo_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
|
|
||||||
|
// Send APDU.
|
||||||
|
uint8_t command[4] = {cla, ins, p1, p2};
|
||||||
|
int command_n = sizeof(command);
|
||||||
|
res = ExchangeAPDU14a(command, command_n, activate_field, keep_field_on, response, sizeof(response), &response_n);
|
||||||
if (res) {
|
if (res) {
|
||||||
e++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t sw = get_sw(response, resplen);
|
// Was there and length error? If so, try with Le length (case 2 instad of case 1,
|
||||||
|
// https://stackoverflow.com/a/30679558). Le = 0x00 will get interpreted as extended length APDU
|
||||||
|
// with Le being 0x0100.
|
||||||
|
uint16_t sw = get_sw(response, response_n);
|
||||||
|
bool command_with_le = false;
|
||||||
|
if (sw == 0x6700) {
|
||||||
|
PrintAndLogEx(INFO, "Got response for APDU: %02X%02X%02X%02X (%04x - %s)", cla,ins,p1,p2,
|
||||||
|
sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
PrintAndLogEx(INFO, "Resending current command with Le = 0x0100 (extended length APDU)");
|
||||||
|
uint8_t command2[7] = {cla, ins, p1, p2, 0x00};
|
||||||
|
int command2_n = sizeof(command2);
|
||||||
|
res = ExchangeAPDU14a(command2, command2_n, activate_field, keep_field_on, response, sizeof(response), &response_n);
|
||||||
|
if (res) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
command_with_le = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check response.
|
||||||
|
// TODO: What response values should be considerd "valid" or "instersting"?
|
||||||
|
sw = get_sw(response, response_n);
|
||||||
if (sw != 0x6a86 &&
|
if (sw != 0x6a86 &&
|
||||||
sw != 0x6986 &&
|
sw != 0x6986 &&
|
||||||
sw != 0x6d00
|
sw != 0x6d00
|
||||||
) {
|
) {
|
||||||
PrintAndLogEx(INFO, "%02X %02X %02X %02X %02X (%04x - %s)", a,b,c,d,e, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
if (command_with_le) {
|
||||||
|
PrintAndLogEx(INFO, "Got response for APDU: %02X%02X%02X%02X00 (%04x - %s)", cla,ins,p1,p2,
|
||||||
|
sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, "Got response for APDU: %02X%02X%02X%02X (%04x - %s)", cla,ins,p1,p2,
|
||||||
|
sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||||
}
|
}
|
||||||
e++;
|
// Show response data.
|
||||||
if (verbose) {
|
if (response_n > 2) {
|
||||||
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
|
PrintAndLogEx(INFO, "Response data is: %s | %s", sprint_hex_inrow(response, response_n-2),
|
||||||
|
sprint_ascii(response, response_n-2));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} while (e);
|
} while (++ins != ins_arg[0]);
|
||||||
d++;
|
p1++;
|
||||||
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
|
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla,ins,p1,p2);
|
||||||
} while (d);
|
} while (p1 != p1_arg[0]);
|
||||||
c++;
|
p2++;
|
||||||
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
|
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla,ins,p1,p2);
|
||||||
} while (c);
|
} while (p2 != p2_arg[0]);
|
||||||
b++;
|
cla++;
|
||||||
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
|
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla,ins,p1,p2);
|
||||||
} while (b);
|
} while (cla != cla_arg[0]);
|
||||||
a++;
|
|
||||||
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
|
|
||||||
} while(a);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
|
PrintAndLogEx(SUCCESS, "Runtime: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
|
||||||
DropField();
|
DropField();
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -2266,7 +2286,7 @@ static command_t CommandTable[] = {
|
||||||
{"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
|
{"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
|
||||||
{"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"},
|
{"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"},
|
||||||
{"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"},
|
{"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"},
|
||||||
{"apdufuzz", CmdHf14AFuzzapdu, IfPm3Iso14443a, "Fuzz APDU - CLA/INS/P1P2"},
|
{"apdufind", CmdHf14AFindapdu, IfPm3Iso14443a, "Enuerate APDUs - CLA/INS/P1P2"},
|
||||||
{NULL, NULL, NULL, NULL}
|
{NULL, NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue