From a739d333982e190566906b61f89364ef8e3ba578 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 15 May 2020 13:07:27 +0200 Subject: [PATCH] Add mf_nonce_brute from https://github.com/iceman1001/mf_nonce_brute and merge to pm3 Makefile --- .gitignore | 2 +- Makefile.host | 4 +- tools/Makefile | 4 - tools/mf_nonce_brute/Makefile | 20 ++ tools/mf_nonce_brute/README.md | 132 +++++++++ tools/mf_nonce_brute/iso14443crc.c | 46 +++ tools/mf_nonce_brute/iso14443crc.h | 26 ++ tools/mf_nonce_brute/mf_nonce_brute.c | 395 ++++++++++++++++++++++++++ tools/mf_nonce_brute/protocol.h | 20 ++ tools/mf_nonce_brute/sleep.c | 28 ++ tools/mf_nonce_brute/sleep.h | 27 ++ 11 files changed, 698 insertions(+), 6 deletions(-) create mode 100644 tools/mf_nonce_brute/Makefile create mode 100644 tools/mf_nonce_brute/README.md create mode 100644 tools/mf_nonce_brute/iso14443crc.c create mode 100644 tools/mf_nonce_brute/iso14443crc.h create mode 100644 tools/mf_nonce_brute/mf_nonce_brute.c create mode 100644 tools/mf_nonce_brute/protocol.h create mode 100644 tools/mf_nonce_brute/sleep.c create mode 100644 tools/mf_nonce_brute/sleep.h diff --git a/.gitignore b/.gitignore index fd8fe070c..d28933d82 100644 --- a/.gitignore +++ b/.gitignore @@ -87,7 +87,7 @@ client/dumps/* *.ice *.new armsrc/TEMP EMV/* -tools/mf_nonce_brute/* +tools/mf_nonce_brute/mf_nonce_brute tools/andrew/* tools/jtag_openocd/openocd_configuration ppls patches/* diff --git a/Makefile.host b/Makefile.host index c95f4bb4f..07b8c2199 100644 --- a/Makefile.host +++ b/Makefile.host @@ -17,6 +17,8 @@ endif CFLAGS ?= $(DEFCFLAGS) CFLAGS += $(MYDEFS) $(MYCFLAGS) $(MYINCLUDES) +LDFLAGS += $(MYLDFLAGS) +LDLIBS += $(MYLDLIBS) vpath %.c $(MYSRCPATHS) @@ -66,7 +68,7 @@ $(BINDIR)/$(LIB_A): $(MYOBJS) $(BINDIR)/% : $(OBJDIR)/%.o $(MYOBJS) $(MYLIBS) $(info [=] LD $(notdir $@)) - $(Q)$(LD) $(LDFLAGS) $(MYOBJS) $< -o $@ $(MYLIBS) + $(Q)$(LD) $(LDFLAGS) $(MYOBJS) $< -o $@ $(MYLIBS) $(MYLDLIBS) $(OBJDIR)/%.o : %.c | $(OBJDIR) $(info [-] CC $<) diff --git a/tools/Makefile b/tools/Makefile index 5f2355f20..df6a0682f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -11,10 +11,6 @@ get_crapto1: $(WGET) http://crapto1.netgarage.org/crapto1-v3.3.tar.xz $(TAR) Jxvf crapto1-v3.3.tar.xz -C crapto1-v3.3 -get_nonce_bf: -# $(GIT) https://github.com/J-Run/mf_key_brute.git mf_key_brute - $(GIT) https://github.com/iceman1001/mf_nonce_brute mf_nonce_brute - get_xorsearch: $(MKDIR) xorsearch $(WGET) https://didierstevens.com/files/software/XORSearch_V1_11_2.zip diff --git a/tools/mf_nonce_brute/Makefile b/tools/mf_nonce_brute/Makefile new file mode 100644 index 000000000..7672c89a4 --- /dev/null +++ b/tools/mf_nonce_brute/Makefile @@ -0,0 +1,20 @@ +MYSRCPATHS = ../../common ../../common/crapto1 +MYSRCS = crypto1.c crapto1.c bucketsort.c iso14443crc.c sleep.c +MYINCLUDES = -I../../include -I../../common +MYCFLAGS = +MYDEFS = +MYLDLIBS = -lpthread + +BINS = mf_nonce_brute +INSTALLTOOLS = $(BINS) + +include ../../Makefile.host + +# checking platform can be done only after Makefile.host +ifneq (,$(findstring MINGW,$(platform))) + # Mingw uses by default Microsoft printf, we want the GNU printf (e.g. for %z) + # and setting _ISOC99_SOURCE sets internally __USE_MINGW_ANSI_STDIO=1 + CFLAGS += -D_ISOC99_SOURCE +endif + +mf_nonce_brute : $(OBJDIR)/mf_nonce_brute.o $(MYOBJS) diff --git a/tools/mf_nonce_brute/README.md b/tools/mf_nonce_brute/README.md new file mode 100644 index 000000000..c2e5f3e6f --- /dev/null +++ b/tools/mf_nonce_brute/README.md @@ -0,0 +1,132 @@ +mf_nonce_brute +============== + +Nested authentificated sector key recovery tool +----------------------------------------------- + +Compatible tags: +* Mifare Classic 1k (4k) +* Mifare Plus in SL1 mode + +To recover keys to nested authentificated sectors you need a reader-card communication log. To get it use +hardware tools that able to sniff communication (for example Proxmark3 or HydraNFC). + +This enhanced version: +First 2 bytes should be bruteforced in phase 2 with mf_key_brute tool that interacts with a card. + +Sample trace: +``` + 93 70 fd ac f6 d8 7f 21 4f // select card with UID fdacf6d8 +TAG 08 b6 dd // sak + 60 04 d1 3d // wanna auth block 0x04 with A key +TAG ed 12 9c 74 // 1st auth clear text nt + 55 53 9f cc 41 8d e8 f3 // nr', ar' (nr^ks1, ar^ks2 ) +TAG 05 49 e1 65 // at' ( at^ks3 ) + 03 24 26 56 // wanna read block 0x04 +TAG ac 69 ef 58 45 e1 c2 1d a9 47 a5 94 54 ef 5d c7 1e a9 // block 0x04 content + d4 3e a8 aa +TAG 8e 8e e3 e6 e9 e2 5f dd f6 08 ce fb 02 6a db 75 94 2f + 79 77 68 3c +TAG e0 00 00 80 80 08 cc 80 08 9c 82 e0 68 64 60 30 91 60 // 18 bytes = 16 byte content + 2 bytes crc + ea 88 c3 c2 // 4 byte read cmd +TAG a3 76 dc df c1 42 e0 ee c6 75 a4 ca eb 0c da eb 46 a0 // 18 bytes = 16 byte content + 2 bytes crc ks8 + crc + 2d 27 ab 6f // wanna auth to 0x04 block with key B + +-------Until this line we can recover key or decrypt communication with no troubles (see mfkey64 tool)-------------------------------- + +TAG 52 6e af 8b // nested auth encrypted tag nonce that we dont know + 8e 21 3a 29 a4 80 7e 02 // nr_enc = nr^ks1, ar_enc = ar^ks2 +TAG b9 43 74 8d // at_enc = at^ks3 + e2 25 f8 32 // probably next command (actually is read block cmd, but we dont know it yet) +TAG 1f 26 82 8d 12 21 dd 42 c2 84 3e d0 26 7f 6b 2a 81 a9 // probably data + ba 85 1d 36 // probably read cmd +TAG 62 a8 78 69 ee 36 22 16 1c ff 4b 4e 69 cb 27 c2 e8 7e // probably data + a7 b1 c8 da // probably read cmd +TAG b2 fc 6c 65 60 ec 35 83 87 56 e3 7e 3c bf 38 b8 73 21 // probably data + 99 92 13 55 // probably read cmd +TAG 93 5b 65 a3 1d 8c 75 b8 3a 63 e2 31 f0 d0 a9 24 9a f6 // probably data +``` + + +Phase 1 +------- + +Syntax: +`mf_nonce_brute <{nt}> <{nr}> <{ar}> <{at}> [<{next_command}>]` + +Example: if `nt` in trace is `8c! 42 e6! 4e!`, then `nt` is `8c42e64e` and `nt_par_err` is `1011` + +Example with parity (from this trace http://www.proxmark.org/forum/viewtopic.php?pid=550#p550) : + +``` + + 561882: 1 : 26 + + 64: 2 : TAG 04 00 + + 10217: 2 : 93 20 + + 64: 5 : TAG 9c 59 9b 32 6c UID + + 12313: 9 : 93 70 9c 59 9b 32 6c 6b 30 + + 64: 3 : TAG 08 b6 dd + + 923318: 4 : 60 00 f5 7b AUTH Block 0 + + 112: 4 : TAG 82 a4 16 6c Nonce Tag (NT) + + 6985: 8 : a1 e4! 58 ce! 6e ea! 41 e0! NR , AR + + 64: 4 : TAG 5c! ad f4 39! AT + + 811513: 4 : 8e 0e! 5d! b9 AUTH Block 0 (nested) + + 112: 4 : TAG 5a! 92 0d! 85! Nonce Tag (NT) + + 6946: 8 : 98! d7 6b! 77 d6 c6 e8 70 NR , AR + + 64: 4 : TAG ca 7e! 0b! 63! AT + + 670868: 4 : 3e! 70 9c! 8a + + 112: 4 : TAG 36! 41 24! 79 + + 9505: 8 : 1b! 8c 3a! 48! 83 5a 4a! 27 + + 64: 4 : TAG 40! 6a! 99! 4b + + 905612: 4 : c9 7c 64! 13! !crc + + 112: 4 : TAG b5! ab! 1d! 2b + + 6936: 8 : 7e! d2 5c! ca! 4b! 50! 88! c4 !crc + + 64: 4 : TAG bf dd 01 be! + + 987853: 4 : 56 98 49 d6! !crc +``` +=> +``` +./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 3e709c8a + | | | | | | | | | + +UID +nt_enc | +nr_enc +ar_enc | +at_enc | +encrypted next cmd + +nt_par_err +at_par_err +at_par_err +``` + +These two taken from above use the plaintext tagnonce `nt`=`82a4166c`, they still find a possible key candidate. +``` +./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 +./mf_nonce_brute 9c599b32 82a4166c 0000 98d76b77 d6c6e870 0000 ca7e0b63 0111 +``` + +This one uses the encrypted tagnonce `nt`=`5a920d85`, it finds a valid key. +``` +./mf_nonce_brute 9c599b32 5a920d85 1011 98d76b77 d6c6e870 0000 ca7e0b63 0111 +``` + +This one uses the encrypted tagnonce `nt`=`5a920d85` and the encrypted cmd `3e709c8a` to validate , it finds a valid key. +``` +./mf_nonce_brute 9c599b32 5a920d85 1011 98d76b77 d6c6e870 0000 ca7e0b63 0111 3e709c8a +``` +Full output: +``` +$ ./mf_nonce_brute 9c599b32 5a920d85 1011 98d76b77 d6c6e870 0000 ca7e0b63 0111 3e709c8a +Mifare classic nested auth key recovery. Phase 1. +------------------------------------------------- +uid: 9c599b32 +nt encrypted: 5a920d85 +nt parity err: 1011 +nr encrypted: 98d76b77 +ar encrypted: d6c6e870 +ar parity err: 0000 +at encrypted: ca7e0b63 +at parity err: 0111 +next cmd enc: 3e709c8a + + +Starting 4 threads to bruteforce encrypted tag nonce last bytes +CMD enc(3e709c8a) + dec(6000f57b) <-- Valid cmd + +Valid Key found: [ffffffffffff] + +Time in mf_nonce_brute (Phase 1): 1763 ticks 2.0 seconds +``` diff --git a/tools/mf_nonce_brute/iso14443crc.c b/tools/mf_nonce_brute/iso14443crc.c new file mode 100644 index 000000000..60631f4fb --- /dev/null +++ b/tools/mf_nonce_brute/iso14443crc.c @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// ISO14443 CRC calculation code. +//----------------------------------------------------------------------------- + +#include "iso14443crc.h" + +static unsigned short UpdateCrc14443(unsigned char ch, unsigned short *lpwCrc) { + ch = (ch ^ (unsigned char)((*lpwCrc) & 0x00FF)); + ch = (ch ^ (ch << 4)); + *lpwCrc = (*lpwCrc >> 8) ^ ((unsigned short) ch << 8) ^ + ((unsigned short) ch << 3) ^ ((unsigned short) ch >> 4); + return (*lpwCrc); +} + +void ComputeCrc14443(int CrcType, + const unsigned char *Data, int Length, + unsigned char *TransmitFirst, + unsigned char *TransmitSecond) { + unsigned char chBlock; + unsigned short wCrc = CrcType; + + do { + chBlock = *Data++; + UpdateCrc14443(chBlock, &wCrc); + } while (--Length); + + if (CrcType == CRC_14443_B) + wCrc = ~wCrc; /* ISO/IEC 13239 (formerly ISO/IEC 3309) */ + + *TransmitFirst = (unsigned char)(wCrc & 0xFF); + *TransmitSecond = (unsigned char)((wCrc >> 8) & 0xFF); + return; +} + +int CheckCrc14443(int CrcType, const unsigned char *Data, int Length) { + unsigned char b1; + unsigned char b2; + if (Length < 3) return 0; + ComputeCrc14443(CrcType, Data, Length - 2, &b1, &b2); + if ((b1 == Data[Length - 2]) && (b2 == Data[Length - 1])) return 1; + return 0; +} diff --git a/tools/mf_nonce_brute/iso14443crc.h b/tools/mf_nonce_brute/iso14443crc.h new file mode 100644 index 000000000..e9a82d521 --- /dev/null +++ b/tools/mf_nonce_brute/iso14443crc.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// ISO14443 CRC calculation code. +//----------------------------------------------------------------------------- + +#ifndef __ISO14443CRC_H +#define __ISO14443CRC_H + +//----------------------------------------------------------------------------- +// Routines to compute the CRCs (two different flavours, just for confusion) +// required for ISO 14443, swiped directly from the spec. +//----------------------------------------------------------------------------- +#define CRC_14443_A 0x6363 /* ITU-V.41 */ +#define CRC_14443_B 0xFFFF /* ISO/IEC 13239 (formerly ISO/IEC 3309) */ +#define CRC_ICLASS 0xE012 /* ICLASS PREFIX */ + +void ComputeCrc14443(int CrcType, + const unsigned char *Data, int Length, + unsigned char *TransmitFirst, + unsigned char *TransmitSecond); +int CheckCrc14443(int CrcType, const unsigned char *Data, int Length); + +#endif diff --git a/tools/mf_nonce_brute/mf_nonce_brute.c b/tools/mf_nonce_brute/mf_nonce_brute.c new file mode 100644 index 000000000..6bc69c3d9 --- /dev/null +++ b/tools/mf_nonce_brute/mf_nonce_brute.c @@ -0,0 +1,395 @@ +#define __STDC_FORMAT_MACROS +#define _USE_32BIT_TIME_T 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include "crapto1/crapto1.h" +#include "protocol.h" +#include "iso14443crc.h" + +#define odd_parity(i) (( (i) ^ (i)>>1 ^ (i)>>2 ^ (i)>>3 ^ (i)>>4 ^ (i)>>5 ^ (i)>>6 ^ (i)>>7 ^ 1) & 0x01) + +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; + +//--------------------- define options here +uint32_t uid = 0; // serial number +uint32_t nt_enc = 0; // Encrypted tag nonce +uint32_t nr_enc = 0; // encrypted reader challenge +uint32_t ar_enc = 0; // encrypted reader response +uint32_t at_enc = 0; // encrypted tag response +uint32_t cmd_enc = 0; // next encrypted command to sector + +uint32_t nt_par_err = 0; +uint32_t ar_par_err = 0; +uint32_t at_par_err = 0; + +typedef struct thread_args { + uint16_t xored; + int thread; + int idx; + bool ev1; +} targs; + +//------------------------------------------------------------------ +uint8_t cmds[] = { + ISO14443A_CMD_READBLOCK, + ISO14443A_CMD_WRITEBLOCK, + MIFARE_AUTH_KEYA, + MIFARE_AUTH_KEYB, + MIFARE_CMD_INC, + MIFARE_CMD_DEC, + MIFARE_CMD_RESTORE, + MIFARE_CMD_TRANSFER +}; + +int global_counter = 0; +int global_fin_flag = 0; +int global_found = 0; +int global_found_candidate = 0; +size_t thread_count = 4; + +static uint16_t parity_from_err(uint32_t data, uint16_t par_err) { + + uint16_t par = 0; + par |= odd_parity((data >> 24) & 0xFF) ^ ((par_err >> 12) & 1); + par <<= 4; + + par |= odd_parity((data >> 16) & 0xFF) ^ ((par_err >> 8) & 1); + par <<= 4; + + par |= odd_parity((data >> 8) & 0xFF) ^ ((par_err >> 4) & 1); + par <<= 4; + + par |= odd_parity(data & 0xFF) ^ (par_err & 1); + return par; +} + +static uint16_t xored_bits(uint16_t nt_par, uint32_t ntenc, uint16_t ar_par, uint32_t arenc, uint16_t at_par, uint32_t atenc) { + uint16_t xored = 0; + + uint8_t par; + //1st (1st nt) + par = (nt_par >> 12) & 1; + xored |= par ^ ((ntenc >> 16) & 1); + xored <<= 1; + + //2nd (2nd nt) + par = (nt_par >> 8) & 1; + xored |= par ^ ((ntenc >> 8) & 1); + xored <<= 1; + + //3rd (3rd nt) + par = (nt_par >> 4) & 1; + xored |= par ^ (ntenc & 1); + xored <<= 1; + + //4th (1st ar) + par = (ar_par >> 12) & 1; + xored |= par ^ ((arenc >> 16) & 1); + xored <<= 1; + + //5th (2nd ar) + par = (ar_par >> 8) & 1; + xored |= par ^ ((arenc >> 8) & 1); + xored <<= 1; + + //6th (3rd ar) + par = (ar_par >> 4) & 1; + xored |= par ^ (arenc & 1); + xored <<= 1; + + //7th (4th ar) + par = ar_par & 1; + xored |= par ^ ((atenc >> 24) & 1); + xored <<= 1; + + //8th (1st at) + par = (at_par >> 12) & 1; + xored |= par ^ ((atenc >> 16) & 1); + xored <<= 1; + + //9th (2nd at) + par = (at_par >> 8) & 1; + xored |= par ^ ((atenc >> 8) & 1); + xored <<= 1; + + //10th (3rd at) + par = (at_par >> 4) & 1; + xored |= par ^ (atenc & 1); + + return xored; +} + +static bool candidate_nonce(uint32_t xored, uint32_t nt, bool ev1) { + uint8_t byte, check; + + if (!ev1) { + //1st (1st nt) + byte = (nt >> 24) & 0xFF; + check = odd_parity(byte) ^ ((nt >> 16) & 1) ^ ((xored >> 9) & 1); + if (check) return false; + + //2nd (2nd nt) + byte = (nt >> 16) & 0xFF; + check = odd_parity(byte) ^ ((nt >> 8) & 1) ^ ((xored >> 8) & 1); + if (check) return false; + } + + //3rd (3rd nt) + byte = (nt >> 8) & 0xFF; + check = odd_parity(byte) ^ (nt & 1) ^ ((xored >> 7) & 1); + if (check) return false; + + uint32_t ar = prng_successor(nt, 64); + + //4th (1st ar) + byte = (ar >> 24) & 0xFF; + check = odd_parity(byte) ^ ((ar >> 16) & 1) ^ ((xored >> 6) & 1); + if (check) return false; + + //5th (2nd ar) + byte = (ar >> 16) & 0x0FF; + check = odd_parity(byte) ^ ((ar >> 8) & 1) ^ ((xored >> 5) & 1); + if (check) return false; + + //6th (3rd ar) + byte = (ar >> 8) & 0xFF; + check = odd_parity(byte) ^ (ar & 1) ^ ((xored >> 4) & 1); + if (check) return false; + + uint32_t at = prng_successor(nt, 96); + + //7th (4th ar) + byte = ar & 0xFF; + check = odd_parity(byte) ^ ((at >> 24) & 1) ^ ((xored >> 3) & 1); + if (check) return false; + + //8th (1st at) + byte = (at >> 24) & 0xFF; + check = odd_parity(byte) ^ ((at >> 16) & 1) ^ ((xored >> 2) & 1); + if (check) return false; + + //9th (2nd at) + byte = (at >> 16) & 0xFF; + check = odd_parity(byte) ^ ((at >> 8) & 1) ^ ((xored >> 1) & 1) ; + if (check) return false; + + //10th (3rd at) + byte = (at >> 8) & 0xFF; + check = odd_parity(byte) ^ (at & 1) ^ (xored & 1); + if (check) return false; + + return true; +} + +static bool checkValidCmd(uint32_t decrypted) { + uint8_t cmd = (decrypted >> 24) & 0xFF; + for (int i = 0; i < sizeof(cmds); ++i) { + if (cmd == cmds[i]) + return true; + } + return false; +} + +static bool checkCRC(uint32_t decrypted) { + uint8_t data[] = { + (decrypted >> 24) & 0xFF, + (decrypted >> 16) & 0xFF, + (decrypted >> 8) & 0xFF, + decrypted & 0xFF + }; + return CheckCrc14443(CRC_14443_A, data, sizeof(data)); +} + +static void *brute_thread(void *arguments) { + + //int shift = (int)arg; + struct thread_args *args = (struct thread_args *) arguments; + + struct Crypto1State *revstate; + uint64_t key; // recovered key candidate + uint32_t ks2; // keystream used to encrypt reader response + uint32_t ks3; // keystream used to encrypt tag response + uint32_t ks4; // keystream used to encrypt next command + uint32_t nt; // current tag nonce + + uint32_t p64 = 0; + uint32_t count; + int found = 0; + // TC == 4 ( + // threads calls 0 ev1 == false + // threads calls 0,1,2 ev1 == true + for (count = args->idx; count < 0xFFFF; count += thread_count - 1) { + + found = global_found; + if (found) break; + + nt = count << 16 | prng_successor(count, 16); + + if (!candidate_nonce(args->xored, nt, args->ev1)) + continue; + + p64 = prng_successor(nt, 64); + ks2 = ar_enc ^ p64; + ks3 = at_enc ^ prng_successor(p64, 32); + revstate = lfsr_recovery64(ks2, ks3); + ks4 = crypto1_word(revstate, 0, 0); + + if (ks4 != 0) { + + // lock this section to avoid interlacing prints from different threats + pthread_mutex_lock(&print_lock); + if (args->ev1) + printf("\n**** Possible key candidate ****\n"); + +#if 0 + printf("thread #%d idx %d %s\n", args->thread, args->idx, (args->ev1) ? "(Ev1)" : ""); + printf("current nt(%08x) ar_enc(%08x) at_enc(%08x)\n", nt, ar_enc, at_enc); + printf("ks2:%08x\n", ks2); + printf("ks3:%08x\n", ks3); + printf("ks4:%08x\n", ks4); +#endif + if (cmd_enc) { + uint32_t decrypted = ks4 ^ cmd_enc; + printf("CMD enc(%08x)\n", cmd_enc); + printf(" dec(%08x)\t", decrypted); + + uint8_t isOK = 0; + // check if cmd exists + isOK = checkValidCmd(decrypted); + + // Add a crc-check. + isOK = checkCRC(decrypted); + + if (!isOK) { + printf("<-- not a valid cmd\n"); + pthread_mutex_unlock(&print_lock); + continue; + } else { + printf("<-- Valid cmd\n"); + } + } + + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, uid ^ nt, 0); + crypto1_get_lfsr(revstate, &key); + free(revstate); + + if (args->ev1) { + printf("\nKey candidate: [%012" PRIx64 "]\n\n", key); + __sync_fetch_and_add(&global_found_candidate, 1); + } else { + printf("\nValid Key found: [%012" PRIx64 "]\n\n", key); + __sync_fetch_and_add(&global_found, 1); + } + //release lock + pthread_mutex_unlock(&print_lock); + } + } + return NULL; +} + +static int usage(void) { + printf(" syntax: mf_nonce_brute []\n\n"); + printf(" example: nt in trace = 8c! 42 e6! 4e!\n"); + printf(" nt = 8c42e64e\n"); + printf(" nt_par_err = 1011\n\n"); + printf("\n expected outcome:\n"); + printf(" KEY 0xFFFFFFFFFFFF == fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n"); + return 1; +} + +int main(int argc, char *argv[]) { + printf("Mifare classic nested auth key recovery. Phase 1.\n"); + + if (argc < 9) return usage(); + + sscanf(argv[1], "%x", &uid); + sscanf(argv[2], "%x", &nt_enc); + sscanf(argv[3], "%x", &nt_par_err); + sscanf(argv[4], "%x", &nr_enc); + sscanf(argv[5], "%x", &ar_enc); + sscanf(argv[6], "%x", &ar_par_err); + sscanf(argv[7], "%x", &at_enc); + sscanf(argv[8], "%x", &at_par_err); + + if (argc > 9) + sscanf(argv[9], "%x", &cmd_enc); + + printf("-------------------------------------------------\n"); + printf("uid:\t\t%08x\n", uid); + printf("nt encrypted:\t%08x\n", nt_enc); + printf("nt parity err:\t%04x\n", nt_par_err); + printf("nr encrypted:\t%08x\n", nr_enc); + printf("ar encrypted:\t%08x\n", ar_enc); + printf("ar parity err:\t%04x\n", ar_par_err); + printf("at encrypted:\t%08x\n", at_enc); + printf("at parity err:\t%04x\n", at_par_err); + + if (argc > 9) + printf("next cmd enc:\t%08x\n\n", cmd_enc); + + clock_t t1 = clock(); + uint16_t nt_par = parity_from_err(nt_enc, nt_par_err); + uint16_t ar_par = parity_from_err(ar_enc, ar_par_err); + uint16_t at_par = parity_from_err(at_enc, at_par_err); + + //calc (parity XOR corresponding nonce bit encoded with the same keystream bit) + uint16_t xored = xored_bits(nt_par, nt_enc, ar_par, ar_enc, at_par, at_enc); + +#ifndef __WIN32 + thread_count = sysconf(_SC_NPROCESSORS_CONF); + if (thread_count < 2) + thread_count = 2; +#endif /* _WIN32 */ + + printf("\nBruteforce using %zu threads to find encrypted tagnonce last bytes\n", thread_count); + + pthread_t threads[thread_count]; + + // create a mutex to avoid interlacing print commands from our different threads + pthread_mutex_init(&print_lock, NULL); + + // one thread T0 for none EV1. + struct thread_args *a = malloc(sizeof(struct thread_args)); + a->xored = xored; + a->thread = 0; + a->idx = 0; + a->ev1 = false; + pthread_create(&threads[0], NULL, brute_thread, (void *)a); + + // the rest of available threads to EV1 scenario + for (int i = 0; i < thread_count - 1; ++i) { + struct thread_args *b = malloc(sizeof(struct thread_args)); + b->xored = xored; + b->thread = i + 1; + b->idx = i; + b->ev1 = true; + pthread_create(&threads[i + 1], NULL, brute_thread, (void *)b); + } + + // wait for threads to terminate: + for (int i = 0; i < thread_count; ++i) + pthread_join(threads[i], NULL); + + if (!global_found && !global_found_candidate) { + printf("\nFailed to find a key\n\n"); + } + + t1 = clock() - t1; + if (t1 > 0) + printf("Execution time: %.0f ticks\n", (float)t1); + + // clean up mutex + pthread_mutex_destroy(&print_lock); + return 0; +} diff --git a/tools/mf_nonce_brute/protocol.h b/tools/mf_nonce_brute/protocol.h new file mode 100644 index 000000000..4f7f8b7e0 --- /dev/null +++ b/tools/mf_nonce_brute/protocol.h @@ -0,0 +1,20 @@ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#define ISO14443A_CMD_READBLOCK 0x30 +#define ISO14443A_CMD_WRITEBLOCK 0xA0 + +#define MIFARE_AUTH_KEYA 0x60 +#define MIFARE_AUTH_KEYB 0x61 +#define MIFARE_CMD_INC 0xC0 +#define MIFARE_CMD_DEC 0xC1 +#define MIFARE_CMD_RESTORE 0xC2 +#define MIFARE_CMD_TRANSFER 0xB0 + +// mifare 4bit card answers +#define CARD_ACK 0x0A // 1010 - ACK +#define CARD_NACK_NA 0x04 // 0100 - NACK, not allowed (command not allowed) +#define CARD_NACK_TR 0x05 // 0101 - NACK, transmission error + +#endif +// PROTOCOL_H diff --git a/tools/mf_nonce_brute/sleep.c b/tools/mf_nonce_brute/sleep.c new file mode 100644 index 000000000..0dd4c089d --- /dev/null +++ b/tools/mf_nonce_brute/sleep.c @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// +// 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. +//----------------------------------------------------------------------------- +// platform-independant sleep macros +//----------------------------------------------------------------------------- + +#ifndef _WIN32 + +#define _POSIX_C_SOURCE 199309L +#include "sleep.h" +#include +#include +#include +#include + +void nsleep(uint64_t n) { + struct timespec timeout; + timeout.tv_sec = n / 1000000000; + timeout.tv_nsec = n % 1000000000; + while (nanosleep(&timeout, &timeout) && errno == EINTR); +} + +#endif // _WIN32 + diff --git a/tools/mf_nonce_brute/sleep.h b/tools/mf_nonce_brute/sleep.h new file mode 100644 index 000000000..8465b6041 --- /dev/null +++ b/tools/mf_nonce_brute/sleep.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// +// 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. +//----------------------------------------------------------------------------- +// platform-independant sleep macros +//----------------------------------------------------------------------------- + +#ifndef SLEEP_H__ +#define SLEEP_H__ + +#ifdef _WIN32 +# include +# define sleep(n) Sleep(1000 * n) +# define msleep(n) Sleep(n) +#else +# include +# include +void nsleep(uint64_t n); +# define msleep(n) nsleep(1000000 * n) +# define usleep(n) nsleep(1000 * n) +#endif // _WIN32 + +#endif // SLEEP_H__ +