mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Merge pull request #1946 from AloneLiberty/mf-supercard
Support of Mifare Classic Super Gen2
This commit is contained in:
@@ -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]
|
||||
- Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty)
|
||||
- Added iClass credit key to default iClass key table and reorganized key order (@GuruSteve)
|
||||
- Changed `hf mf value` - ability to use transfer on different block (@AloneLiberty)
|
||||
- Change `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001)
|
||||
|
||||
+26
-17
@@ -497,7 +497,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
|
||||
|
||||
retval = 1;
|
||||
|
||||
OUT:
|
||||
OUT:
|
||||
crypto1_deinit(pcs);
|
||||
|
||||
reply_mix(CMD_ACK, retval, 0, 0, 0, 0);
|
||||
@@ -2597,11 +2597,12 @@ void MifareCIdent(bool is_mfc) {
|
||||
uint8_t isGen = 0;
|
||||
uint8_t rec[1] = {0x00};
|
||||
uint8_t recpar[1] = {0x00};
|
||||
uint8_t rats[4] = { ISO14443A_CMD_RATS, 0x80, 0x31, 0x73 };
|
||||
uint8_t rdblf0[4] = { ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f};
|
||||
uint8_t rdbl00[4] = { ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8};
|
||||
uint8_t gen4gmd[4] = { MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92};
|
||||
uint8_t gen4GetConf[8] = { GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0};
|
||||
uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73};
|
||||
uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f};
|
||||
uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8};
|
||||
uint8_t gen4gmd[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92};
|
||||
uint8_t gen4GetConf[8] = {GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0};
|
||||
uint8_t superGen1[9] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D};
|
||||
uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE);
|
||||
uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE);
|
||||
uint8_t *uid = BigBuf_malloc(10);
|
||||
@@ -2634,8 +2635,7 @@ void MifareCIdent(bool is_mfc) {
|
||||
|
||||
int res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
|
||||
// Check for Magic Gen4 GTU with default password :
|
||||
// Check for Magic Gen4 GTU with default password:
|
||||
// Get config should return 30 or 32 bytes
|
||||
AddCrc14A(gen4GetConf, sizeof(gen4GetConf) - 2);
|
||||
ReaderTransmit(gen4GetConf, sizeof(gen4GetConf), NULL);
|
||||
@@ -2653,7 +2653,6 @@ void MifareCIdent(bool is_mfc) {
|
||||
|
||||
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
|
||||
if (cuid == 0xAA55C396) {
|
||||
isGen = MAGIC_GEN_UNFUSED;
|
||||
goto OUT;
|
||||
@@ -2662,19 +2661,29 @@ void MifareCIdent(bool is_mfc) {
|
||||
ReaderTransmit(rats, sizeof(rats), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res) {
|
||||
// test for super card
|
||||
ReaderTransmit(superGen1, sizeof(superGen1), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 22) {
|
||||
isGen = MAGIC_SUPER_GEN1;
|
||||
|
||||
// test for some MFC gen2
|
||||
if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) {
|
||||
// check for super card gen2
|
||||
// not available after RATS, reset card before executing
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
// super card ident
|
||||
uint8_t super[] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D};
|
||||
ReaderTransmit(super, sizeof(super), NULL);
|
||||
iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 22) {
|
||||
isGen = MAGIC_SUPER;
|
||||
goto OUT;
|
||||
if (res == 18) {
|
||||
isGen = MAGIC_SUPER_GEN2;
|
||||
}
|
||||
|
||||
goto OUT;
|
||||
}
|
||||
// test for some MFC gen2
|
||||
if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
}
|
||||
|
||||
@@ -182,10 +182,10 @@ void MifareDesfireGetInformation(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < sizeof(payload.versionHW)+1) {
|
||||
if (len < sizeof(payload.versionHW) + 1) {
|
||||
Dbprintf("Tag answer to MFDES_GET_VERSION was too short: data in Hardware Information is probably invalid.");
|
||||
print_result("Answer", resp, len);
|
||||
memset(resp+len, 0xFF, sizeof(payload.versionHW)+1 - len); // clear remaining bytes
|
||||
memset(resp + len, 0xFF, sizeof(payload.versionHW) + 1 - len); // clear remaining bytes
|
||||
}
|
||||
|
||||
memcpy(payload.versionHW, resp + 1, sizeof(payload.versionHW));
|
||||
@@ -201,10 +201,10 @@ void MifareDesfireGetInformation(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < sizeof(payload.versionSW)+1) {
|
||||
if (len < sizeof(payload.versionSW) + 1) {
|
||||
Dbprintf("Tag answer to MFDES_ADDITIONAL_FRAME 1 was too short: data in Software Information is probably invalid.");
|
||||
print_result("Answer", resp, len);
|
||||
memset(resp+len, 0xFF, sizeof(payload.versionSW)+1 - len); // clear remaining bytes
|
||||
memset(resp + len, 0xFF, sizeof(payload.versionSW) + 1 - len); // clear remaining bytes
|
||||
}
|
||||
|
||||
memcpy(payload.versionSW, resp + 1, sizeof(payload.versionSW));
|
||||
@@ -219,10 +219,10 @@ void MifareDesfireGetInformation(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < sizeof(payload.details)+1) {
|
||||
if (len < sizeof(payload.details) + 1) {
|
||||
Dbprintf("Tag answer to MFDES_ADDITIONAL_FRAME 2 was too short: data in Batch number and Production date is probably invalid");
|
||||
print_result("Answer", resp, len);
|
||||
memset(resp+len, 0xFF, sizeof(payload.details)+1 - len); // clear remaining bytes
|
||||
memset(resp + len, 0xFF, sizeof(payload.details) + 1 - len); // clear remaining bytes
|
||||
}
|
||||
|
||||
memcpy(payload.details, resp + 1, sizeof(payload.details));
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc
|
||||
|
||||
// Generate (encrypted) nr+parity by loading it into the cipher (Nr)
|
||||
uint32_t pos;
|
||||
uint8_t par[1] = {0x00};
|
||||
uint8_t par[1] = {0x00};
|
||||
uint8_t mf_nr_ar[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
for (pos = 0; pos < 4; pos++) {
|
||||
mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos];
|
||||
|
||||
+12
-12
@@ -36,7 +36,7 @@
|
||||
#include "crypto/asn1utils.h" // ASN1 decode / print
|
||||
#include "cmdflashmemspiffs.h" // SPIFFS flash memory download
|
||||
#include "mbedtls/bignum.h" // big num
|
||||
#include "mbedtls/entropy.h" //
|
||||
#include "mbedtls/entropy.h" //
|
||||
#include "mbedtls/ctr_drbg.h" // random generator
|
||||
|
||||
uint8_t g_DemodBuffer[MAX_DEMOD_BUF_LEN];
|
||||
@@ -2429,7 +2429,7 @@ static int CmdZerocrossings(const char *Cmd) {
|
||||
}
|
||||
|
||||
static bool data_verify_hex(uint8_t *d, size_t n) {
|
||||
if (d == NULL)
|
||||
if (d == NULL)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -2517,7 +2517,7 @@ static int Cmdhex2bin(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (data_verify_hex((uint8_t*)data, dlen) == false) {
|
||||
if (data_verify_hex((uint8_t *)data, dlen) == false) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
@@ -3230,7 +3230,7 @@ static int CmdNumCon(const char *Cmd) {
|
||||
|
||||
// hex
|
||||
if (hlen > 0) {
|
||||
if (data_verify_hex((uint8_t*)hex, hlen) == false) {
|
||||
if (data_verify_hex((uint8_t *)hex, hlen) == false) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 16, hex));
|
||||
@@ -3257,22 +3257,22 @@ static int CmdNumCon(const char *Cmd) {
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base));
|
||||
}
|
||||
|
||||
// printing
|
||||
// printing
|
||||
typedef struct {
|
||||
const char* desc;
|
||||
const char *desc;
|
||||
uint8_t radix;
|
||||
} radix_t;
|
||||
|
||||
radix_t radix[] = {
|
||||
{"dec..... ", 10},
|
||||
{"hex..... 0x", 16},
|
||||
{"bin..... 0b", 2}
|
||||
{"dec..... ", 10},
|
||||
{"hex..... 0x", 16},
|
||||
{"bin..... 0b", 2}
|
||||
};
|
||||
|
||||
char s[600] = {0};
|
||||
size_t slen = 0;
|
||||
|
||||
for (uint8_t i=0; i < ARRAYLEN(radix); i++) {
|
||||
for (uint8_t i = 0; i < ARRAYLEN(radix); i++) {
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen));
|
||||
if (slen > 0) {
|
||||
PrintAndLogEx(INFO, "%s%s", radix[i].desc, s);
|
||||
@@ -3285,9 +3285,9 @@ static int CmdNumCon(const char *Cmd) {
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
mbedtls_entropy_init(&entropy);
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 ));
|
||||
MBEDTLS_MPI_CHK(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0));
|
||||
|
||||
res = mbedtls_mpi_is_prime_ext( &N, 50, mbedtls_ctr_drbg_random, &ctr_drbg );
|
||||
res = mbedtls_mpi_is_prime_ext(&N, 50, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
if (res == 0) {
|
||||
PrintAndLogEx(INFO, "prime... " _YELLOW_("yes"));
|
||||
}
|
||||
|
||||
+200
-101
@@ -411,7 +411,7 @@ static void mf_analyse_acl(uint16_t n, uint8_t *d) {
|
||||
Sector trailer sanity checks.
|
||||
Warn if ACL is strict read-only, or invalid ACL.
|
||||
*/
|
||||
static int mf_analyse_st_block(uint8_t blockno, uint8_t *block, bool force){
|
||||
static int mf_analyse_st_block(uint8_t blockno, uint8_t *block, bool force) {
|
||||
|
||||
if (mfIsSectorTrailer(blockno) == false) {
|
||||
return PM3_SUCCESS;
|
||||
@@ -825,7 +825,7 @@ static int CmdHF14AMfWrBl(const char *Cmd) {
|
||||
PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
|
||||
PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify");
|
||||
} else if (status == PM3_ETEAROFF) {
|
||||
return status;
|
||||
return status;
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )");
|
||||
// suggest the opposite keytype than what was used.
|
||||
@@ -6610,135 +6610,232 @@ static int CmdHf14AGen3Freeze(const char *Cmd) {
|
||||
}
|
||||
|
||||
static int CmdHf14AMfSuperCard(const char *Cmd) {
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf supercard",
|
||||
"Extract info from a `super card`",
|
||||
"hf mf supercard");
|
||||
"hf mf supercard -> recover key\n"
|
||||
"hf mf supercard -r -> reset card\n"
|
||||
"hf mf supercard -u 11223344 -> change UID\n");
|
||||
|
||||
void *argtable[] = {
|
||||
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_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
bool reset_card = arg_get_lit(ctx, 1);
|
||||
uint8_t uid[4];
|
||||
int uidlen = 0;
|
||||
CLIParamHexToBuf(arg_get_str(ctx, 2), uid, sizeof(uid), &uidlen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
bool activate_field = true;
|
||||
bool keep_field_on = true;
|
||||
int res = 0;
|
||||
if (uidlen && uidlen != 4) {
|
||||
PrintAndLogEx(ERR, "UID must include 8 HEX symbols");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (reset_card) {
|
||||
uint32_t trace = 0;
|
||||
uint8_t traces[7][16];
|
||||
for (trace = 0; trace < 7; trace++) {
|
||||
uint8_t data[] = {0x30, 0x00 + trace};
|
||||
uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS;
|
||||
|
||||
keep_field_on = false;
|
||||
uint8_t response[6];
|
||||
int resplen = 0;
|
||||
clearCommandBuffer();
|
||||
SendCommandOLD(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data));
|
||||
|
||||
// --------------- RESET CARD ----------------
|
||||
uint8_t aRESET[] = { 0x00, 0xa6, 0xc0, 0x00 };
|
||||
res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response), &resplen);
|
||||
if (!WaitForResponseTimeout(CMD_ACK, NULL, 1500)) {
|
||||
break; // Select card
|
||||
}
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||
break; // Data not received
|
||||
}
|
||||
|
||||
uint16_t len = resp.oldarg[0] & 0xFFFF;
|
||||
if (len != 18) {
|
||||
break; // Not trace data
|
||||
}
|
||||
|
||||
memcpy(&traces[trace], resp.data.asBytes, len - 2);
|
||||
}
|
||||
|
||||
if (trace == 7) {
|
||||
if (uidlen || reset_card) {
|
||||
PrintAndLogEx(FAILED, "Not supported on this card");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
for (trace = 0; trace < 7; trace++) {
|
||||
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;
|
||||
int res = mfkey32_moebius(&data, &key64);
|
||||
|
||||
if (res) {
|
||||
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 {
|
||||
// Commands:
|
||||
// a0 - set UID
|
||||
// b0 - read traces
|
||||
// c0 - clear card
|
||||
|
||||
bool activate_field = true;
|
||||
bool keep_field_on = true;
|
||||
int res = 0;
|
||||
if (uidlen) {
|
||||
keep_field_on = false;
|
||||
uint8_t response[6];
|
||||
int resplen = 0;
|
||||
|
||||
// --------------- CHANGE UID ----------------
|
||||
uint8_t aCHANGE[] = {0x00, 0xa6, 0xa0, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0x00};
|
||||
memcpy(aCHANGE + 5, uid, uidlen);
|
||||
res = ExchangeAPDU14a(aCHANGE, sizeof(aCHANGE), activate_field, keep_field_on, response, sizeof(response),
|
||||
&resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Super card UID change [ " _RED_("fail") " ]");
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Super card UID change ( " _GREEN_("ok") " )");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (reset_card) {
|
||||
keep_field_on = false;
|
||||
uint8_t response[6];
|
||||
int resplen = 0;
|
||||
|
||||
// --------------- RESET CARD ----------------
|
||||
uint8_t aRESET[] = {0x00, 0xa6, 0xc0, 0x00};
|
||||
res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response),
|
||||
&resplen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]");
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t responseA[22];
|
||||
uint8_t responseB[22];
|
||||
int respAlen = 0;
|
||||
int respBlen = 0;
|
||||
|
||||
// --------------- First ----------------
|
||||
uint8_t aFIRST[] = {0x00, 0xa6, 0xb0, 0x00, 0x10};
|
||||
res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA),
|
||||
&respAlen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]");
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// --------------- Second ----------------
|
||||
activate_field = false;
|
||||
keep_field_on = false;
|
||||
|
||||
uint8_t responseA[22];
|
||||
uint8_t responseB[22];
|
||||
int respAlen = 0;
|
||||
int respBlen = 0;
|
||||
uint8_t aSECOND[] = {0x00, 0xa6, 0xb0, 0x01, 0x10};
|
||||
res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB),
|
||||
&respBlen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
|
||||
// --------------- First ----------------
|
||||
uint8_t aFIRST[] = { 0x00, 0xa6, 0xb0, 0x00, 0x10 };
|
||||
res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
uint8_t outA[16] = {0};
|
||||
uint8_t outB[16] = {0};
|
||||
|
||||
// --------------- Second ----------------
|
||||
activate_field = false;
|
||||
keep_field_on = false;
|
||||
uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
||||
for (uint8_t i = 0; i < 16; i += 8) {
|
||||
des_decrypt(outA + i, responseA + i, key);
|
||||
des_decrypt(outB + i, responseB + i, key);
|
||||
}
|
||||
|
||||
uint8_t aSECOND[] = { 0x00, 0xa6, 0xb0, 0x01, 0x10 };
|
||||
res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen));
|
||||
PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA)));
|
||||
PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen));
|
||||
PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB)));
|
||||
|
||||
// uint8_t inA[] = { 0x72, 0xD7, 0xF4, 0x3E, 0xFD, 0xAB, 0xF2, 0x35, 0xFD, 0x49, 0xEE, 0xDC, 0x44, 0x95, 0x43, 0xC4};
|
||||
// uint8_t inB[] = { 0xF0, 0xA2, 0x67, 0x6A, 0x04, 0x6A, 0x72, 0x12, 0x76, 0xA4, 0x1D, 0x02, 0x1F, 0xEA, 0x20, 0x85};
|
||||
if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) {
|
||||
PrintAndLogEx(INFO, "No trace recorded");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t outA[16] = {0};
|
||||
uint8_t outB[16] = {0};
|
||||
// second trace?
|
||||
if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) {
|
||||
PrintAndLogEx(INFO, "Only one trace recorded");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
||||
for (uint8_t i = 0; i < 16; i += 8) {
|
||||
des_decrypt(outA + i, responseA + i, key);
|
||||
des_decrypt(outB + i, responseB + i, key);
|
||||
}
|
||||
nonces_t data;
|
||||
|
||||
PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen));
|
||||
PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA)));
|
||||
PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen));
|
||||
PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB)));
|
||||
// first
|
||||
uint16_t NT0 = (outA[6] << 8) | outA[7];
|
||||
data.cuid = bytes_to_num(outA, 4);
|
||||
data.nonce = prng_successor(NT0, 31);
|
||||
data.nr = bytes_to_num(outA + 8, 4);
|
||||
data.ar = bytes_to_num(outA + 12, 4);
|
||||
data.at = 0;
|
||||
|
||||
if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) {
|
||||
PrintAndLogEx(INFO, "No trace recorded");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
// second
|
||||
NT0 = (outB[6] << 8) | outB[7];
|
||||
data.nonce2 = prng_successor(NT0, 31);
|
||||
data.nr2 = bytes_to_num(outB + 8, 4);
|
||||
data.ar2 = bytes_to_num(outB + 12, 4);
|
||||
data.sector = mfSectorNum(outA[5]);
|
||||
data.keytype = outA[4];
|
||||
data.state = FIRST;
|
||||
|
||||
// second trace?
|
||||
if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) {
|
||||
PrintAndLogEx(INFO, "Only one trace recorded");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
PrintAndLogEx(DEBUG, "A Sector %02x", data.sector);
|
||||
PrintAndLogEx(DEBUG, "A NT %08x", data.nonce);
|
||||
PrintAndLogEx(DEBUG, "A NR %08x", data.nr);
|
||||
PrintAndLogEx(DEBUG, "A AR %08x", data.ar);
|
||||
PrintAndLogEx(DEBUG, "");
|
||||
PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2);
|
||||
PrintAndLogEx(DEBUG, "B NR %08x", data.nr2);
|
||||
PrintAndLogEx(DEBUG, "B AR %08x", data.ar2);
|
||||
|
||||
nonces_t data;
|
||||
uint64_t key64 = -1;
|
||||
res = mfkey32_moebius(&data, &key64);
|
||||
|
||||
// first
|
||||
uint16_t NT0 = (outA[6] << 8) | outA[7];
|
||||
data.cuid = bytes_to_num(outA, 4);
|
||||
data.nonce = prng_successor(NT0, 31);
|
||||
data.nr = bytes_to_num(outA + 8, 4);
|
||||
data.ar = bytes_to_num(outA + 12, 4);
|
||||
data.at = 0;
|
||||
|
||||
// second
|
||||
NT0 = (outB[6] << 8) | outB[7];
|
||||
data.nonce2 = prng_successor(NT0, 31);;
|
||||
data.nr2 = bytes_to_num(outB + 8, 4);
|
||||
data.ar2 = bytes_to_num(outB + 12, 4);
|
||||
data.sector = mfSectorNum(outA[5]);
|
||||
data.keytype = outA[4];
|
||||
data.state = FIRST;
|
||||
|
||||
PrintAndLogEx(DEBUG, "A Sector %02x", data.sector);
|
||||
PrintAndLogEx(DEBUG, "A NT %08x", data.nonce);
|
||||
PrintAndLogEx(DEBUG, "A NR %08x", data.nr);
|
||||
PrintAndLogEx(DEBUG, "A AR %08x", data.ar);
|
||||
PrintAndLogEx(DEBUG, "");
|
||||
PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2);
|
||||
PrintAndLogEx(DEBUG, "B NR %08x", data.nr2);
|
||||
PrintAndLogEx(DEBUG, "B AR %08x", data.ar2);
|
||||
|
||||
uint64_t key64 = -1;
|
||||
res = mfkey32_moebius(&data, &key64);
|
||||
|
||||
if (res) {
|
||||
PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%12" PRIX64) " ]"
|
||||
, sprint_hex_inrow(outA, 4)
|
||||
, data.sector
|
||||
, (data.keytype == 0x60) ? 'A' : 'B'
|
||||
, key64);
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "failed to recover any key");
|
||||
if (res) {
|
||||
PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%012" PRIX64) " ]", sprint_hex_inrow(outA, 4), data.sector, (data.keytype == 0x60) ? 'A' : 'B', key64);
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "failed to recover any key");
|
||||
}
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
@@ -7786,7 +7883,7 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) {
|
||||
PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify");
|
||||
} else if (resp.status == PM3_ETEAROFF) {
|
||||
return resp.status;
|
||||
} else {
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )");
|
||||
PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype `" _YELLOW_("hf mf gdmsetblk -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a');
|
||||
}
|
||||
@@ -7985,6 +8082,7 @@ static int CmdHF14AMfValue(const char *Cmd) {
|
||||
PrintAndLogEx(FAILED, "Command execute timeout");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
isok = resp.oldarg[0] & 0xff;
|
||||
} else { // set value
|
||||
// To set a value block (or setup) we can use the normal mifare classic write block
|
||||
@@ -8010,6 +8108,7 @@ static int CmdHF14AMfValue(const char *Cmd) {
|
||||
PrintAndLogEx(FAILED, "Command execute timeout");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
isok = resp.oldarg[0] & 0xff;
|
||||
}
|
||||
|
||||
@@ -8113,7 +8212,7 @@ static command_t CommandTable[] = {
|
||||
{"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"},
|
||||
{"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"},
|
||||
{"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"},
|
||||
{"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"},
|
||||
{"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"},
|
||||
{"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"},
|
||||
{"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"},
|
||||
{"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"},
|
||||
|
||||
@@ -271,4 +271,4 @@ static int CmdHelp(const char *Cmd) {
|
||||
int CmdHFTESLA(const char *Cmd) {
|
||||
clearCommandBuffer();
|
||||
return CmdsParse(CommandTable, Cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown, bool full_help)
|
||||
|
||||
char currentparent[MAX_PM3_INPUT_ARGS_LENGTH] = {0};
|
||||
snprintf(currentparent, sizeof currentparent, "%s%s ", parent, cmds[i].Name);
|
||||
|
||||
|
||||
char *old_parent = parent;
|
||||
parent = currentparent;
|
||||
// This is what causes the recursion, since commands Parse-implementation
|
||||
|
||||
@@ -141,7 +141,7 @@ static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag
|
||||
break;
|
||||
|
||||
// month
|
||||
PrintAndLogEx(NORMAL, "%.*s-" NOLF, 2, tlv->value + startidx );
|
||||
PrintAndLogEx(NORMAL, "%.*s-" NOLF, 2, tlv->value + startidx);
|
||||
if (len < startidx + 4)
|
||||
break;
|
||||
|
||||
@@ -233,16 +233,16 @@ static void asn1_tag_dump_hex(const struct tlv *tlv, const struct asn1_tag *tag,
|
||||
}
|
||||
|
||||
static void asn1_tag_dump_octet_string(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
|
||||
/*
|
||||
for (size_t i = 0; i < tlv->len; i++) {
|
||||
if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])) {
|
||||
*needdump = true;
|
||||
break;
|
||||
/*
|
||||
for (size_t i = 0; i < tlv->len; i++) {
|
||||
if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])) {
|
||||
*needdump = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
PrintAndLogEx(NORMAL, " " NOLF);
|
||||
asn1_tag_dump_string(tlv, tag, level);
|
||||
*/
|
||||
PrintAndLogEx(NORMAL, " " NOLF);
|
||||
asn1_tag_dump_string(tlv, tag, level);
|
||||
}
|
||||
|
||||
static void asn1_tag_dump_boolean(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
|
||||
@@ -262,7 +262,7 @@ static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *
|
||||
return;
|
||||
}
|
||||
|
||||
hex_to_buffer((uint8_t*)hex, tlv->value, tlv->len, tlv->len, 0, 0, false);
|
||||
hex_to_buffer((uint8_t *)hex, tlv->value, tlv->len, tlv->len, 0, 0, false);
|
||||
|
||||
// results for MPI actions
|
||||
bool ret = false;
|
||||
@@ -277,7 +277,7 @@ static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *
|
||||
size_t slen = 0;
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 10, s, sizeof(s), &slen));
|
||||
if (slen > 0) {
|
||||
PrintAndLogEx(NORMAL, "%*s value: %s", (level ), "", s);
|
||||
PrintAndLogEx(NORMAL, "%*s value: %s", (level), "", s);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
@@ -227,7 +227,7 @@ struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) {
|
||||
|
||||
while (left != 0) {
|
||||
struct tlvdb *db = calloc(1, sizeof(*db));
|
||||
if (db == NULL ) {
|
||||
if (db == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
@@ -1421,8 +1421,11 @@ int detect_mf_magic(bool is_mfc) {
|
||||
case MAGIC_GEN_UNFUSED:
|
||||
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Write Once / FUID"));
|
||||
break;
|
||||
case MAGIC_SUPER:
|
||||
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("super card"));
|
||||
case MAGIC_SUPER_GEN1:
|
||||
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Super card (") _CYAN_("Gen 1") _GREEN_(")"));
|
||||
break;
|
||||
case MAGIC_SUPER_GEN2:
|
||||
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Super card (") _CYAN_("Gen 2") _GREEN_(")"));
|
||||
break;
|
||||
case MAGIC_NTAG21X:
|
||||
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("NTAG21x"));
|
||||
@@ -1512,16 +1515,16 @@ int vigik_verify(mfc_vigik_t *d) {
|
||||
PrintAndLogEx(INFO, "Raw signature");
|
||||
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
|
||||
}
|
||||
|
||||
/*
|
||||
int dl = 0;
|
||||
|
||||
param_gethex_to_eol("1C07D46DA3849326D24B3468BD76673F4F3C41827DC413E81E4F3C7804FAC727213059B21D047510D6432448643A92EBFC67FBEDDAB468D13D948B172F5EBC79A0E3FEFDFAF4E81FC7108E070F1E3CD0", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
|
||||
|
||||
param_gethex_to_eol("1AB86FE0C17FFFFE4379D5E15A4B2FAFFEFCFA0F1F3F7FA03E7DDDF1E3C78FFFB1F0E23F7FFF51584771C5C18307FEA36CA74E60AA6B0409ACA66A9EC155F4E9112345708A2B8457E722608EE1157408", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
|
||||
signature_len = dl;
|
||||
*/
|
||||
|
||||
/*
|
||||
int dl = 0;
|
||||
|
||||
param_gethex_to_eol("1C07D46DA3849326D24B3468BD76673F4F3C41827DC413E81E4F3C7804FAC727213059B21D047510D6432448643A92EBFC67FBEDDAB468D13D948B172F5EBC79A0E3FEFDFAF4E81FC7108E070F1E3CD0", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
|
||||
|
||||
param_gethex_to_eol("1AB86FE0C17FFFFE4379D5E15A4B2FAFFEFCFA0F1F3F7FA03E7DDDF1E3C78FFFB1F0E23F7FFF51584771C5C18307FEA36CA74E60AA6B0409ACA66A9EC155F4E9112345708A2B8457E722608EE1157408", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
|
||||
signature_len = dl;
|
||||
*/
|
||||
|
||||
uint8_t rev_sig[128];
|
||||
reverse_array_copy(d->rsa_signature, sizeof(d->rsa_signature), rev_sig);
|
||||
|
||||
|
||||
+757
-753
File diff suppressed because it is too large
Load Diff
@@ -126,7 +126,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) {
|
||||
struct addrinfo info;
|
||||
int iResult;
|
||||
|
||||
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
PrintAndLogEx(ERR, "error: WSAStartup failed with error: %d", iResult);
|
||||
free(sp);
|
||||
@@ -231,7 +231,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) {
|
||||
|
||||
void uart_close(const serial_port sp) {
|
||||
serial_port_windows_t *spw = (serial_port_windows_t *)sp;
|
||||
if (spw->hSocket != INVALID_SOCKET){
|
||||
if (spw->hSocket != INVALID_SOCKET) {
|
||||
shutdown(spw->hSocket, SD_BOTH);
|
||||
closesocket(spw->hSocket);
|
||||
WSACleanup();
|
||||
@@ -294,8 +294,7 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin
|
||||
}
|
||||
|
||||
return PM3_ENOTTY;
|
||||
}
|
||||
else { // TCP
|
||||
} else { // TCP
|
||||
uint32_t byteCount; // FIONREAD returns size on 32b
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
@@ -332,12 +331,12 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin
|
||||
|
||||
// Retrieve the count of the incoming bytes
|
||||
res = ioctlsocket(spw->hSocket, FIONREAD, (u_long *)&byteCount);
|
||||
// PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount);
|
||||
// PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount);
|
||||
if (res == SOCKET_ERROR) return PM3_ENOTTY;
|
||||
|
||||
// Cap the number of bytes, so we don't overrun the buffer
|
||||
if (pszMaxRxLen - (*pszRxLen) < byteCount) {
|
||||
// PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount);
|
||||
// PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount);
|
||||
byteCount = pszMaxRxLen - (*pszRxLen);
|
||||
}
|
||||
|
||||
@@ -374,8 +373,7 @@ int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) {
|
||||
return PM3_EIO;
|
||||
}
|
||||
return PM3_ENOTTY;
|
||||
}
|
||||
else { // TCP
|
||||
} else { // TCP
|
||||
uint32_t pos = 0;
|
||||
fd_set wfds;
|
||||
struct timeval tv;
|
||||
|
||||
+2
-2
@@ -280,7 +280,7 @@ void reverse_array(uint8_t *d, size_t n) {
|
||||
d[i] ^= d[j];
|
||||
d[j] ^= d[i];
|
||||
d[i] ^= d[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reverse src array into dest array
|
||||
@@ -292,4 +292,4 @@ void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest) {
|
||||
for (int i = 0; i < src_len; i++) {
|
||||
dest[i] = src[(src_len - 1) - i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+127
-42
File diff suppressed because one or more lines are too long
+7
-2
@@ -126,6 +126,7 @@ Check column "offline" for their availability.
|
||||
|`data hexsamples `|N |`Dump big buffer as hex bytes`
|
||||
|`data hex2bin `|Y |`Converts hexadecimal to binary`
|
||||
|`data load `|Y |`Load contents of file into graph window`
|
||||
|`data num `|Y |`Converts dec/hex/bin`
|
||||
|`data print `|Y |`Print the data in the DemodBuffer`
|
||||
|`data samples `|N |`Get raw samples for graph window (GraphBuffer)`
|
||||
|`data save `|Y |`Save signal trace data (from graph window)`
|
||||
@@ -444,8 +445,9 @@ Check column "offline" for their availability.
|
||||
|`hf legic eload `|N |`Load binary dump to emulator memory`
|
||||
|`hf legic esave `|N |`Save emulator memory to binary file`
|
||||
|`hf legic eview `|N |`View emulator memory`
|
||||
|`hf legic einfo `|N |`Display deobfuscated and decoded emulator memory`
|
||||
|`hf legic crc `|Y |`Calculate Legic CRC over given bytes`
|
||||
|`hf legic view `|Y |`Display content from tag dump file`
|
||||
|`hf legic view `|Y |`Display deobfuscated and decoded content from tag dump file`
|
||||
|
||||
|
||||
### hf lto
|
||||
@@ -521,6 +523,8 @@ Check column "offline" for their availability.
|
||||
|`hf mf gsave `|N |`Save dump from card into file or emulator`
|
||||
|`hf mf gsetblk `|N |`Write block to card`
|
||||
|`hf mf gview `|N |`View card`
|
||||
|`hf mf gdmconfig `|N |`Read config block from card`
|
||||
|`hf mf gdmsetblk `|N |`Write block to card`
|
||||
|`hf mf ndefformat `|N |`Format MIFARE Classic Tag as NFC Tag`
|
||||
|`hf mf ndefread `|N |`Read and print NDEF records from card`
|
||||
|`hf mf ndefwrite `|N |`Write NDEF records to card`
|
||||
@@ -564,6 +568,7 @@ Check column "offline" for their availability.
|
||||
|`hf mfu restore `|N |`Restore a dump onto a MFU MAGIC tag`
|
||||
|`hf mfu view `|Y |`Display content from tag dump file`
|
||||
|`hf mfu wrbl `|N |`Write block`
|
||||
|`hf mfu tamper `|N |`Cofigure the tamper feature on an NTAG 213TT`
|
||||
|`hf mfu eload `|N |`Load Ultralight dump file into emulator memory`
|
||||
|`hf mfu esave `|N |`Save Ultralight dump file from emulator memory`
|
||||
|`hf mfu eview `|N |`View emulator memory`
|
||||
@@ -952,7 +957,7 @@ Check column "offline" for their availability.
|
||||
|`lf hid reader `|N |`attempt to read and extract tag data`
|
||||
|`lf hid clone `|N |`clone HID tag to T55x7`
|
||||
|`lf hid sim `|N |`simulate HID tag`
|
||||
|`lf hid brute `|N |`bruteforce card number against reader`
|
||||
|`lf hid brute `|N |`bruteforce facility code or card number against reader`
|
||||
|`lf hid watch `|N |`continuously watch for cards. Reader mode`
|
||||
|
||||
|
||||
|
||||
@@ -627,24 +627,71 @@ No implemented commands today
|
||||
## MIFARE Classic Super
|
||||
^[Top](#top)
|
||||
|
||||
It behaves like DirectWrite but records reader auth attempts.
|
||||
It behaves like regular Mifare Classic but records reader auth attempts.
|
||||
|
||||
To change UID: same commands as for MFC DirectWrite
|
||||
#### MIFARE Classic Super Gen1
|
||||
^[Top](#top)
|
||||
|
||||
To do reader-only attack: at least two versions exist.
|
||||
Old type of cards, hard to obtain. They are DirectWrite, UID can be changed via 0 block or backdoor commands.
|
||||
|
||||
* type 1: https://github.com/nfc-tools/nfc-supercard for card with ATS: 0978009102DABC1910F005
|
||||
* type 2: https://github.com/netscylla/super-card/blob/master/libnfc-1.7.1/utils/nfc-super.c for ??
|
||||
* UID: 4b version
|
||||
* ATQA/SAK: fixed
|
||||
* BCC: auto
|
||||
* ATS: fixed, 0978009102DABC1910F005
|
||||
|
||||
ATQA/SAK matches 1k card, but works as 4k card.
|
||||
|
||||
Backdoor commands provided over APDU. Format:
|
||||
|
||||
```
|
||||
00 A6 A0 00 05 FF FF FF FF 00
|
||||
^^ ^^ Backdoor command header
|
||||
^^ Backdoor command (A0 - set UID/B0 - get trace/C0 - reset card)
|
||||
^^ Type of answer (used in key recovery to select trace number)
|
||||
^^ Length of user provided data
|
||||
^^ ^^ ^^ ^^ ^^ User data
|
||||
```
|
||||
|
||||
👉 You can't change UID with backdoor command if incorrect data is written to the 0 sector trailer!
|
||||
|
||||
#### MIFARE Classic Super Gen1B
|
||||
|
||||
DirectWrite card, ATS unknown. Probably same as Gen1, except backdoor commands.
|
||||
Implementation: https://github.com/netscylla/super-card/blob/master/libnfc-1.7.1/utils/nfc-super.c
|
||||
|
||||
#### MIFARE Classic Super Gen2
|
||||
^[Top](#top)
|
||||
|
||||
New generation of cards, based on limited Gen4 chip. Emulates Gen1 backdoor protocol, but can store up to 7 different traces.
|
||||
|
||||
Card always answer `ff ff ff ff` to auth, so writing/reading it via Mifare protocol is impossible.
|
||||
|
||||
UID is changeable via Gen4 backdoor write to 0 block.
|
||||
|
||||
* UID: 4b and 7b versions
|
||||
* ATQA/SAK: fixed
|
||||
* BCC: auto
|
||||
* ATS: changeable, default as Gen1
|
||||
|
||||
Gen4 commands available:
|
||||
|
||||
```
|
||||
CF <passwd> 34 <1b length><0-16b ATS> // Configure ATS
|
||||
CF <passwd> CC // Factory test, returns 00 00 00 02 AA
|
||||
CF <passwd> CD <1b block number><16b block data> // Backdoor write 16b block
|
||||
CF <passwd> CE <1b block number> // Backdoor read 16b block
|
||||
CF <passwd> FE <4b new_password> // Change password
|
||||
```
|
||||
|
||||
### Identify
|
||||
^[Top](#top)
|
||||
|
||||
Only type 1 at the moment:
|
||||
Only Gen1/Gen2 at this moment (Gen1B is unsupported):
|
||||
|
||||
```
|
||||
hf 14a info
|
||||
...
|
||||
[+] Magic capabilities : super card
|
||||
[+] Magic capabilities : Super card (Gen ?)
|
||||
```
|
||||
|
||||
# MIFARE Ultralight
|
||||
|
||||
+6
-5
@@ -257,11 +257,12 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
||||
#define MAGIC_GEN_1B 2
|
||||
#define MAGIC_GEN_2 4
|
||||
#define MAGIC_GEN_UNFUSED 5
|
||||
#define MAGIC_SUPER 6
|
||||
#define MAGIC_NTAG21X 7
|
||||
#define MAGIC_GEN_3 8
|
||||
#define MAGIC_GEN_4GTU 9
|
||||
#define MAGIC_GEN_4GDM 10
|
||||
#define MAGIC_SUPER_GEN1 6
|
||||
#define MAGIC_SUPER_GEN2 7
|
||||
#define MAGIC_NTAG21X 8
|
||||
#define MAGIC_GEN_3 9
|
||||
#define MAGIC_GEN_4GTU 10
|
||||
#define MAGIC_GEN_4GDM 11
|
||||
|
||||
// Commands for configuration of Gen4 GTU cards.
|
||||
// see https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md
|
||||
|
||||
Reference in New Issue
Block a user