mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Improved magic detection
Magic detection no longer stops when a single type is found as cards may support multiple types of magic, so all detected types will be reported now. GDM/USCUID chips are now detected when GDM magic auth is disabled but magic WUP (40 or 20) is enabled. Gen2/CUID/DirectWrite is now detected when default keys and ACLs are used by attempting to write to block 0 but aborting before actually completing the write.
This commit is contained in:
parent
32b846dbea
commit
8f577ad963
4 changed files with 173 additions and 121 deletions
|
@ -1067,13 +1067,13 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
|
|||
dmax = MAX(dmax, i);
|
||||
} else {
|
||||
// allow some slack for proper timing
|
||||
delta_time = auth2_time - auth1_time + 32;
|
||||
delta_time = auth2_time - auth1_time + 32;
|
||||
}
|
||||
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Nested: calibrating... ntdist=%d", i);
|
||||
} else {
|
||||
unsuccessful_tries++;
|
||||
// card isn't vulnerable to nested attack (random numbers are not predictable)
|
||||
if (unsuccessful_tries > NESTED_MAX_TRIES) {
|
||||
if (unsuccessful_tries > NESTED_MAX_TRIES) {
|
||||
isOK = PM3_EFAILED;
|
||||
}
|
||||
}
|
||||
|
@ -2208,6 +2208,9 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) {
|
|||
static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 };
|
||||
static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 };
|
||||
static uint8_t wipeC[] = { MIFARE_MAGICWIPEC };
|
||||
// GDM alt magic wakeup
|
||||
static uint8_t wupGDM1[] = { MIFARE_MAGIC_GDM_WUPC1 };
|
||||
//static uint8_t wupGDM2[] = { MIFARE_MAGIC_GDM_WUPC2 };
|
||||
|
||||
void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) {
|
||||
|
||||
|
@ -2413,15 +2416,17 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) {
|
|||
|
||||
void MifareCIdent(bool is_mfc) {
|
||||
// variables
|
||||
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 gen4gdm[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92};
|
||||
uint8_t gen4gdmAuth[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92};
|
||||
uint8_t gen4gdmGetConf[4] = {MIFARE_MAGIC_GDM_READ_CFG, 0x00, 0x39, 0xF7};
|
||||
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};
|
||||
bool isGen2 = false;
|
||||
bool isGen1AGdm = false;
|
||||
|
||||
uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE);
|
||||
uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE);
|
||||
|
@ -2432,20 +2437,27 @@ void MifareCIdent(bool is_mfc) {
|
|||
memset(uid, 0x00, 10);
|
||||
|
||||
uint32_t cuid = 0;
|
||||
uint8_t data[1] = {0x00};
|
||||
size_t data_off = 0;
|
||||
uint8_t data[16] = {0x00};
|
||||
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
// Generation 1 test
|
||||
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
|
||||
if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) {
|
||||
uint8_t isGen = MAGIC_GEN_1A;
|
||||
ReaderTransmit(wupC2, sizeof(wupC2), NULL);
|
||||
if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) {
|
||||
isGen = MAGIC_GEN_1B;
|
||||
goto OUT;
|
||||
};
|
||||
isGen = MAGIC_GEN_1A;
|
||||
goto OUT;
|
||||
data[data_off++] = isGen;
|
||||
|
||||
// check for GDM config
|
||||
ReaderTransmit(gen4gdmGetConf, sizeof(gen4gdmGetConf), NULL);
|
||||
int res = ReaderReceive(buf, par);
|
||||
if (res > 1) {
|
||||
isGen1AGdm = true;
|
||||
}
|
||||
}
|
||||
|
||||
// reset card
|
||||
|
@ -2453,6 +2465,11 @@ void MifareCIdent(bool is_mfc) {
|
|||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
// reset card
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
int res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
// Check for Magic Gen4 GTU with default password:
|
||||
|
@ -2461,8 +2478,7 @@ void MifareCIdent(bool is_mfc) {
|
|||
ReaderTransmit(gen4GetConf, sizeof(gen4GetConf), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 32 || res == 34) {
|
||||
isGen = MAGIC_GEN_4GTU;
|
||||
goto OUT;
|
||||
data[data_off++] = MAGIC_GEN_4GTU;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2474,8 +2490,7 @@ 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;
|
||||
data[data_off++] = MAGIC_GEN_UNFUSED;
|
||||
}
|
||||
|
||||
ReaderTransmit(rats, sizeof(rats), NULL);
|
||||
|
@ -2485,7 +2500,7 @@ void MifareCIdent(bool is_mfc) {
|
|||
ReaderTransmit(superGen1, sizeof(superGen1), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 22) {
|
||||
isGen = MAGIC_SUPER_GEN1;
|
||||
uint8_t isGen = MAGIC_SUPER_GEN1;
|
||||
|
||||
// check for super card gen2
|
||||
// not available after RATS, reset card before executing
|
||||
|
@ -2500,42 +2515,37 @@ void MifareCIdent(bool is_mfc) {
|
|||
isGen = MAGIC_SUPER_GEN2;
|
||||
}
|
||||
|
||||
goto OUT;
|
||||
data[data_off++] = isGen;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// test for some MFC 7b gen2
|
||||
if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
}
|
||||
// test for Ultralight magic gen2
|
||||
if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
}
|
||||
// test for Ultralight EV1 magic gen2
|
||||
if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
}
|
||||
// test for some other Ultralight EV1 magic gen2
|
||||
if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
}
|
||||
// test for some other Ultralight magic gen2
|
||||
if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
}
|
||||
// test for NTAG213 magic gen2
|
||||
if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) {
|
||||
isGen = MAGIC_GEN_2;
|
||||
goto OUT;
|
||||
// test for some MFC gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
} else if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) {
|
||||
// test for some MFC 7b gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
} else if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) {
|
||||
// test for Ultralight magic gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
} else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) {
|
||||
// test for Ultralight EV1 magic gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
} else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) {
|
||||
// test for some other Ultralight EV1 magic gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
} else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) {
|
||||
// test for some other Ultralight magic gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
} else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) {
|
||||
// test for NTAG213 magic gen2
|
||||
isGen2 = true;
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2549,10 +2559,35 @@ void MifareCIdent(bool is_mfc) {
|
|||
ReaderTransmit(rdblf0, sizeof(rdblf0), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 18) {
|
||||
isGen = MAGIC_NTAG21X;
|
||||
data[data_off++] = MAGIC_NTAG21X;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isGen2) {
|
||||
// CUID (with default sector 0 B key) test
|
||||
// regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it
|
||||
// if we do get an ACK, we immediately abort to ensure nothing is ever actually written
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
if (mifare_classic_authex(pcs, cuid, 0, MF_KEY_B, 0xFFFFFFFFFFFF, AUTH_FIRST, NULL, NULL) == 0) {
|
||||
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
|
||||
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
|
||||
if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, receivedAnswer, receivedAnswerPar, NULL) == 1) && (receivedAnswer[0] == 0x0A)) {
|
||||
data[data_off++] = MAGIC_GEN_2;
|
||||
// turn off immediately to ensure nothing ever accidentally writes to the block
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
}
|
||||
}
|
||||
crypto1_deinit(pcs);
|
||||
}
|
||||
}
|
||||
|
||||
// magic MFC Gen3 test 1
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
|
@ -2562,48 +2597,51 @@ void MifareCIdent(bool is_mfc) {
|
|||
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 18) {
|
||||
isGen = MAGIC_GEN_3;
|
||||
data[data_off++] = MAGIC_GEN_3;
|
||||
}
|
||||
}
|
||||
|
||||
// magic MFC Gen4 GDM test
|
||||
if (isGen != MAGIC_GEN_3) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
ReaderTransmit(gen4gdm, sizeof(gen4gdm), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 4) {
|
||||
isGen = MAGIC_GEN_4GDM;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGen != MAGIC_GEN_4GDM) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) {
|
||||
isGen = MAGIC_QL88;
|
||||
}
|
||||
crypto1_deinit(pcs);
|
||||
}
|
||||
// magic MFC Gen4 GDM magic auth test
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL);
|
||||
res = ReaderReceive(buf, par);
|
||||
if (res == 4) {
|
||||
data[data_off++] = MAGIC_GDM_AUTH;
|
||||
}
|
||||
}
|
||||
|
||||
// QL88 test
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
SpinDelay(40);
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
||||
if (res == 2) {
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) {
|
||||
data[data_off++] = MAGIC_QL88;
|
||||
}
|
||||
crypto1_deinit(pcs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OUT:
|
||||
if (isGen1AGdm == true) {
|
||||
data[data_off++] = MAGIC_GDM_WUP_40;
|
||||
}
|
||||
|
||||
data[0] = isGen;
|
||||
reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, sizeof(data));
|
||||
// GEM alt magic wakeup (20)
|
||||
ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL);
|
||||
if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) {
|
||||
data[data_off++] = MAGIC_GDM_WUP_20;
|
||||
}
|
||||
|
||||
reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, data_off);
|
||||
// turns off
|
||||
OnSuccessMagic();
|
||||
BigBuf_free();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue