Merge pull request #2223 from nvx/feature/gdm_magic_wakeup_detection

Improved magic detection
This commit is contained in:
Iceman 2023-12-30 11:07:54 +01:00 committed by GitHub
commit 27b92591aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 173 additions and 121 deletions

View file

@ -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... 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] ## [unreleased][unreleased]
- Changed `hf 14a info` magic detection to better detect different GDM/USCUID configurations and improved Gen2/CUID detection when default keys are used (@nvx)
- Changed `hf_cardhopper` standalone mode to allow running over the internal Proxmark3 USB-CDC serial port (@nvx) - Changed `hf_cardhopper` standalone mode to allow running over the internal Proxmark3 USB-CDC serial port (@nvx)
- Fixed CLI prompt - Update connection type prompt after running `hw connect` (@wh201906) - Fixed CLI prompt - Update connection type prompt after running `hw connect` (@wh201906)
- Changed `uart_receive()` - Check if TCP connection is lost (@wh201906) - Changed `uart_receive()` - Check if TCP connection is lost (@wh201906)

View file

@ -2208,6 +2208,9 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) {
static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 }; static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 };
static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 }; static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 };
static uint8_t wipeC[] = { MIFARE_MAGICWIPEC }; 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) { 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) { void MifareCIdent(bool is_mfc) {
// variables // variables
uint8_t isGen = 0;
uint8_t rec[1] = {0x00}; uint8_t rec[1] = {0x00};
uint8_t recpar[1] = {0x00}; uint8_t recpar[1] = {0x00};
uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73}; uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73};
uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f};
uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; 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 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 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 *par = BigBuf_malloc(MAX_PARITY_SIZE);
uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE);
@ -2432,21 +2437,33 @@ void MifareCIdent(bool is_mfc) {
memset(uid, 0x00, 10); memset(uid, 0x00, 10);
uint32_t cuid = 0; 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); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
// Generation 1 test // Generation 1 test
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) { if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) {
uint8_t isGen = MAGIC_GEN_1A;
ReaderTransmit(wupC2, sizeof(wupC2), NULL); ReaderTransmit(wupC2, sizeof(wupC2), NULL);
if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) {
isGen = MAGIC_GEN_1B; isGen = MAGIC_GEN_1B;
goto OUT;
}; };
isGen = MAGIC_GEN_1A; data[data_off++] = isGen;
goto OUT;
// check for GDM config
ReaderTransmit(gen4gdmGetConf, sizeof(gen4gdmGetConf), NULL);
int res = ReaderReceive(buf, par);
if (res > 1) {
isGen1AGdm = true;
} }
}
// reset card
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
SpinDelay(40);
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
// reset card // reset card
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
@ -2461,8 +2478,7 @@ void MifareCIdent(bool is_mfc) {
ReaderTransmit(gen4GetConf, sizeof(gen4GetConf), NULL); ReaderTransmit(gen4GetConf, sizeof(gen4GetConf), NULL);
res = ReaderReceive(buf, par); res = ReaderReceive(buf, par);
if (res == 32 || res == 34) { if (res == 32 || res == 34) {
isGen = MAGIC_GEN_4GTU; data[data_off++] = MAGIC_GEN_4GTU;
goto OUT;
} }
} }
@ -2474,8 +2490,7 @@ void MifareCIdent(bool is_mfc) {
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res == 2) { if (res == 2) {
if (cuid == 0xAA55C396) { if (cuid == 0xAA55C396) {
isGen = MAGIC_GEN_UNFUSED; data[data_off++] = MAGIC_GEN_UNFUSED;
goto OUT;
} }
ReaderTransmit(rats, sizeof(rats), NULL); ReaderTransmit(rats, sizeof(rats), NULL);
@ -2485,7 +2500,7 @@ void MifareCIdent(bool is_mfc) {
ReaderTransmit(superGen1, sizeof(superGen1), NULL); ReaderTransmit(superGen1, sizeof(superGen1), NULL);
res = ReaderReceive(buf, par); res = ReaderReceive(buf, par);
if (res == 22) { if (res == 22) {
isGen = MAGIC_SUPER_GEN1; uint8_t isGen = MAGIC_SUPER_GEN1;
// check for super card gen2 // check for super card gen2
// not available after RATS, reset card before executing // not available after RATS, reset card before executing
@ -2500,42 +2515,37 @@ void MifareCIdent(bool is_mfc) {
isGen = MAGIC_SUPER_GEN2; 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) { if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) {
isGen = MAGIC_GEN_2; // test for some MFC gen2
goto OUT; 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 // 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) { isGen2 = true;
isGen = MAGIC_GEN_2; data[data_off++] = MAGIC_GEN_2;
goto OUT; } else if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) {
}
// test for Ultralight magic gen2 // test for Ultralight magic gen2
if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { isGen2 = true;
isGen = MAGIC_GEN_2; data[data_off++] = MAGIC_GEN_2;
goto OUT; } 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 // 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) { isGen2 = true;
isGen = MAGIC_GEN_2; data[data_off++] = MAGIC_GEN_2;
goto OUT; } 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 // 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) { isGen2 = true;
isGen = MAGIC_GEN_2; data[data_off++] = MAGIC_GEN_2;
goto OUT; } 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 // 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) { isGen2 = true;
isGen = MAGIC_GEN_2; data[data_off++] = MAGIC_GEN_2;
goto OUT; } 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 // 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) { isGen2 = true;
isGen = MAGIC_GEN_2; data[data_off++] = MAGIC_GEN_2;
goto OUT;
} }
} }
@ -2549,10 +2559,35 @@ void MifareCIdent(bool is_mfc) {
ReaderTransmit(rdblf0, sizeof(rdblf0), NULL); ReaderTransmit(rdblf0, sizeof(rdblf0), NULL);
res = ReaderReceive(buf, par); res = ReaderReceive(buf, par);
if (res == 18) { if (res == 18) {
isGen = MAGIC_NTAG21X; data[data_off++] = MAGIC_NTAG21X;
} }
} }
} else { } 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 // magic MFC Gen3 test 1
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
SpinDelay(40); SpinDelay(40);
@ -2562,25 +2597,24 @@ void MifareCIdent(bool is_mfc) {
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
res = ReaderReceive(buf, par); res = ReaderReceive(buf, par);
if (res == 18) { if (res == 18) {
isGen = MAGIC_GEN_3; data[data_off++] = MAGIC_GEN_3;
} }
} }
// magic MFC Gen4 GDM test // magic MFC Gen4 GDM magic auth test
if (isGen != MAGIC_GEN_3) {
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
SpinDelay(40); SpinDelay(40);
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res == 2) { if (res == 2) {
ReaderTransmit(gen4gdm, sizeof(gen4gdm), NULL); ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL);
res = ReaderReceive(buf, par); res = ReaderReceive(buf, par);
if (res == 4) { if (res == 4) {
isGen = MAGIC_GEN_4GDM; data[data_off++] = MAGIC_GDM_AUTH;
} }
} }
if (isGen != MAGIC_GEN_4GDM) { // QL88 test
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
SpinDelay(40); SpinDelay(40);
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
@ -2590,20 +2624,24 @@ void MifareCIdent(bool is_mfc) {
struct Crypto1State *pcs; struct Crypto1State *pcs;
pcs = &mpcs; pcs = &mpcs;
if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) { if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) {
isGen = MAGIC_QL88; data[data_off++] = MAGIC_QL88;
} }
crypto1_deinit(pcs); crypto1_deinit(pcs);
} }
} }
}
}
}; };
OUT: if (isGen1AGdm == true) {
data[data_off++] = MAGIC_GDM_WUP_40;
}
data[0] = isGen; // GEM alt magic wakeup (20)
reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, sizeof(data)); 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 // turns off
OnSuccessMagic(); OnSuccessMagic();
BigBuf_free(); BigBuf_free();

