diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd2f9b3b..ba2423d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] - Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty) + - Added `hf mf gdmgetblk` - Support Gen4 GDM read configuration block (@iceman1001) + - Changed magic note to include a section about GDM tags (@iceman1001) - Added `hf mf gdmsetblk` - Support Gen4 GDM write block (@iceman1001) - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) - Changed CLI max string argument length limit from 512 to 4096 (@iceman1001) @@ -20,7 +22,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Fixed `pm3` script for passing arguments (@doegox) - Fixed python paths to include current directory (@jmichelp) - Fixed infinite loops in spindelayus (@lnv42) - - Add ICECLASS standalone read/sim mode (@natesales) + - Changed ICECLASS standalone to support a read/sim mode (@natesales) - Changed `hf iclass encode` - added verbose flag (@natesales) - Changed `hf waveshare` - now identify 1.54 nfc epaper correct (@ah01) - Fixed `Makefile` regression that broke `make install` (@henrygab) @@ -32,12 +34,13 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `--back` option to `clear` command to clear the scrollback buffer (@wh201906) - Changed `hf iclass decrypt` - mark credentials as decrypted in the dump (@natesales) - Changed `hf iclass view` - show credentials on a decrypted dump (@natesales) - - Show NTAG213TT tamper info in `hf mfu info` and add commands for configuring it's tamper feature (@mjaksn) - - Add Mifare Classic EV1 signature write support to gen4 magic tag lua script (@augustozanellato) + - Changed `hf mfu info` - NTAG213TT tamper info (mjaksn) + - Added commands for configuring NTAG213TT tamper featue (@mjaksn) + - Added Mifare Classic EV1 signature write support to gen4 magic tag lua script (@augustozanellato) - Added XOR key extraction and flag to Guardall G-Prox II (@GuruSteve) - Changed verbiage on `hf iclass info` KeyAccess area to be congruent with AA1 and AA2 areas (@GuruSteve) - Added `hf legic info` command for other sources: `hf legic einfo`, `hf legic view` (@0xdeb) - - + ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index d435f9967..c90640828 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1735,7 +1735,16 @@ static void PacketReceived(PacketCommandNG *packet) { MifareG4WriteBlk(payload->blockno, payload->pwd, payload->data, payload->workFlags); break; } - + case CMD_HF_MIFARE_G4_GDM_RDBL: { + struct p { + uint8_t blockno; + uint8_t keytype; + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareReadBlockGDM(payload->blockno, payload->keytype, payload->key); + break; + } case CMD_HF_MIFARE_G4_GDM_WRBL: { struct p { uint8_t blockno; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index aaf1d795a..0bcb4fd74 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -214,6 +214,65 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { LEDsoff(); } +void MifareReadBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key) { + + int retval = PM3_SUCCESS; + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // variables + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint64_t ui64key = bytes_to_num(key, 6); + uint8_t outbuf[16] = {0x00}; + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_authex_2(pcs, cuid, blockno, keytype, ui64key, AUTH_FIRST, NULL, NULL, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (mifare_classic_readblock_ex(pcs, cuid, blockno, outbuf, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (mifare_classic_halt(pcs, cuid)) { + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_G4_GDM_RDBL, retval, outbuf, sizeof(outbuf)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); +} + //----------------------------------------------------------------------------- // Select, Authenticate, Read a MIFARE tag. // read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index f471ccecc..062c02367 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -59,6 +59,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overw void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes // MFC GEN4 GDM +void MifareReadBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key); void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain); // MFC GEN4 GTU diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index c349860dd..29754538a 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -230,13 +230,20 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc } int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + return mifare_classic_readblock_ex(pcs, uid, blockNo, blockData, false); +} +int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm) { - int len; - uint8_t bt[2] = {0x00, 0x00}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + uint16_t len; + if (is_gdm) { + len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + } else { + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + } + if (len == 1) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error %02x", receivedAnswer[0]); return 1; @@ -246,6 +253,7 @@ int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blo return 2; } + uint8_t bt[2] = {0x00, 0x00}; memcpy(bt, receivedAnswer + 16, 2); AddCrc14A(receivedAnswer, 16); if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { @@ -416,19 +424,14 @@ int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { return mifare_classic_writeblock_ex(pcs, uid, blockNo, blockData, false); } - int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm) { - // variables - uint16_t len = 0; - uint32_t pos = 0; - uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send - uint8_t res = 0; - uint8_t d_block[18], d_block_enc[18]; + // variables uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; // command MIFARE_MAGIC_GDM_WRITEBLOCK + uint16_t len; if (is_gdm) { len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); } else { @@ -440,11 +443,14 @@ int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t return 1; } + uint8_t d_block[18], d_block_enc[18]; memcpy(d_block, blockData, 16); AddCrc14A(d_block, 16); + // enough for 18 Bytes to send + uint8_t par[3] = {0x00, 0x00, 0x00}; // crypto - for (pos = 0; pos < 18; pos++) { + for (uint32_t pos = 0; pos < 18; pos++) { d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); } @@ -452,9 +458,10 @@ int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); // Receive the response + len = ReaderReceive(receivedAnswer, receivedAnswerPar); - res = 0; + uint8_t res = 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index c7c6b4a94..37b1b1294 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -73,7 +73,10 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing); int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm); + int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm); + int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); int mifare_classic_halt_ex(struct Crypto1State *pcs); int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 0abc1eb1f..b820b6add 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -7674,6 +7674,78 @@ static int CmdHF14AGen4Save(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AGen4_GDM_GetBlk(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gdmgetblk", + "Get block data from magic gen4 GDM card.", + "hf mf gdmgetblk --blk 0 --> get block 0 (manufacturer)\n" + "hf mf gdmgetblk --blk 3 -v --> get block 3, decode sector trailer\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("b", "blk", "", "block number"), + arg_lit0("v", "verbose", "verbose output"), + arg_str0("k", "key", "", "key 6 bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int b = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 3, key, &keylen); + CLIParserFree(ctx); + + // validate args + if (b < 0 || b >= MIFARE_4K_MAXBLOCK) { + PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b); + return PM3_EINVARG; + } + + if (keylen != 6 && keylen != 0) { + PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen); + return PM3_EINVARG; + } + + uint8_t blockno = (uint8_t)b; + PrintAndLogEx(NORMAL, "Block: %x", blockno) ; + + struct p { + uint8_t blockno; + uint8_t keytype; + uint8_t key[6]; + } PACKED payload; + + payload.blockno = blockno; + payload.keytype = 0; + memcpy(payload.key, key, sizeof(payload.key)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_RDBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_RDBL, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + uint8_t sector = mfSectorNum(blockno); + mf_print_sector_hdr(sector); + + uint8_t *d = resp.data.asBytes; + mf_print_block_one(blockno, d, verbose); + + if (verbose) { + decode_print_st(blockno, d); + } else { + PrintAndLogEx(NORMAL, ""); + } + } + + return resp.status; +} + static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { CLIParserContext *ctx; @@ -7713,7 +7785,7 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { CLIParserFree(ctx); if (blen != MFBLOCK_SIZE) { - PrintAndLogEx(WARNING, "expected 16 HEX bytes. got %i", blen); + PrintAndLogEx(WARNING, "expected %u HEX bytes. got %i", MFBLOCK_SIZE, blen); return PM3_EINVARG; } @@ -7722,6 +7794,11 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { return PM3_EINVARG; } + if (keylen != 6 && keylen != 0) { + PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen); + return PM3_EINVARG; + } + uint8_t blockno = (uint8_t)b; PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); @@ -8022,6 +8099,7 @@ static command_t CommandTable[] = { {"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, + {"gdmgetblk", CmdHF14AGen4_GDM_GetBlk, IfPm3Iso14443a, "Read block from card"}, {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index 0e487f8a2..1684f145d 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -20,6 +20,7 @@ Useful docs: * [MIFARE Classic DirectWrite, UFUID version](#mifare-classic-directwrite-ufuid-version) * [MIFARE Classic, other versions](#mifare-classic-other-versions) * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) + * [MIFARE Classic Gen4 aka GDM](#mifare-classic-gen4-aka-gdm) * [MIFARE Classic Super](#mifare-classic-super) - [MIFARE Ultralight](#mifare-ultralight) * [MIFARE Ultralight blocks 0..2](#mifare-ultralight-blocks-02) @@ -68,6 +69,7 @@ To restore anticollision config of the Proxmark3: ``` hf 14a config --std ``` + # MIFARE Classic ^[Top](#top) @@ -89,7 +91,7 @@ UID 4b: (actually NUID as there are no more "unique" IDs on 4b) ``` -Computing BCC on UID 11223344: `hf analyse lcr -d 11223344` = `44` +Computing BCC on UID 11223344: `analyse lcr -d 11223344` = `44` UID 7b: @@ -262,6 +264,8 @@ hf 14a info * Read: `40(7)`, `30xx` * Write: `40(7)`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc + + ## MIFARE Classic DirectWrite aka Gen2 aka CUID ^[Top](#top) @@ -396,6 +400,7 @@ hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566184200626364656667 # for 4k hf 14a config --std hf 14a reader ``` + ## MIFARE Classic DirectWrite, FUID version aka 1-write ^[Top](#top) @@ -436,14 +441,6 @@ hf 14a raw -k -c e100 hf 14a raw -c 85000000000000000000000000000008 ``` -## MIFARE Classic, other versions -^[Top](#top) - -**TODO** - -* ZXUID, EUID, ICUID ? -* Some cards exhibit a specific SAK=28 ?? - ## MIFARE Classic Gen3 aka APDU ^[Top](#top) @@ -517,6 +514,72 @@ hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000 hf 14a raw -s -c 90FD111100 ``` +## MIFARE Classic Gen4 aka GDM +^[Top](#top) +Tag has shadow mode enabled from start. +Meaning every write or changes to normal MFC memory is restored back to a copy from persistent memory after about 3 seconds +off rfid field. +Tag also seems to support Gen2 style, direct write, to block 0 to the normal MFC memory. + +The persistent memory is also writable. For that tag uses its own backdoor commands. +for example to write, you must use a customer authentication byte, 0x80, to authenticate with an all zeros key, 0x0000000000. +Then send the data to be written. + +** OBS ** +When writing to persistent memory it is possible to write _bad_ ACL and perm-brick the tag. + + +### Identify +^[Top](#top) + +``` +hf 14a info +... +[+] Magic capabilities : Gen 4 GDM +``` +### Magic commands +^[Top](#top) + +* Auth: `80xx`+crc +* Write: `A8xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc +* Read : `E000`+crc (unidentified) + +### Characteristics +^[Top](#top) + +* Have no knowledge in ATQA/SAK/BCC quirks or if there is a wipe, softbrick recover +* Its magic part seem to be three identified custom command. +* Auth command 0x80, with the key 0x0000000000, Write 0xA8 allows writing to persistent memory, Read 0xE0 which seems to return a configuration. This is unknown today what these bytes are. + +It is unknown what kind of block 0 changes the tag supports +* UID: 4b +* ATQA/SAK: unknown +* BCC: unknown +* ATS: none + +### Proxmark3 commands +^[Top](#top) +``` +# Write to persistent memory +hf mf gdmsetblk + +# Read 0xE0 configuration: +hf mf gdmgetblk + +``` + +### libnfc commands +^[Top](#top) +No implemented commands today + +## MIFARE Classic, other versions +^[Top](#top) + +**TODO** + +* ZXUID, EUID, ICUID ? +* Some cards exhibit a specific SAK=28 ?? + ## MIFARE Classic Super ^[Top](#top) @@ -1019,7 +1082,6 @@ script run hf_15_magic -u E004013344556677 A.k.a ultimate magic card, most promenent feature is shadow mode (GTU) and optional password protected backdoor commands. - Can emulate MIFARE Classic, Ultralight/NTAG families, 14b UID & App Data - [Identify](#identify) @@ -1057,6 +1119,7 @@ The card will be identified only if the password is the default one. One can ide hf 14a raw -s -c -t 1000 CF00000000C6 ``` If the card is an Ultimate Magic Card, it returns 30 or 32 bytes. + ### Magic commands ^[Top](#top) ^^[Gen4](#g4top) @@ -1203,6 +1266,7 @@ OR (Note the script will correct the ATQA correctly) ``` script run hf_mf_ultimatecard -q 004428 ``` + ### Change ATS ^[Top](#top) ^^[Gen4](#g4top) @@ -1240,6 +1304,7 @@ Example: set UID length to 7 bytes, default pwd ``` hf 14a raw -s -c -t 1000 CF000000006801 ``` + ### Set 14443A UID ^[Top](#top) ^^[Gen4](#g4top) @@ -1353,6 +1418,7 @@ script run hf_mf_ultimatecard -m 02 ``` Now the card supports the 3DES UL-C authentication. + ### Set Ultralight and M1 maximum read/write sectors ^[Top](#top) ^^[Gen4](#g4top) @@ -1366,6 +1432,7 @@ Example: set maximum 63 blocks read/write for Mifare Classic 1K ``` hf 14a raw -s -c -t 1000 CF000000006B3F ``` + ### Set shadow mode (GTU) ^[Top](#top) ^^[Gen4](#g4top) @@ -1479,6 +1546,7 @@ Default configuration: ^^ cf cmd 68: UID length ^^ cf cmd 69: Ultralight protocol ``` + ### Fast configuration ^[Top](#top) ^^[Gen4](#g4top) diff --git a/include/protocols.h b/include/protocols.h index 333dcdb71..8450dc58a 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -193,6 +193,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MIFARE_MAGIC_GDM_AUTH_KEYA 0x80 #define MIFARE_MAGIC_GDM_AUTH_KEYB 0x81 #define MIFARE_MAGIC_GDM_WRITEBLOCK 0xA8 +#define MIFARE_MAGIC_GDM_READBLOCK 0xE0 #define MIFARE_EV1_PERSONAL_UID 0x40 #define MIFARE_EV1_SETMODE 0x43