Merge remote-tracking branch 'origin/master' into emrtd
This commit is contained in:
@@ -135,12 +135,6 @@ The separation from official Proxmark3 repo gives us a lot of freedom to create
|
||||
The official PM3-GUI from Gaucho will not work.
|
||||
The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers.
|
||||
|
||||
## The end
|
||||
|
||||
- July 2018 [@herrmann1001](https://mobile.twitter.com/herrmann1001)
|
||||
- updated Feb 2019 [@5w0rdfish](https://mobile.twitter.com/5w0rdFish)
|
||||
- updated 2019 [@doegox](https://mobile.twitter.com/doegox)
|
||||
|
||||
# Donations
|
||||
|
||||
Nothing says thank you as much as a donation. So if you feel the love, do feel free to become a iceman patron. For some tiers it comes with rewards.
|
||||
|
||||
@@ -4,26 +4,32 @@ local ansicolors = require('ansicolors')
|
||||
|
||||
copyright = ''
|
||||
author = "Iceman"
|
||||
version = 'v1.0.2'
|
||||
version = 'v1.0.3'
|
||||
desc = [[
|
||||
This script tries to set UID on a mifare Ultralight magic card which either
|
||||
- answers to chinese backdoor commands
|
||||
- brickable magic tag (must write in one session)
|
||||
|
||||
It defaults to GEN1A type of uid changeable card.
|
||||
]]
|
||||
example = [[
|
||||
-- backdoor magic tag
|
||||
-- backdoor magic tag (gen1a)
|
||||
script run hf_mfu_setuid -u 11223344556677
|
||||
|
||||
-- brickable magic tag
|
||||
-- backdoor magic tag (gen1b)
|
||||
script run hf_mfu_setuid -b -u 11223344556677
|
||||
|
||||
-- brickable magic tag (gen2)
|
||||
script run hf_mfu_setuid -2 -u 11223344556677
|
||||
]]
|
||||
usage = [[
|
||||
script run hf_mfu_setuid [-h] [-b] [-u <uid>]
|
||||
script run hf_mfu_setuid [-h] [-b] [-2] [-u <uid>]
|
||||
]]
|
||||
arguments = [[
|
||||
-h : this help
|
||||
-u <UID> : UID (14 hexsymbols)
|
||||
-b : write to brickable magic tag
|
||||
-b : write to magic tag GEN1B
|
||||
-2 : write to brickable magic tag GEN2
|
||||
]]
|
||||
|
||||
local DEBUG = true
|
||||
@@ -65,23 +71,33 @@ local function help()
|
||||
end
|
||||
--
|
||||
--- Set UID on magic command enabled
|
||||
function magicUID(b0, b1, b2)
|
||||
function magicUID(b0, b1, b2, isgen1a)
|
||||
|
||||
print('Using backdoor Magic tag function')
|
||||
if isgen1a then
|
||||
print('Using backdoor Magic tag (gen1a) function')
|
||||
else
|
||||
print('Using backdoor Magic tag (gen1b) function')
|
||||
end
|
||||
|
||||
-- write block 0
|
||||
core.console('hf 14a raw -k -a -b 7 40')
|
||||
core.console('hf 14a raw -k -a 43')
|
||||
if isgen1a then
|
||||
core.console('hf 14a raw -k -a 43')
|
||||
end
|
||||
core.console('hf 14a raw -c -a A200'..b0)
|
||||
|
||||
-- write block 1
|
||||
core.console('hf 14a raw -k -a -b 7 40')
|
||||
core.console('hf 14a raw -k -a 43')
|
||||
if isgen1a then
|
||||
core.console('hf 14a raw -k -a 43')
|
||||
end
|
||||
core.console('hf 14a raw -c -a A201'..b1)
|
||||
|
||||
-- write block 2
|
||||
core.console('hf 14a raw -k -a -b 7 40')
|
||||
core.console('hf 14a raw -k -a 43')
|
||||
if isgen1a then
|
||||
core.console('hf 14a raw -k -a 43')
|
||||
end
|
||||
core.console('hf 14a raw -c -a A202'..b2)
|
||||
end
|
||||
--
|
||||
@@ -113,10 +129,11 @@ function main(args)
|
||||
local tagtype = 1
|
||||
|
||||
-- Read the parameters
|
||||
for o, a in getopt.getopt(args, 'hu:b') do
|
||||
for o, a in getopt.getopt(args, 'hu:b2') do
|
||||
if o == 'h' then return help() end
|
||||
if o == 'u' then uid = a end
|
||||
if o == 'b' then tagtype = 2 end
|
||||
if o == '2' then tagtype = 3 end
|
||||
end
|
||||
|
||||
-- uid string checks
|
||||
@@ -137,10 +154,11 @@ function main(args)
|
||||
|
||||
core.clearCommandBuffer()
|
||||
|
||||
if tagtype == 2 then
|
||||
if tagtype == 3 then
|
||||
brickableUID(block0, block1, block2)
|
||||
else
|
||||
magicUID(block0, block1, block2)
|
||||
local is_gen1a = (tagtype == 1)
|
||||
magicUID(block0, block1, block2, is_gen1a)
|
||||
end
|
||||
|
||||
--halt
|
||||
|
||||
@@ -2135,20 +2135,30 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
||||
}
|
||||
|
||||
static uint16_t get_sw(uint8_t *d, uint8_t n) {
|
||||
if (n < 2)
|
||||
if (n < 2) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
n -= 2;
|
||||
return d[n] * 0x0100 + d[n + 1];
|
||||
}
|
||||
|
||||
static uint64_t inc_sw_error_occurence(uint16_t sw, uint64_t all_sw[256][256]) {
|
||||
uint8_t sw1 = (uint8_t)(sw >> 8);
|
||||
uint8_t sw2 = (uint8_t)(0xff & sw);
|
||||
if (sw1 == 0x90 && sw2 == 0x00) {
|
||||
return 0; // Don't count successes.
|
||||
}
|
||||
if (sw1 == 0x6d && sw2 == 0x00) {
|
||||
return 0xffffffffffffffffULL; // Always max "Instruction not supported".
|
||||
}
|
||||
return ++all_sw[sw1][sw2];
|
||||
}
|
||||
|
||||
static int CmdHf14AFindapdu(const char *Cmd) {
|
||||
// TODO: What response values should be considerd "valid" or "instersting" (worth dispalying)?
|
||||
// TODO: Option to select AID/File (and skip INS 0xA4).
|
||||
// TODO: Validate the decoding of the APDU (not specific to this command, check
|
||||
// https://cardwerk.com/smartcards/smartcard_standard_ISO7816-4_5_basic_organizations.aspx#chap5_3_2).
|
||||
// TODO: Check all cases (APDUs) with no data bytes (no/short/extended length).
|
||||
// TODO: Option to blacklist instructions (or whole APDUs).
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf 14a apdufind",
|
||||
"Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1P2 commands.\n"
|
||||
@@ -2157,16 +2167,19 @@ static int CmdHf14AFindapdu(const char *Cmd) {
|
||||
"Tag must be on antenna before running.",
|
||||
"hf 14a apdufind\n"
|
||||
"hf 14a apdufind --cla 80\n"
|
||||
"hf 14a apdufind --cla 80 --error-limit 20 --skip-ins a4 --skip-ins b0\n"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("c", "cla", "<hex>", "Start value of CLASS (1 hex byte)"),
|
||||
arg_str0("i", "ins", "<hex>", "Start value of INSTRUCTION (1 hex byte)"),
|
||||
arg_str0(NULL, "p1", "<hex>", "Start value of P1 (1 hex byte)"),
|
||||
arg_str0(NULL, "p2", "<hex>", "Start value of P2 (1 hex byte)"),
|
||||
arg_u64_0("r", "reset", "<number>", "Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes"),
|
||||
arg_lit0("v", "verbose", "Verbose output"),
|
||||
arg_str0("c", "cla", "<hex>", "Start value of CLASS (1 hex byte)"),
|
||||
arg_str0("i", "ins", "<hex>", "Start value of INSTRUCTION (1 hex byte)"),
|
||||
arg_str0(NULL, "p1", "<hex>", "Start value of P1 (1 hex byte)"),
|
||||
arg_str0(NULL, "p2", "<hex>", "Start value of P2 (1 hex byte)"),
|
||||
arg_u64_0("r", "reset", "<number>", "Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes"),
|
||||
arg_u64_0("e", "error-limit", "<number>", "Maximum times an status word other than 0x9000 or 0x6D00 is shown. Default is 256."),
|
||||
arg_strx0("s", "skip-ins", "<hex>", "Do not test an instructions (can be specifed multiple times)"),
|
||||
arg_lit0("v", "verbose", "Verbose output"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
@@ -2183,8 +2196,12 @@ static int CmdHf14AFindapdu(const char *Cmd) {
|
||||
int p2_len = 0;
|
||||
uint8_t p2_arg[1] = {0};
|
||||
CLIGetHexWithReturn(ctx, 4, p2_arg, &p2_len);
|
||||
uint64_t reset_time = arg_get_u64_def(ctx, 5, 5 * 60); // Reset every 5 minutes.
|
||||
bool verbose = arg_get_lit(ctx, 6);
|
||||
uint64_t reset_time = arg_get_u64_def(ctx, 5, 5 * 60);
|
||||
uint64_t error_limit = arg_get_u64_def(ctx, 6, 256);
|
||||
int ignore_ins_len = 0;
|
||||
uint8_t ignore_ins_arg[250] = {0};
|
||||
CLIGetHexWithReturn(ctx, 7, ignore_ins_arg, &ignore_ins_len);
|
||||
bool verbose = arg_get_lit(ctx, 8);
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
@@ -2211,6 +2228,9 @@ static int CmdHf14AFindapdu(const char *Cmd) {
|
||||
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
||||
|
||||
bool inc_p1 = true;
|
||||
bool skip_ins = false;
|
||||
uint64_t all_sw[256][256] = {0};
|
||||
uint64_t sw_occurences = 0;
|
||||
uint64_t t_start = msclock();
|
||||
uint64_t t_last_reset = msclock();
|
||||
|
||||
@@ -2218,12 +2238,25 @@ static int CmdHf14AFindapdu(const char *Cmd) {
|
||||
do {
|
||||
do {
|
||||
do {
|
||||
retry_ins:
|
||||
// Exit (was the Enter key pressed)?
|
||||
if (kbd_enter_pressed()) {
|
||||
PrintAndLogEx(INFO, "User interrupted detected. Aborting");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Skip/Ignore this instrctuion?
|
||||
for (int i = 0; i < ignore_ins_len; i++) {
|
||||
if (ins == ignore_ins_arg[i]) {
|
||||
skip_ins = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip_ins) {
|
||||
skip_ins = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2);
|
||||
}
|
||||
@@ -2233,38 +2266,47 @@ static int CmdHf14AFindapdu(const char *Cmd) {
|
||||
int command_n = sizeof(command);
|
||||
res = ExchangeAPDU14a(command, command_n, activate_field, keep_field_on, response, sizeof(response), &response_n);
|
||||
if (res) {
|
||||
continue;
|
||||
DropField();
|
||||
activate_field = true;
|
||||
goto retry_ins;
|
||||
}
|
||||
uint16_t sw = get_sw(response, response_n);
|
||||
sw_occurences = inc_sw_error_occurence(sw, all_sw);
|
||||
|
||||
// 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)");
|
||||
if (sw_occurences < error_limit) {
|
||||
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;
|
||||
DropField();
|
||||
activate_field = true;
|
||||
goto retry_ins;
|
||||
}
|
||||
sw = get_sw(response, response_n);
|
||||
sw_occurences = inc_sw_error_occurence(sw, all_sw);
|
||||
command_with_le = true;
|
||||
}
|
||||
|
||||
// Check response.
|
||||
sw = get_sw(response, response_n);
|
||||
if (sw != 0x6a86 &&
|
||||
sw != 0x6986 &&
|
||||
sw != 0x6d00
|
||||
) {
|
||||
// Show response.
|
||||
if (sw_occurences < error_limit) {
|
||||
logLevel_t log_level = INFO;
|
||||
if (sw == 0x9000) {
|
||||
log_level = SUCCESS;
|
||||
}
|
||||
if (command_with_le) {
|
||||
PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X00\": %04X (%s)", cla, ins, p1, p2,
|
||||
PrintAndLogEx(log_level, "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,
|
||||
PrintAndLogEx(log_level, "Got response for APDU \"%02X%02X%02X%02X\": %04X (%s)", cla, ins, p1, p2,
|
||||
sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||
}
|
||||
// Show response data.
|
||||
|
||||
@@ -657,7 +657,7 @@ void print_progress(size_t count, uint64_t max, barMode_t style) {
|
||||
"\xe2\x96\x88",
|
||||
};
|
||||
|
||||
uint8_t mode = session.supports_colors;
|
||||
uint8_t mode = (session.emoji_mode == EMOJI);
|
||||
|
||||
const char *block[] = {"#", "\xe2\x96\x88"};
|
||||
// use a 3-byte space in emoji mode to ease computations
|
||||
@@ -689,11 +689,15 @@ void print_progress(size_t count, uint64_t max, barMode_t style) {
|
||||
char *cbar = calloc(collen, sizeof(uint8_t));
|
||||
|
||||
// Add colors
|
||||
int p60 = unit * (width * 60 / 100);
|
||||
int p20 = unit * (width * 20 / 100);
|
||||
snprintf(cbar, collen, _GREEN_("%.*s"), p60, bar);
|
||||
snprintf(cbar + strlen(cbar), collen - strlen(cbar), _CYAN_("%.*s"), p20, bar + p60);
|
||||
snprintf(cbar + strlen(cbar), collen - strlen(cbar), _YELLOW_("%.*s"), unit * width - p60 - p20, bar + p60 + p20);
|
||||
if (session.supports_colors) {
|
||||
int p60 = unit * (width * 60 / 100);
|
||||
int p20 = unit * (width * 20 / 100);
|
||||
snprintf(cbar, collen, _GREEN_("%.*s"), p60, bar);
|
||||
snprintf(cbar + strlen(cbar), collen - strlen(cbar), _CYAN_("%.*s"), p20, bar + p60);
|
||||
snprintf(cbar + strlen(cbar), collen - strlen(cbar), _YELLOW_("%.*s"), unit * width - p60 - p20, bar + p60 + p20);
|
||||
} else {
|
||||
snprintf(cbar, collen, "%s", bar);
|
||||
}
|
||||
|
||||
size_t len = strlen(cbar) + 32;
|
||||
char *buffer = calloc(len, sizeof(uint8_t));
|
||||
|
||||
Reference in New Issue
Block a user