proxmark3/armsrc/Standalone/hf_mattyrun.c
2020-02-28 10:01:52 +01:00

466 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//-----------------------------------------------------------------------------
// 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 "standalone.h" // standalone definitions
#include "proxmark3_arm.h"
#include "appmain.h"
#include "fpgaloader.h"
#include "util.h"
#include "dbprint.h"
#include "ticks.h"
#include "string.h"
#include "commonutil.h"
#include "iso14443a.h"
#include "mifarecmd.h"
#include "crc16.h"
#include "BigBuf.h"
#include "mifaresim.h" // mifare1ksim
#include "mifareutil.h"
uint8_t uid[10];
uint32_t cuid;
iso14a_card_select_t p_card;
//-----------------------------------------------------------------------------
// Matt's StandAlone mod.
// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn)
//-----------------------------------------------------------------------------
static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) {
// params
uint8_t needWipe = arg0;
// bit 0 - need get UID
// bit 1 - need wupC
// bit 2 - need HALT after sequence
// bit 3 - need init FPGA and field before sequence
// bit 4 - need reset FPGA and LED
uint8_t workFlags = arg1;
uint8_t blockNo = arg2;
// card commands
uint8_t wupC1[] = {0x40};
uint8_t wupC2[] = {0x43};
uint8_t wipeC[] = {0x41};
// variables
uint8_t isOK = 0;
uint8_t d_block[18] = {0x00};
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE];
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE];
// reset FPGA and LED
if (workFlags & 0x08) {
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
set_tracing(false);
}
while (true) {
// get UID from chip
if (workFlags & 0x01) {
if (!iso14443a_select_card(uid, &p_card, &cuid, true, 0, true)) {
DbprintfEx(FLAG_NEWLINE, "Can't select card");
break;
};
if (mifare_classic_halt(NULL, cuid)) {
DbprintfEx(FLAG_NEWLINE, "Halt error");
break;
};
};
// reset chip
if (needWipe) {
ReaderTransmitBitsPar(wupC1, 7, 0, NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
DbprintfEx(FLAG_NEWLINE, "wupC1 error");
break;
};
ReaderTransmit(wipeC, sizeof(wipeC), NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
DbprintfEx(FLAG_NEWLINE, "wipeC error");
break;
};
if (mifare_classic_halt(NULL, cuid)) {
DbprintfEx(FLAG_NEWLINE, "Halt error");
break;
};
};
// chaud
// write block
if (workFlags & 0x02) {
ReaderTransmitBitsPar(wupC1, 7, 0, NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
DbprintfEx(FLAG_NEWLINE, "wupC1 error");
break;
};
ReaderTransmit(wupC2, sizeof(wupC2), NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
DbprintfEx(FLAG_NEWLINE, "wupC2 errorv");
break;
};
}
if ((mifare_sendcmd_short(NULL, CRYPT_NONE, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) {
DbprintfEx(FLAG_NEWLINE, "write block send command error");
break;
};
memcpy(d_block, datain, 16);
AddCrc14A(d_block, 16);
ReaderTransmit(d_block, sizeof(d_block), NULL);
if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) {
DbprintfEx(FLAG_NEWLINE, "write block send data error");
break;
};
if (workFlags & 0x04) {
if (mifare_classic_halt(NULL, cuid)) {
DbprintfEx(FLAG_NEWLINE, "Halt error");
break;
};
}
isOK = 1;
break;
}
if ((workFlags & 0x10) || (!isOK)) {
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
}
return isOK;
}
/* the chk function is a piwied(tm) check that will try all keys for
a particular sector. also no tracing no dbg */
static int saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, uint8_t keyCount, uint8_t *datain, uint64_t *key) {
DBGLEVEL = DBG_NONE;
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
set_tracing(false);
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
for (int i = 0; i < keyCount; ++i) {
/* no need for anticollision. just verify tag is still here */
// if (!iso14443a_fast_select_card(cjuid, 0)) {
if (!iso14443a_select_card(uid, &p_card, &cuid, true, 0, true)) {
DbprintfEx(FLAG_NEWLINE, "FATAL : E_MF_LOSTTAG");
return -1;
}
uint64_t ui64Key = bytes_to_num(datain + i * 6, 6);
if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
uint8_t dummy_answer = 0;
ReaderTransmit(&dummy_answer, 1, NULL);
// wait for the card to become ready again
SpinDelayUs(AUTHENTICATION_TIMEOUT);
continue;
}
crypto1_deinit(pcs);
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
*key = ui64Key;
return i;
}
crypto1_deinit(pcs);
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
return -1;
}
void ModInfo(void) {
DbpString(" HF Mifare sniff/clone - aka MattyRun (Matías A. Ré Medina)");
}
void RunMod() {
StandAloneMode();
Dbprintf(">> Matty mifare chk/dump/sim a.k.a MattyRun Started <<");
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
/*
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().
*/
// Comment this line below if you want to see debug messages.
// usb_disable();
/*
Pseudo-configuration block.
*/
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 = 2; // Keytype buffer
uint64_t key64; // Defines current key
uint8_t *keyBlock; // Where the keys will be held in memory.
uint8_t stKeyBlock = 20; // Set the quantity of keys in the block.
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 = ARRAYLEN(mfKeys);
for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) {
num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t *)(keyBlock + mfKeyCounter * 6));
}
/*
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.
*/
bool err = 0;
bool allKeysFound = true;
uint32_t size = mfKeysCnt;
for (int type = !keyType; type < 2 && !err; keyType == 2 ? (type++) : (type = 2)) {
int 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);
int key = saMifareChkKeys(block, type, true, size, &keyBlock[0], &key64);
if (key == -1) {
LED(LED_RED, 50);
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]
);
}
block < 127 ? (block += 4) : (block += 16);
}
}
/*
TODO:
- Get UID from tag and set accordingly in emulator memory and call mifaresim with right flags (iceman)
*/
if (allKeysFound) {
Dbprintf("\t✓ All keys found");
} else {
if (keyFound) {
Dbprintf("\t✕ There's currently no nested attack in MattyRun, sorry!");
LED_C_ON(); //red
LED_A_ON(); //yellow
// no room to run nested attack on device (iceman)
// 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) {
int filled;
Dbprintf("\tFilling in with key A.");
filled = MifareECardLoad(sectorsCnt, 0);
if (filled != PM3_SUCCESS) {
Dbprintf("\t✕ Failed filling with A.");
}
Dbprintf("\tFilling in with key B.");
filled = MifareECardLoad(sectorsCnt, 1);
if (filled != PM3_SUCCESS) {
Dbprintf("\t✕ Failed filling with B.");
}
if ((filled == PM3_SUCCESS) && simulation) {
Dbprintf("\t✓ Emulator memory 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
// assuming arg0==0, use hardcoded uid 0xdeadbeaf
uint16_t simflags;
switch (p_card.uidlen) {
case 10:
simflags = FLAG_10B_UID_IN_DATA;
break;
case 7:
simflags = FLAG_7B_UID_IN_DATA;
break;
default:
simflags = FLAG_4B_UID_IN_DATA;
break;
}
Mifare1ksim(simflags | FLAG_MF_1K, 0, uid, 0, 0);
LED_B_OFF();
/*
Needs further testing.
*/
if (fillFromEmulator) {
uint8_t retry = 5;
Dbprintf("\t Trying to dump into blank card.");
int flags = 0;
LED_A_ON(); //yellow
for (int blockNum = 0; blockNum < 16 * 4; blockNum += 1) {
uint8_t 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 != PM3_SUCCESS) {
Dbprintf("\t✕ Emulator memory could not be filled due to errors.");
LED_C_ON();
}
}
}
}