View file

@ -612,7 +612,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl
uint32_t uid; uint32_t uid;
StateList_t statelists[2]; StateList_t statelists[2];
struct Crypto1State *p1, * p2, * p3, * p4; struct Crypto1State *p1, *p2, *p3, *p4;
struct { struct {
uint8_t block; uint8_t block;
@ -1392,18 +1392,20 @@ int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, ui
/* try to see if card responses to "Chinese magic backdoor" commands. */ /* try to see if card responses to "Chinese magic backdoor" commands. */
int detect_mf_magic(bool is_mfc) { int detect_mf_magic(bool is_mfc) {
uint8_t isGeneration = 0; uint8_t isMagic = 0;
PacketResponseNG resp; PacketResponseNG resp;
clearCommandBuffer(); clearCommandBuffer();
uint8_t payload[] = { is_mfc }; uint8_t payload[] = { is_mfc };
SendCommandNG(CMD_HF_MIFARE_CIDENT, payload, sizeof(payload)); SendCommandNG(CMD_HF_MIFARE_CIDENT, payload, sizeof(payload));
if (WaitForResponseTimeout(CMD_HF_MIFARE_CIDENT, &resp, 1500)) { if (WaitForResponseTimeout(CMD_HF_MIFARE_CIDENT, &resp, 1500)) {
if (resp.status == PM3_SUCCESS) { if (resp.status != PM3_SUCCESS) {
isGeneration = resp.data.asBytes[0]; return 0;
} }
} }
switch (isGeneration) { for (size_t i = 0; i < resp.length; i++) {
isMagic = 1;
switch (resp.data.asBytes[i]) {
case MAGIC_GEN_1A: case MAGIC_GEN_1A:
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 1a")); PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 1a"));
break; break;
@ -1419,8 +1421,14 @@ int detect_mf_magic(bool is_mfc) {
case MAGIC_GEN_4GTU: case MAGIC_GEN_4GTU:
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GTU")); PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GTU"));
break; break;
case MAGIC_GEN_4GDM: case MAGIC_GDM_AUTH:
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM")); PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM / USCUID (Magic Auth)"));
break;
case MAGIC_GDM_WUP_20:
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM / USCUID (Alt Magic Wakeup)"));
break;
case MAGIC_GDM_WUP_40:
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM / USCUID (Gen1 Magic Wakeup)"));
break; break;
case MAGIC_GEN_UNFUSED: case MAGIC_GEN_UNFUSED:
PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Write Once / FUID")); PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Write Once / FUID"));
@ -1439,7 +1447,8 @@ int detect_mf_magic(bool is_mfc) {
default: default:
break; break;
} }
return isGeneration; }
return isMagic;
} }
bool detect_mfc_ev1_signature(void) { bool detect_mfc_ev1_signature(void) {

View file

@ -191,6 +191,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MIFARE_CMD_RESTORE 0xC2 #define MIFARE_CMD_RESTORE 0xC2
#define MIFARE_CMD_TRANSFER 0xB0 #define MIFARE_CMD_TRANSFER 0xB0
#define MIFARE_MAGIC_GDM_WUPC1 0x20
#define MIFARE_MAGIC_GDM_WUPC2 0x23
#define MIFARE_MAGIC_GDM_AUTH_KEY 0x80 #define MIFARE_MAGIC_GDM_AUTH_KEY 0x80
#define MIFARE_MAGIC_GDM_READBLOCK 0x38 #define MIFARE_MAGIC_GDM_READBLOCK 0x38
#define MIFARE_MAGIC_GDM_WRITEBLOCK 0xA8 #define MIFARE_MAGIC_GDM_WRITEBLOCK 0xA8
@ -264,8 +266,10 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MAGIC_NTAG21X 8 #define MAGIC_NTAG21X 8
#define MAGIC_GEN_3 9 #define MAGIC_GEN_3 9
#define MAGIC_GEN_4GTU 10 #define MAGIC_GEN_4GTU 10
#define MAGIC_GEN_4GDM 11 #define MAGIC_GDM_AUTH 11
#define MAGIC_QL88 12 #define MAGIC_QL88 12
#define MAGIC_GDM_WUP_20 13
#define MAGIC_GDM_WUP_40 14
// Commands for configuration of Gen4 GTU cards. // Commands for configuration of Gen4 GTU cards.