mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 22:03:42 -07:00
"hf mf supercard --furui" - add furui supercard key covery. Thanks to Foxushka for the PoC
This commit is contained in:
parent
636e6bcc6b
commit
f0a6b1bd67
1 changed files with 260 additions and 156 deletions
|
@ -6753,6 +6753,102 @@ static int CmdHf14AGen3Freeze(const char *Cmd) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FURUI_MAX_TRACES 8
|
||||||
|
static int mfc_furui_recovery(uint8_t items, uint8_t tracedata[FURUI_MAX_TRACES][18]) {
|
||||||
|
// recover key from collected traces
|
||||||
|
// outer loop
|
||||||
|
for (uint8_t i = 0; i < items; i++) {
|
||||||
|
|
||||||
|
// first
|
||||||
|
nonces_t data;
|
||||||
|
data.cuid = bytes_to_num(tracedata[i], 4);
|
||||||
|
data.nonce = bytes_to_num(tracedata[i] + 6, 4);
|
||||||
|
data.nr = bytes_to_num(tracedata[i] + 10, 4);
|
||||||
|
data.ar = bytes_to_num(tracedata[i] + 14, 4);
|
||||||
|
data.at = 0;
|
||||||
|
|
||||||
|
// inner loop
|
||||||
|
for (uint8_t j = i + 1; j < items; j++) {
|
||||||
|
|
||||||
|
uint8_t *p = tracedata[j];
|
||||||
|
PrintAndLogEx(INFO, "%u... %s", i, sprint_hex_inrow(p, 18));
|
||||||
|
|
||||||
|
// since data stored as block number but its the same key for all blocks in one sector
|
||||||
|
// we compare with sector number here
|
||||||
|
uint8_t s = mfSectorNum(tracedata[i][4]);
|
||||||
|
if (mfSectorNum(p[4]) == s) {
|
||||||
|
|
||||||
|
data.nonce2 = bytes_to_num(p + 6, 4);
|
||||||
|
data.nr2 = bytes_to_num(p + 10, 4);
|
||||||
|
data.ar2 = bytes_to_num(p + 14, 4);
|
||||||
|
data.sector = s;
|
||||||
|
data.keytype = tracedata[i][5];
|
||||||
|
data.state = FIRST;
|
||||||
|
|
||||||
|
uint64_t key64 = -1;
|
||||||
|
if (mfkey32_moebius(&data, &key64)) {
|
||||||
|
PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]",
|
||||||
|
sprint_hex_inrow(tracedata[i], 4),
|
||||||
|
data.sector,
|
||||||
|
(data.keytype == 0x60) ? 'A' : 'B',
|
||||||
|
key64
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mfc_supercard_gen2_recovery(uint8_t items, uint8_t tracedata[FURUI_MAX_TRACES][18]) {
|
||||||
|
for (uint8_t i = 0; i < items; i++) {
|
||||||
|
uint8_t *tmp = tracedata[i];
|
||||||
|
|
||||||
|
// first
|
||||||
|
uint16_t NT0 = (tmp[6] << 8) | tmp[7];
|
||||||
|
|
||||||
|
nonces_t data;
|
||||||
|
data.cuid = bytes_to_num(tmp, 4);
|
||||||
|
data.nonce = prng_successor(NT0, 31);
|
||||||
|
data.nr = bytes_to_num(tmp + 8, 4);
|
||||||
|
data.ar = bytes_to_num(tmp + 12, 4);
|
||||||
|
data.at = 0;
|
||||||
|
|
||||||
|
// second
|
||||||
|
for (uint8_t j = i + 1; j < items; j++) {
|
||||||
|
uint8_t *p = tracedata[j];
|
||||||
|
|
||||||
|
// since data stored as block number but its the same key for all blocks in one sector
|
||||||
|
// we compare with sector number here
|
||||||
|
uint8_t s = mfSectorNum(tmp[5]);
|
||||||
|
if (mfSectorNum(p[5]) == s) {
|
||||||
|
|
||||||
|
NT0 = (p[6] << 8) | p[7];
|
||||||
|
|
||||||
|
data.nonce2 = prng_successor(NT0, 31);
|
||||||
|
data.nr2 = bytes_to_num(p + 8, 4);
|
||||||
|
data.ar2 = bytes_to_num(p + 12, 4);
|
||||||
|
data.sector = s;
|
||||||
|
data.keytype = tmp[4];
|
||||||
|
data.state = FIRST;
|
||||||
|
|
||||||
|
uint64_t key64 = -1;
|
||||||
|
if (mfkey32_moebius(&data, &key64)) {
|
||||||
|
PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]",
|
||||||
|
sprint_hex_inrow(tmp, 4),
|
||||||
|
data.sector,
|
||||||
|
(data.keytype == 0x60) ? 'A' : 'B',
|
||||||
|
key64
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHf14AMfSuperCard(const char *Cmd) {
|
static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf mf supercard",
|
CLIParserInit(&ctx, "hf mf supercard",
|
||||||
|
@ -6765,6 +6861,7 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
arg_param_begin,
|
arg_param_begin,
|
||||||
arg_lit0("r", "reset", "Reset card"),
|
arg_lit0("r", "reset", "Reset card"),
|
||||||
arg_str0("u", "uid", "<hex>", "New UID (4 hex bytes)"),
|
arg_str0("u", "uid", "<hex>", "New UID (4 hex bytes)"),
|
||||||
|
arg_lit0(NULL, "furui", "Furui detection card"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
};
|
};
|
||||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
@ -6772,6 +6869,8 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
uint8_t uid[4];
|
uint8_t uid[4];
|
||||||
int uidlen = 0;
|
int uidlen = 0;
|
||||||
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), uid, sizeof(uid), &uidlen);
|
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), uid, sizeof(uid), &uidlen);
|
||||||
|
bool is_furui = arg_get_lit(ctx, 3);
|
||||||
|
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
if (res || (!res && uidlen && uidlen != sizeof(uid))) {
|
if (res || (!res && uidlen && uidlen != sizeof(uid))) {
|
||||||
|
@ -6779,15 +6878,53 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t tracedata[FURUI_MAX_TRACES][18];
|
||||||
|
|
||||||
|
// Super card FURUI
|
||||||
|
if (is_furui) {
|
||||||
|
|
||||||
|
// no reset on super card FURUI
|
||||||
|
if (uidlen || reset_card) {
|
||||||
|
PrintAndLogEx(FAILED, "Not supported on this card");
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read 8 traces
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < FURUI_MAX_TRACES; i++) {
|
||||||
|
|
||||||
|
uint8_t data[] = {0xAA, 0xA8, 0x00, i};
|
||||||
|
uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS;
|
||||||
|
clearCommandBuffer();
|
||||||
|
SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data));
|
||||||
|
if (WaitForResponseTimeout(CMD_ACK, NULL, 1500) == false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketResponseNG resp;
|
||||||
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t len = resp.oldarg[0] & 0xFFFF;
|
||||||
|
if (len != 20) {
|
||||||
|
break; // Not trace data
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(DEBUG, ">>> %s", sprint_hex_inrow(resp.data.asBytes, len));
|
||||||
|
memcpy(&tracedata[i], resp.data.asBytes, len - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mfc_furui_recovery(i, tracedata);
|
||||||
|
}
|
||||||
|
|
||||||
#define SUPER_MAX_TRACES 7
|
#define SUPER_MAX_TRACES 7
|
||||||
|
|
||||||
uint8_t trace = 0;
|
// read 7 traces from super card generation 1,2
|
||||||
uint8_t traces[SUPER_MAX_TRACES][16];
|
uint8_t i = 0;
|
||||||
|
for (i = 0; i < SUPER_MAX_TRACES; i++) {
|
||||||
|
|
||||||
// read 7 traces from super card
|
uint8_t data[] = {0x30, i};
|
||||||
for (trace = 0; trace < SUPER_MAX_TRACES; trace++) {
|
|
||||||
|
|
||||||
uint8_t data[] = {0x30, 0x00 + trace};
|
|
||||||
uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS;
|
uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS;
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data));
|
SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data));
|
||||||
|
@ -6805,11 +6942,12 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
break; // Not trace data
|
break; // Not trace data
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&traces[trace], resp.data.asBytes, len - 2);
|
PrintAndLogEx(DEBUG, ">>> %s", sprint_hex_inrow(resp.data.asBytes, len));
|
||||||
|
memcpy(&tracedata[i], resp.data.asBytes, len - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Super card generation 2
|
// Super card generation 2
|
||||||
if (trace == SUPER_MAX_TRACES) {
|
if (i == SUPER_MAX_TRACES) {
|
||||||
|
|
||||||
// no reset on super card generation 2.
|
// no reset on super card generation 2.
|
||||||
if (uidlen || reset_card) {
|
if (uidlen || reset_card) {
|
||||||
|
@ -6818,40 +6956,8 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// recover key from collected traces
|
// recover key from collected traces
|
||||||
for (trace = 0; trace < SUPER_MAX_TRACES; trace++) {
|
return mfc_supercard_gen2_recovery(i, tracedata);
|
||||||
uint8_t *trace_data = traces[trace];
|
|
||||||
nonces_t data;
|
|
||||||
|
|
||||||
// first
|
|
||||||
uint16_t NT0 = (trace_data[6] << 8) | trace_data[7];
|
|
||||||
data.cuid = bytes_to_num(trace_data, 4);
|
|
||||||
data.nonce = prng_successor(NT0, 31);
|
|
||||||
data.nr = bytes_to_num(trace_data + 8, 4);
|
|
||||||
data.ar = bytes_to_num(trace_data + 12, 4);
|
|
||||||
data.at = 0;
|
|
||||||
|
|
||||||
// second
|
|
||||||
for (uint8_t s_strace = trace + 1; s_strace < 7; s_strace++) {
|
|
||||||
uint8_t *s_trace_data = traces[s_strace];
|
|
||||||
if (mfSectorNum(s_trace_data[5]) == mfSectorNum(trace_data[5])) {
|
|
||||||
NT0 = (s_trace_data[6] << 8) | s_trace_data[7];
|
|
||||||
data.nonce2 = prng_successor(NT0, 31);
|
|
||||||
data.nr2 = bytes_to_num(s_trace_data + 8, 4);
|
|
||||||
data.ar2 = bytes_to_num(s_trace_data + 12, 4);
|
|
||||||
data.sector = mfSectorNum(trace_data[5]);
|
|
||||||
data.keytype = trace_data[4];
|
|
||||||
data.state = FIRST;
|
|
||||||
|
|
||||||
uint64_t key64 = -1;
|
|
||||||
if (mfkey32_moebius(&data, &key64)) {
|
|
||||||
PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]", sprint_hex_inrow(trace_data, 4), data.sector, (data.keytype == 0x60) ? 'A' : 'B', key64);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Super card generation 1
|
// Super card generation 1
|
||||||
|
|
||||||
|
@ -6902,7 +7008,6 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t responseA[22];
|
uint8_t responseA[22];
|
||||||
uint8_t responseB[22];
|
uint8_t responseB[22];
|
||||||
int respAlen = 0;
|
int respAlen = 0;
|
||||||
|
@ -6931,7 +7036,7 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
uint8_t outB[16] = {0};
|
uint8_t outB[16] = {0};
|
||||||
|
|
||||||
uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
||||||
for (uint8_t i = 0; i < 16; i += 8) {
|
for (i = 0; i < 16; i += 8) {
|
||||||
des_decrypt(outA + i, responseA + i, key);
|
des_decrypt(outA + i, responseA + i, key);
|
||||||
des_decrypt(outB + i, responseB + i, key);
|
des_decrypt(outB + i, responseB + i, key);
|
||||||
}
|
}
|
||||||
|
@ -6986,7 +7091,6 @@ static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(FAILED, "failed to recover any key");
|
PrintAndLogEx(FAILED, "failed to recover any key");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue