From a523c1e062d4319394a3da898859d508ce3ee060 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:47:45 +0300 Subject: [PATCH 1/9] New crypto Add encryption Add key updating Add config updating Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.c | 360 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 315 insertions(+), 45 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 9c02e8aea..bf8e0c95a 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -389,7 +389,7 @@ static int CmdHFMFPInfo(const char *Cmd) { // DESFire answers 0x1C or 67 00 // Plus answers 0x0B, 0x09, 0x06 - // Which tag answers 6D 00 ?? + // 6D00 is "INS code not supported" in APDU if (data[0] != 0x0b && data[0] != 0x09 && data[0] != 0x1C && data[0] != 0x67 && data[0] != 0x6d) { PrintAndLogEx(INFO, _RED_("Send copy to iceman of this command output!")); PrintAndLogEx(INFO, "data: %s", sprint_hex(data, datalen)); @@ -451,49 +451,59 @@ static int CmdHFMFPWritePerso(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp wrp", "Executes Write Perso command. Can be used in SL0 mode only.", - "hf mfp wrp --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" - "hf mfp wrp --ki 4000 -> write default key(0xff..0xff) to key number 4000"); + "Use this command to program AES keys, as well as personalize other data on the tag.\n" + "You can program:\n" + "* Address 00 [00-FF]: Memory blocks (as well as ACLs and Crypto1 keys)\n" + "* Address 40 [00-40]: AES sector keys\n" + "* Address 90 [00-04]: AES administrative keys\n" + "* Address A0 [00, 01, 80, 81]: Virtual Card keys\n" + "* Address B0 [00-03]: Configuration data (DO NOT TOUCH B003)\n" + "Examples:\n" + "hf mfp wrp --adr 4000 --data 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" + "hf mfp wrp --adr 4000 -> write default key(0xff..0xff) to key number 4000\n" + "hf mfp wrp --adr b000 -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> allow 255 commands without MAC in configuration block (B000)\n" + "hf mfp wrp --adr 0003 -d 1234561234567F078869B0B1B2B3B4B5 -> write crypto1 keys A: 123456123456 and B: B0B1B2B3B4B5 to block 3\n"); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "Verbose output"), - arg_str1(NULL, "ki", "", " Key number, 2 hex bytes"), - arg_str0(NULL, "key", "", " Key, 16 hex bytes"), + arg_str1("a", "adr", "", "Address, 2 hex bytes"), + arg_str0("d", "data", "", "Data, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); - uint8_t keyNum[64] = {0}; - int keyNumLen = 0; - CLIGetHexWithReturn(ctx, 2, keyNum, &keyNumLen); + uint8_t addr[64] = {0}; + int addrLen = 0; + CLIGetHexWithReturn(ctx, 2, addr, &addrLen); - uint8_t key[64] = {0}; - int keyLen = 0; - CLIGetHexWithReturn(ctx, 3, key, &keyLen); + uint8_t datain[64] = {0}; + int datainLen = 0; + CLIGetHexWithReturn(ctx, 3, datain, &datainLen); CLIParserFree(ctx); mfpSetVerboseMode(verbose); - if (!keyLen) { - memmove(key, mfp_default_key, 16); - keyLen = 16; + if (!datainLen) { + memmove(datain, mfp_default_key, 16); + datainLen = 16; } - if (keyNumLen != 2) { - PrintAndLogEx(ERR, "Key number length must be 2 bytes. Got %d", keyNumLen); + if (addrLen != 2) { + PrintAndLogEx(ERR, "Address length must be 2 bytes. Got %d", addrLen); return PM3_EINVARG; } - if (keyLen != 16) { - PrintAndLogEx(ERR, "Key length must be 16 bytes. Got %d", keyLen); + if (datainLen != 16) { + PrintAndLogEx(ERR, "Data length must be 16 bytes. Got %d", datainLen); return PM3_EINVARG; } uint8_t data[250] = {0}; int datalen = 0; - int res = MFPWritePerso(keyNum, key, true, false, data, sizeof(data), &datalen); + int res = MFPWritePerso(addr, datain, true, false, data, sizeof(data), &datalen); if (res) { PrintAndLogEx(ERR, "Exchange error: %d", res); return res; @@ -671,7 +681,29 @@ static int CmdHFMFPAuth(const char *Cmd) { return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false); } - +static int data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev){ +uint8_t kenc[16]; + memcpy(kenc, mf4session->Kenc, 16); + uint8_t ti[4]; + memcpy(ti, mf4session->TI, 4); + uint8_t ctr[1]; + uint8_t IV[16] ={0,0,0x00,0x00,0x00,0,0x00,0x00,0x00,0}; + if (rev){ + ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xff); + for (int i = 0; i<9; i+=4){memcpy(&IV[i], ctr, 1);} + memcpy(&IV[12], ti, 4); // For reads TI is LS + } else { + ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xff); + for (int i = 3; i<16; i+=4){memcpy(&IV[i], ctr, 1);} + memcpy(&IV[0], ti, 4); // For writes TI is MS + } + if (rev){ + aes_decode(IV, kenc, dati, dato, 16); + } else { + aes_encode(IV, kenc, dati, dato, 16); + } + return 0; +} static int CmdHFMFPRdbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp rdbl", @@ -684,7 +716,9 @@ static int CmdHFMFPRdbl(const char *Cmd) { arg_lit0("v", "verbose", "Verbose mode"), arg_int0("n", "count", "", "Blocks count (def: 1)"), arg_lit0("b", "keyb", "Use key B (def: keyA)"), - arg_lit0("p", "plain", "Plain communication mode between reader and card"), + arg_lit0("p", "plain", "Do not use encrypted communication mode between reader and card"), + arg_lit0(NULL, "nmc", "Do not append MAC to command"), + arg_lit0(NULL, "nmr", "Do not expect MAC in reply"), arg_int1(NULL, "blk", "<0..255>", "Block number"), arg_str0("k", "key", "", "Key, 16 hex bytes"), arg_param_end @@ -694,13 +728,15 @@ static int CmdHFMFPRdbl(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); int blocksCount = arg_get_int_def(ctx, 2, 1); bool keyB = arg_get_lit(ctx, 3); - int plain = arg_get_lit(ctx, 4); - uint32_t blockn = arg_get_int(ctx, 5); + bool plain = arg_get_lit(ctx, 4); + bool nomaccmd = arg_get_lit(ctx, 5); + bool nomacres = arg_get_lit(ctx, 6); + uint32_t blockn = arg_get_int(ctx, 7); uint8_t keyn[2] = {0}; uint8_t key[250] = {0}; int keylen = 0; - CLIGetHexWithReturn(ctx, 6, key, &keylen); + CLIGetHexWithReturn(ctx, 8, key, &keylen); CLIParserFree(ctx); mfpSetVerboseMode(verbose); @@ -747,7 +783,7 @@ static int CmdHFMFPRdbl(const char *Cmd) { uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; - res = MFPReadBlock(&mf4session, plain, blockn & 0xff, blocksCount, false, false, data, sizeof(data), &datalen, mac); + res = MFPReadBlock(&mf4session, plain, nomaccmd, nomacres, blockn & 0xff, blocksCount, false, false, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Read error: %d", res); return res; @@ -757,12 +793,13 @@ static int CmdHFMFPRdbl(const char *Cmd) { PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return PM3_ESOFT; } - - if (datalen != 1 + blocksCount * 16 + 8 + 2) { + //PrintAndLogEx(INFO, "%i", 8 && (!macres || 0xff)); + if (datalen != 1 + blocksCount * 16 + (nomacres ? 0 : 8) + 2) { PrintAndLogEx(ERR, "Error return length: %d", datalen); return PM3_ESOFT; } - + + if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); uint8_t sector = mfSectorNum(blockn); mf_print_sector_hdr(sector); @@ -772,11 +809,11 @@ static int CmdHFMFPRdbl(const char *Cmd) { indx++; } - if (memcmp(&data[(blocksCount * 16) + 1], mac, 8)) { + if (memcmp(&data[(blocksCount * 16) + 1], mac, 8) && !nomacres) { PrintAndLogEx(WARNING, "WARNING: mac not equal..."); PrintAndLogEx(WARNING, "MAC card... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + (blocksCount * MFBLOCK_SIZE)], 8)); PrintAndLogEx(WARNING, "MAC reader... " _YELLOW_("%s"), sprint_hex_inrow(mac, sizeof(mac))); - } else { + } else if (!nomacres) { if (verbose) { PrintAndLogEx(INFO, "MAC... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + (blocksCount * MFBLOCK_SIZE)], 8)); } @@ -796,7 +833,9 @@ static int CmdHFMFPRdsc(const char *Cmd) { arg_param_begin, arg_lit0("v", "verbose", "Verbose mode"), arg_lit0("b", "keyb", "Use key B (def: keyA)"), - arg_lit0("p", "plain", "Plain communication mode between reader and card"), + arg_lit0("p", "plain", "Do not use encrypted communication mode between reader and card"), + arg_lit0(NULL, "nmc", "Do not append MAC to command"), + arg_lit0(NULL, "nmr", "Do not expect MAC in reply"), arg_int1("s", "sn", "<0..255>", "Sector number"), arg_str0("k", "key", "", "Key, 16 hex bytes"), arg_param_end @@ -806,11 +845,13 @@ static int CmdHFMFPRdsc(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); bool plain = arg_get_lit(ctx, 3); - uint32_t sectorNum = arg_get_int(ctx, 4); + bool nomaccmd = arg_get_lit(ctx, 4); + bool nomacres = arg_get_lit(ctx, 5); + uint32_t sectorNum = arg_get_int(ctx, 6); uint8_t keyn[2] = {0}; uint8_t key[250] = {0}; int keylen = 0; - CLIGetHexWithReturn(ctx, 5, key, &keylen); + CLIGetHexWithReturn(ctx, 7, key, &keylen); CLIParserFree(ctx); mfpSetVerboseMode(verbose); @@ -851,7 +892,7 @@ static int CmdHFMFPRdsc(const char *Cmd) { for (int blockno = mfFirstBlockOfSector(sectorNum); blockno < mfFirstBlockOfSector(sectorNum) + mfNumBlocksPerSector(sectorNum); blockno++) { - res = MFPReadBlock(&mf4session, plain, blockno & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + res = MFPReadBlock(&mf4session, plain, nomaccmd, nomacres, blockno & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Read error: %d", res); DropField(); @@ -864,19 +905,19 @@ static int CmdHFMFPRdsc(const char *Cmd) { return PM3_ESOFT; } - if (datalen != 1 + MFBLOCK_SIZE + 8 + 2) { + if (datalen != 1 + MFBLOCK_SIZE + (nomacres? 0 : 8) + 2) { PrintAndLogEx(ERR, "Error return length:%d", datalen); DropField(); return PM3_ESOFT; } - + if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); mf_print_block_one(blockno, data + 1, verbose); - if (memcmp(&data[1 + 16], mac, 8)) { + if (memcmp(&data[1 + 16], mac, 8) && !nomacres) { PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", blockno); PrintAndLogEx(WARNING, "MAC card... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + MFBLOCK_SIZE], 8)); PrintAndLogEx(WARNING, "MAC reader... " _YELLOW_("%s"), sprint_hex_inrow(mac, sizeof(mac))); - } else { + } else if (!nomacres) { if (verbose) { PrintAndLogEx(INFO, "MAC... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + MFBLOCK_SIZE], 8)); } @@ -900,6 +941,8 @@ static int CmdHFMFPWrbl(const char *Cmd) { arg_lit0("v", "verbose", "Verbose mode"), arg_lit0("b", "keyb", "Use key B (def: keyA)"), arg_int1(NULL, "blk", "<0..255>", "Block number"), + arg_lit0("p", "plain", "Do not use encrypted transmission"), + arg_lit0(NULL, "nmr", "Do not expect MAC in response"), arg_str1("d", "data", "", "Data, 16 hex bytes"), arg_str0("k", "key", "", "Key, 16 hex bytes"), arg_param_end @@ -909,14 +952,16 @@ static int CmdHFMFPWrbl(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); uint32_t blockNum = arg_get_int(ctx, 3); + bool plain = arg_get_lit(ctx, 4); + bool nomacres = arg_get_lit(ctx, 5); uint8_t datain[250] = {0}; int datainlen = 0; - CLIGetHexWithReturn(ctx, 4, datain, &datainlen); + CLIGetHexWithReturn(ctx, 6, datain, &datainlen); uint8_t key[250] = {0}; int keylen = 0; - CLIGetHexWithReturn(ctx, 5, key, &keylen); + CLIGetHexWithReturn(ctx, 7, key, &keylen); CLIParserFree(ctx); uint8_t keyn[2] = {0}; @@ -956,18 +1001,18 @@ static int CmdHFMFPWrbl(const char *Cmd) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - + if (!plain) data_crypt(&mf4session, &datain[0], &datain[0], false); uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; - res = MFPWriteBlock(&mf4session, blockNum & 0xff, datain, false, false, data, sizeof(data), &datalen, mac); + res = MFPWriteBlock(&mf4session, plain, nomacres, blockNum & 0xff, 0x00, datain, false, false, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Write error: %d", res); DropField(); return res; } - if (datalen != 3 && (datalen != 3 + 8)) { + if (datalen != 3 && (datalen != 3 + (nomacres ? 0 : 8))) { PrintAndLogEx(ERR, "Error return length:%d", datalen); DropField(); return PM3_ESOFT; @@ -979,11 +1024,11 @@ static int CmdHFMFPWrbl(const char *Cmd) { return PM3_ESOFT; } - if (memcmp(&data[1], mac, 8)) { + if (memcmp(&data[1], mac, 8) && !nomacres) { PrintAndLogEx(WARNING, "WARNING: mac not equal..."); PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - } else { + } else if (!nomacres) { if (verbose) PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); } @@ -993,6 +1038,229 @@ static int CmdHFMFPWrbl(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFMFPChKey(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp chkey", + "Change the keys on a Mifare Plus tag", + "This requires the key that can update the key that you are trying to update.\n" + "hf mfp chkey --ki 401f -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Change key B for Sector 15 from MAD to default\n" + "hf mfp chkey --ki 9000 -d 32F9351A1C02B35FF97E0CA943F814F6 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> Change card master key to custom from default" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "Verbose mode"), + arg_lit0(NULL, "nmr", "Do not expect MAC in response"), + arg_str1(NULL, "ki", "", "Key Index, 2 hex bytes"), + arg_str0("k", "key", "", "Current sector key, 16 hex bytes"), + arg_lit0("b", "typeb", "Sector key is key B"), + arg_str1("d", "data", "", "New key, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool verbose = arg_get_lit(ctx, 1); + bool nomacres = arg_get_lit(ctx, 2); + + uint8_t keyn[250] = {0}; + + uint8_t ki[250] = {0}; + int kilen = 0; + CLIGetHexWithReturn(ctx, 3, ki, &kilen); + + uint8_t key[250] = {0}; + int keylen = 0; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + + bool usekeyb = arg_get_lit(ctx, 5); + uint8_t datain[250] = {0}; + int datainlen = 0; + CLIGetHexWithReturn(ctx, 6, datain, &datainlen); + + CLIParserFree(ctx); + + mfpSetVerboseMode(verbose); + + if (!keylen) { + memmove(key, mfp_default_key, 16); + keylen = 16; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); + return PM3_EINVARG; + } + + if (datainlen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); + return PM3_EINVARG; + } + mf4Session_t mf4session; + keyn[0] = ki[0]; + if (ki[0] == 0x40){ // Only if we are working with sector keys + if (usekeyb){ + keyn[1] = (ki[1] % 2 ==0) ? ki[1] + 1 : ki[1]; // If we change using key B, check if KI is key A + } else { + keyn[1] = (ki[1] % 2 ==0) ? ki[1] : ki[1] -1; // If we change using key A, check if KI is key A + } + } else {keyn[1] = ki[1];} + if (verbose){ + PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); + } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + if (res) { + PrintAndLogEx(ERR, "Authentication error: %d", res); + return res; + } + data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPWriteBlock(&mf4session, false, nomacres, ki[1], ki[0], datain, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLogEx(ERR, "Write error: %d", res); + DropField(); + return res; + } + + if (datalen != 3 && (datalen != 3 + (nomacres ? 0 : 8))) { + PrintAndLogEx(ERR, "Error return length:%d", datalen); + DropField(); + return PM3_ESOFT; + } + + if (datalen && data[0] != 0x90) { + PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0])); + DropField(); + return PM3_ESOFT; + } + + if (memcmp(&data[1], mac, 8) && !nomacres) { + PrintAndLogEx(WARNING, "WARNING: mac not equal..."); + PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); + PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); + } else if (!nomacres) { + if (verbose) + PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } + + DropField(); + PrintAndLogEx(INFO, "Key update ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + +static int CmdHFMFPChConf(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp chconf", + "Change the configuration on a Mifare Plus tag. DANGER!", + "This requires Card Master Key (9000) or Card Configuration Key (9001).\n" + "Configuration block info can be found below.\n" + "* Block B000 (00; CMK): Max amount of commands without MAC (byte 0), as well as plain mode access (unknown).\n" + "* Block B001 (01; CCK): Installation identifier for Virtual Card. Please consult NXP for data.\n" + "* Block B002 (02; CCK): ATS data.\n" + "* Block B003 (03; CCK): Use Random ID in SL3, decide whether proximity check is mandatory.\n * DO NOT WRITE THIS BLOCK UNDER ANY CIRCUMSTANCES! Risk of bricking.\n" + "More configuration tips to follow. Check JMY600 Series IC Card Module.\n" + "hf mfp chconf -c 00 -d 10ffffffffffffffffffffffffffffff --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Allow 16 commands without MAC in a single transaction." + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "Verbose mode"), + arg_lit0(NULL, "nmr", "Do not expect MAC in response"), + arg_int1("c", "conf", "", "Config block number, 0-3"), + arg_str0("k", "key", "", "Card key, 16 hex bytes"), + arg_lit0(NULL, "cck", "Auth as Card Configuration key instead of than Card Master Key"), + arg_str1("d", "data", "", "New configuration data, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool verbose = arg_get_lit(ctx, 1); + bool nomacres = arg_get_lit(ctx, 2); + + uint8_t keyn[250] = {0}; + uint32_t blockNum = arg_get_int(ctx, 3); + + uint8_t key[250] = {0}; + int keylen = 0; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + bool usecck = arg_get_lit(ctx, 5); + + uint8_t datain[250] = {0}; + int datainlen = 0; + CLIGetHexWithReturn(ctx, 6, datain, &datainlen); + + CLIParserFree(ctx); + + mfpSetVerboseMode(verbose); + + if (!keylen) { + memmove(key, mfp_default_key, 16); + keylen = 16; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); + return PM3_EINVARG; + } + + if (datainlen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); + return PM3_EINVARG; + } + + if (blockNum > 3) { + PrintAndLogEx(ERR, " must be in range [0..3]. Got %d", blockNum); + return PM3_EINVARG; + } + mf4Session_t mf4session; + keyn[0] = 0x90; + keyn[1] = usecck ? 0x01 : 0x00; + if (verbose){ + PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); + } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + if (res) { + PrintAndLogEx(ERR, "Authentication error: %d", res); + return res; + } + data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPWriteBlock(&mf4session, false, nomacres, blockNum & 0xff, 0xb0, datain, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLogEx(ERR, "Write error: %d", res); + DropField(); + return res; + } + + if (datalen != 3 && (datalen != 3 + (nomacres ? 0 : 8))) { + PrintAndLogEx(ERR, "Error return length:%d", datalen); + DropField(); + return PM3_ESOFT; + } + + if (datalen && data[0] != 0x90) { + PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0])); + DropField(); + return PM3_ESOFT; + } + + if (memcmp(&data[1], mac, 8) && !nomacres) { + PrintAndLogEx(WARNING, "WARNING: mac not equal..."); + PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); + PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); + } else if (!nomacres) { + if (verbose) + PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } + + DropField(); + PrintAndLogEx(INFO, "Write config ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB, uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], bool verbose) { @@ -1821,6 +2089,8 @@ static command_t CommandTable[] = { {"rdbl", CmdHFMFPRdbl, IfPm3Iso14443a, "Read blocks from card"}, {"rdsc", CmdHFMFPRdsc, IfPm3Iso14443a, "Read sectors from card"}, {"wrbl", CmdHFMFPWrbl, IfPm3Iso14443a, "Write block to card"}, + {"chkey", CmdHFMFPChKey, IfPm3Iso14443a, "Change key on card"}, + {"chconf", CmdHFMFPChConf, IfPm3Iso14443a, "Change config on card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------- " _CYAN_("personalization") " -------------------"}, {"commitp", CmdHFMFPCommitPerso, IfPm3Iso14443a, "Configure security layer (SL1/SL3 mode)"}, {"initp", CmdHFMFPInitPerso, IfPm3Iso14443a, "Fill all the card's keys in SL0 mode"}, From 91f574ab1939c7f554c02a5cdfed50d66e3300e6 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:48:45 +0300 Subject: [PATCH 2/9] More arguments Add support for writing special blocks Add custom command bytes Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/mifare/mifare4.c | 38 ++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index 420c93a9e..8186b27d5 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -258,9 +258,9 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti memmove(&raw[16], &RndB[1], 16); aes_encode(NULL, key, raw, &cmd2[1], 32); - if (verbose) + if (verbose){ PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); - + } res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode); if (res) { if (!silentMode) PrintAndLogEx(ERR, "Exchange raw error: %d", res); @@ -372,26 +372,34 @@ int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); } -int MFPReadBlock(mf4Session_t *mf4session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { - uint8_t rcmd[4 + 8] = {(plain ? (0x37) : (0x33)), blockNum, 0x00, blockCount}; - if (!plain && mf4session) - CalculateMAC(mf4session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], g_verbose_mode); - - int res = intExchangeRAW14aPlus(rcmd, plain ? 4 : sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); +int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool nomaccmd, bool nomacres, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + int cmdb = 0x31; + if (nomacres){cmdb = cmdb ^ 0x01;} // If we do not want MAC in reply, remove 0x01 + if (plain){cmdb = cmdb ^ 0x02;} // If we do not need an encrypted transmission, add 0x02 + if (nomaccmd){cmdb = cmdb ^ 0x04;} // If we do not want to send a MAC, remove 0x04 + uint8_t rcmd1[4] = {cmdb, blockNum, 0x00, blockCount}; + uint8_t maccmddat[8] = {0}; + uint8_t rcmd[nomaccmd ? 4 : 12]; + if (!nomaccmd && mf4session) + CalculateMAC(mf4session, mtypReadCmd, blockNum, blockCount, rcmd1, 4, &maccmddat[0], g_verbose_mode); + memmove(rcmd, rcmd1, 4); + if (!nomaccmd){memmove(&rcmd[4], maccmddat, 8);} + int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); if (res) return res; - if (mf4session) mf4session->R_Ctr++; - - if (mf4session && mac && *dataoutlen > 11) + if (mf4session && !nomacres && *dataoutlen > 11) CalculateMAC(mf4session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, g_verbose_mode); return 0; } -int MFPWriteBlock(mf4Session_t *mf4session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { - uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00}; +int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + int cmdb = 0xA1; + if (nomacres){cmdb = cmdb ^ 0x01;} // If we do not want MAC in reply, remove 0x01 + if (plain){cmdb = cmdb ^ 0x02;} // If we do not need an encrypted transmission, add 0x02 + uint8_t rcmd[1 + 2 + 16 + 8] = {cmdb, blockNum, blockHdr}; memmove(&rcmd[3], data, 16); if (mf4session) CalculateMAC(mf4session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], g_verbose_mode); @@ -403,7 +411,7 @@ int MFPWriteBlock(mf4Session_t *mf4session, uint8_t blockNum, uint8_t *data, boo if (mf4session) mf4session->W_Ctr++; - if (mf4session && mac && *dataoutlen > 3) + if (mf4session && mac && *dataoutlen > 3 && !nomacres) CalculateMAC(mf4session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, g_verbose_mode); return 0; @@ -431,7 +439,7 @@ int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data uint8_t mac[8] = {0}; uint8_t firstBlockNo = mfFirstBlockOfSector(sectorNo); for (int n = firstBlockNo; n < firstBlockNo + mfNumBlocksPerSector(sectorNo); n++) { - res = MFPReadBlock(&_session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + res = MFPReadBlock(&_session, plain, false, false, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Sector %u read error: %d", sectorNo, res); DropField(); From ce99e8699737eeaa9f769242c0e4ab11d3ed9b70 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:49:19 +0300 Subject: [PATCH 3/9] New arguments Won't compile otherwise Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/mifare/mifare4.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/mifare/mifare4.h b/client/src/mifare/mifare4.h index 6687930e6..b5449ab44 100644 --- a/client/src/mifare/mifare4.h +++ b/client/src/mifare/mifare4.h @@ -63,8 +63,8 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); -int MFPReadBlock(mf4Session_t *mf4session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); -int MFPWriteBlock(mf4Session_t *mf4session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); +int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool maccmd, bool macres, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); +int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose); int MFPGetSignature(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); From 046237500be4a9a685b15bd01ccdce3d63e2bd4b Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:28:32 +0300 Subject: [PATCH 4/9] Proper block data for Mifare Plus All blocks are NOT data blocks Some are now config/management keys/sector keys/... Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhflist.c | 134 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index bf67a2b0b..aacbb4085 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -1331,8 +1331,57 @@ void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { case MFP_AUTHENTICATEFIRST_VARIANT: if (data_size > 1) { // key : uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); - uint16_t uKeyNum = MemLeToUint2byte(data) ; - snprintf(exp, size, "FIRST AUTH (Keynr 0x%04X: %c sector %d)", uKeyNum, uKeyNum & 0x0001 ? 'B' : 'A', (uKeyNum - 0x4000) / 2); + uint16_t uKeyNum = MemLeToUint2byte(data); + switch (uKeyNum & 0xf000){ + const char* stringdata; + default: + stringdata = "FIRST AUTH (Keynr 0x%04X: Key not identified)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x4000: + stringdata = "FIRST AUTH (Keynr 0x%04X: %c sector %d)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum, uKeyNum & 0x0001 ? 'B' : 'A', (uKeyNum - 0x4000) / 2); + break; + case 0xA000: // There are virtual card encryption and MACing keys, but this is NOT their place! + stringdata = "FIRST AUTH(Keynr 0x%04X: Proximity Check Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x9000: + switch (uKeyNum & 0xf){ + case 0x0: + stringdata = "FIRST AUTH (Keynr 0x%04X: Card Master Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x1: + stringdata = "FIRST AUTH (Keynr 0x%04X: Card Configuration Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x2: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL2 Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x3: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL3 Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x4: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL1 Additional Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x6: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL3 Sector Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + case 0x7: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL1SL3Mix Sector Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + default: + stringdata = "FIRST AUTH(Keynr 0x%04X: Management Key not identified)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); + break; + } + } } else { snprintf(exp, size, "FIRST AUTH") ; } @@ -1341,7 +1390,7 @@ void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { case MFP_WRITEPERSO: if (data_size > 1) { uint16_t uKeyNum = MemLeToUint2byte(data) ; - snprintf(exp, size, "WRITE PERSO (Keynr 0x%04X)", uKeyNum); + snprintf(exp, size, "WRITE PERSO (Addr 0x%04X)", uKeyNum); } else { snprintf(exp, size, "WRITE PERSO"); } @@ -1381,8 +1430,83 @@ void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { const char *responseMaced = mfpGetResponseMacedForCode(opcode) ; if (data_size > 1) { - uint16_t uBlockNum = MemLeToUint2byte(data) ; - snprintf(exp, size, "WRITE %s(%u) %s", encrypted, uBlockNum, responseMaced); + uint16_t uBlockNum = MemLeToUint2byte(data); + switch (uBlockNum & 0xF000){ + const char* stringdata; + default: + stringdata = "WRITE %s(%u) %s"; + snprintf(exp, size, stringdata, encrypted, uBlockNum, responseMaced); + break; + case 0x4000: + stringdata = "WRITE (Keynr 0x%04X: %c sector %d)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum, uBlockNum & 0x0001 ? 'B' : 'A', (uBlockNum - 0x4000) / 2); + break; + case 0xA000: // There are virtual card encryption and MACing keys, but this is NOT their place! + stringdata = "WRITE(Keynr 0x%04X: Proximity Check Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0xB000: + case 0x9000: + if ((uBlockNum & 0x2000) == 0x2000){ + switch (uBlockNum & 0xf){ + default: + stringdata = "WRITE(Config %04X: Unidentified)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x0: + stringdata = "WRITE(Config %04X: Config)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x1: + stringdata = "WRITE(Config %04X: Virtual Card Installation ID)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x2: + stringdata = "WRITE(Config %04X: ATS)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x3: + stringdata = "WRITE(Config %04X: Field configuration)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + } + } else { + switch (uBlockNum & 0xf){ + default: + stringdata = "WRITE(Keynr 0x%04X: Management Key not identified)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x0: + stringdata = "WRITE(Keynr 0x%04X: Card Master Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x1: + stringdata = "WRITE(Keynr 0x%04X: Card Configuration Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x2: + stringdata = "WRITE(Keynr 0x%04X: SL2 Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x3: + stringdata = "WRITE(Keynr 0x%04X: SL3 Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x4: + stringdata = "WRITE(Keynr 0x%04X: SL1 Additional Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x6: + stringdata = "WRITE(Keynr 0x%04X: SL3 Sector Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + case 0x7: + stringdata = "WRITE(Keynr 0x%04X: SL1SL3Mix Sector Switch Key)"; + snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum); + break; + } + } + } } else { snprintf(exp, size, "WRITE %s %s ?", encrypted, responseMaced); } From 192ccf7c3631df0b2e785f8e7066f32ce3a5000c Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:22:47 +0300 Subject: [PATCH 5/9] New mifare plus data 2 new commands Some argument names updated Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- doc/commands.json | 76 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/doc/commands.json b/doc/commands.json index 99785b3cd..89f0ac7fd 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -6247,6 +6247,32 @@ ], "usage": "hf mfp auth [-hv] --ki --key " }, + "hf mfp chconf": { + "command": "hf mfp chconf", + "description": "Change the configuration on a Mifare Plus tag. DANGER!", + "notes": [ + "This requires Card Master Key (9000) or Card Configuration Key (9001).", + "Configuration block info can be found below.", + "* Block B000 (00; CMK): Max amount of commands without MAC (byte 0), as well as plain mode access (unknown).", + "* Block B001 (01; CCK): Installation identifier for Virtual Card. Please consult NXP for data.", + "* Block B002 (02; CCK): ATS data.", + "* Block B003 (03; CCK): Use Random ID in SL3, decide whether proximity check is mandatory.", + " * DO NOT WRITE THIS BLOCK UNDER ANY CIRCUMSTANCES! Risk of bricking.", + "More configuration tips to follow. Check JMY600 Series IC Card Module.", + "hf mfp chconf -c 00 -d 10ffffffffffffffffffffffffffffff --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Allow 16 commands without MAC in a single transaction." + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose Verbose mode", + "--nmr Do not expect MAC in response", + "-c, --conf Config block number, 0-3", + "-k, --key Card key, 16 hex bytes", + "--cck Auth as Card Configuration key instead of Card Master Key", + "-d, --data New configuration data, 16 hex bytes" + ], + "usage": "hf mfp chconf [-hv] [--nmr] -c [-k ] [--cck] -d " + }, "hf mfp chk": { "command": "hf mfp chk", "description": "Checks keys on MIFARE Plus card", @@ -6274,6 +6300,26 @@ ], "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [--dump]" }, + "hf mfp chkey": { + "command": "hf mfp chkey", + "description": "Change the keys on a Mifare Plus tag", + "notes": [ + "This requires the key that can update the key that you are trying to update.", + "hf mfp chkey --ki 401f -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Change key B for Sector 15 from MAD to default", + "hf mfp chkey --ki 9000 -d 32F9351A1C02B35FF97E0CA943F814F6 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> Change card master key to custom from default" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose Verbose mode", + "--nmr Do not expect MAC in response", + "--ki Key Index, 2 hex bytes", + "-k, --key Current sector key, 16 hex bytes", + "-b, --typeb Sector key is key B", + "-d, --data New key, 16 hex bytes" + ], + "usage": "hf mfp chkey [-hvb] [--nmr] --ki [-k ] -d " + }, "hf mfp commitp": { "command": "hf mfp commitp", "description": "Executes Commit Perso command. Can be used in SL0 mode only. OBS! This command will not be executed if CardConfigKey, CardMasterKey and L3SwitchKey AES keys are not written.", @@ -6436,11 +6482,13 @@ "-v, --verbose Verbose mode", "-n, --count Blocks count (def: 1)", "-b, --keyb Use key B (def: keyA)", - "-p, --plain Plain communication mode between reader and card", + "-p, --plain Do not use encrypted transmission between reader and card", + "--nmc Do not append MAC to command", + "--nmr Do not expect MAC in reply", "--blk <0..255> Block number", "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp rdbl [-hvbp] [-n ] --blk <0..255> [-k ]" + "usage": "hf mfp rdbl [-hvbp] [-n ] [--nmc] [--nmr] --blk <0..255> [-k ]" }, "hf mfp rdsc": { "command": "hf mfp rdsc", @@ -6454,11 +6502,13 @@ "-h, --help This help", "-v, --verbose Verbose mode", "-b, --keyb Use key B (def: keyA)", - "-p, --plain Plain communication mode between reader and card", + "-p, --plain Do not use encrypted transmission between reader and card", + "--nmc Do not append MAC to command", + "--nmr Do not expect MAC in reply", "-s, --sn <0..255> Sector number", "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp rdsc [-hvbp] -s <0..255> [-k ]" + "usage": "hf mfp rdsc [-hvbp] [--nmc] [--nmr] -s <0..255> [-k ]" }, "hf mfp wrbl": { "command": "hf mfp wrbl", @@ -6473,26 +6523,30 @@ "-v, --verbose Verbose mode", "-b, --keyb Use key B (def: keyA)", "--blk <0..255> Block number", + "-p, --plain Do not use encrypted transmission", + "--nmr Do not expect MAC in response", "-d, --data Data, 16 hex bytes", "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp wrbl [-hvb] --blk <0..255> -d [-k ]" + "usage": "hf mfp wrbl [-hvbp] --blk <0..255> [--nmr] -d [-k ]" }, "hf mfp wrp": { "command": "hf mfp wrp", "description": "Executes Write Perso command. Can be used in SL0 mode only.", "notes": [ - "hf mfp wrp --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000", - "hf mfp wrp --ki 4000 -> write default key(0xff..0xff) to key number 4000" + "hf mfp wrp --adr 4000 --data 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000", + "hf mfp wrp --adr 4000 -> write default key(0xff..0xff) to key number 4000", + "hf mfp wrp -a b000 -d 20FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> allow 32 commands without MAC in configuration block (B000)", + "hf mfp wrp -a 0003 -d 1234561234567F078869B0B1B2B3B4B5 -> write crypto1 keys A: 123456123456 and B: B0B1B2B3B4B5 to block 3" ], "offline": false, "options": [ "-h, --help This help", "-v, --verbose Verbose output", - "--ki Key number, 2 hex bytes", - "--key Key, 16 hex bytes" + "-a, --adr Address, 2 hex bytes", + "-d, --data Data, 16 hex bytes" ], - "usage": "hf mfp wrp [-hv] --ki [--key ]" + "usage": "hf mfp wrp [-hv] -a [-d ]" }, "hf mfu cauth": { "command": "hf mfu cauth", @@ -12068,4 +12122,4 @@ "extracted_by": "PM3Help2JSON v1.00", "extracted_on": "2023-12-01T14:06:40" } -} \ No newline at end of file +} From 29773e92ca903fbfc30ee08195a3307271d23c1c Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:24:38 +0300 Subject: [PATCH 6/9] Fix typo, correct data 2 new keys (SL3 sector switch; SL1SL3Mix sector switch). "instead of than" - removed the than. Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index bf8e0c95a..5d6c94929 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -37,7 +37,7 @@ #include "cmdtrace.h" static const uint8_t mfp_default_key[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; +static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; #define MFP_KEY_FILE_SIZE 14 + (2 * 64 * (AES_KEY_LEN + 1)) @@ -1169,7 +1169,7 @@ static int CmdHFMFPChConf(const char *Cmd) { arg_lit0(NULL, "nmr", "Do not expect MAC in response"), arg_int1("c", "conf", "", "Config block number, 0-3"), arg_str0("k", "key", "", "Card key, 16 hex bytes"), - arg_lit0(NULL, "cck", "Auth as Card Configuration key instead of than Card Master Key"), + arg_lit0(NULL, "cck", "Auth as Card Configuration key instead of Card Master Key"), arg_str1("d", "data", "", "New configuration data, 16 hex bytes"), arg_param_end }; From 8300ab923202bfbc651f5590a971291f9f6d65a6 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:28:23 +0300 Subject: [PATCH 7/9] 2 new commands We're done for now Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- doc/commands.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/commands.md b/doc/commands.md index ef9dac251..8e6060400 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -561,6 +561,8 @@ Check column "offline" for their availability. |`hf mfp rdbl `|N |`Read blocks from card` |`hf mfp rdsc `|N |`Read sectors from card` |`hf mfp wrbl `|N |`Write block to card` +|`hf mfp chkey `|N |`Change key on card` +|`hf mfp chconf `|N |`Change config on card` |`hf mfp commitp `|N |`Configure security layer (SL1/SL3 mode)` |`hf mfp initp `|N |`Fill all the card's keys in SL0 mode` |`hf mfp wrp `|N |`Write Perso command` From 3523c3df62920da7a18328f2f2e0b91505d7502c Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:38:09 +0300 Subject: [PATCH 8/9] Added plus code Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf4fcc240..e3c4787de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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] + - Added encryption and other SL3 functions for Mifare Plus - more to come (@team-orangeBlue) - Fixed the corrupted data in real-time sampling (@wh201906) - Added a slider in the plot window for navigation (@wh201906) - Fixed client build bug with Python 3.12 (@wh201906) From b82857f8922110be83ea9b6a80d2dd373ff3cc82 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Sun, 3 Dec 2023 12:05:15 +0300 Subject: [PATCH 9/9] Compilation workaround May be reverted if fails Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhflist.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index aacbb4085..ccedc5c66 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -1339,8 +1339,7 @@ void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum); break; case 0x4000: - stringdata = "FIRST AUTH (Keynr 0x%04X: %c sector %d)"; - snprintf(exp, strlen(stringdata)+1, stringdata, uKeyNum, uKeyNum & 0x0001 ? 'B' : 'A', (uKeyNum - 0x4000) / 2); + snprintf(exp, size, "FIRST AUTH (Keynr 0x%04X: %c sector %d)", uKeyNum, uKeyNum & 0x0001 ? 'B' : 'A', (uKeyNum - 0x4000) / 2); break; case 0xA000: // There are virtual card encryption and MACing keys, but this is NOT their place! stringdata = "FIRST AUTH(Keynr 0x%04X: Proximity Check Key)"; @@ -1438,8 +1437,7 @@ void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, stringdata, encrypted, uBlockNum, responseMaced); break; case 0x4000: - stringdata = "WRITE (Keynr 0x%04X: %c sector %d)"; - snprintf(exp, strlen(stringdata)+1, stringdata, uBlockNum, uBlockNum & 0x0001 ? 'B' : 'A', (uBlockNum - 0x4000) / 2); + snprintf(exp, size, "WRITE (Keynr 0x%04X: %c sector %d)", uBlockNum, uBlockNum & 0x0001 ? 'B' : 'A', (uBlockNum - 0x4000) / 2); break; case 0xA000: // There are virtual card encryption and MACing keys, but this is NOT their place! stringdata = "WRITE(Keynr 0x%04X: Proximity Check Key)";