diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 67afee4f1..60023fe4d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2552,6 +2552,7 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype ) { /* * Mifare Classic NACK-bug detection +* Thanks to @doegox for the feedback and new approaches. */ void DetectNACKbug() { @@ -2571,6 +2572,7 @@ void DetectNACKbug() { uint16_t sync_tries = 0; uint32_t sync_time = 0; bool have_uid = false; + bool received_nack; // Mifare Classic's random generator repeats every 2^16 cycles (and so do the nonces). uint32_t sync_cycles = PRNG_SEQUENCE_LENGTH; @@ -2592,6 +2594,18 @@ void DetectNACKbug() { uint16_t i; for (i = 0; true; ++i) { + received_nack = false; + if ((i==10) && (num_nacks == i)) { + // Cards always leaks a NACK, no matter the parity + isOK = 2; + break; + } + if ( (i > 1) && (num_nacks == 1) ) { + // NACK bug + isOK = 1; + break; + } + WDT_HIT(); // Test if the action was cancelled @@ -2639,7 +2653,7 @@ void DetectNACKbug() { // Receive the (4 Byte) "random" nonce from TAG if (!ReaderReceive(receivedAnswer, receivedAnswerPar)) continue; - + previous_nt = nt; nt = bytes_to_num(receivedAnswer, 4); @@ -2735,7 +2749,7 @@ void DetectNACKbug() { // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding if (ReaderReceive(receivedAnswer, receivedAnswerPar)) { catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer - isOK = 1; + received_nack = true; num_nacks++; } diff --git a/client/Makefile b/client/Makefile index a53903dd6..b05845db1 100644 --- a/client/Makefile +++ b/client/Makefile @@ -212,7 +212,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ #GCC_GTEQ_490 := $(shell expr `gcc --version | awk '/gcc/{print $$NF;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/'` \>= 40900) GCC_VERSION := $(shell gcc --version | awk '/gcc/{print $$NF;}' | sed -e 's/\-.*//g' -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') -CLANG_VERSION := $(shell gcc --version | awk '/Apple LLVM version/{print $$4;}' | sed -e -e 's/\-.*//g' 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') +CLANG_VERSION := $(shell gcc --version | awk '/Apple LLVM version/{print $$4;}' | sed -e 's/\-.*//g' -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') ifneq ($(CLANG_VERSION), ) SUPPORTS_AVX512 := $(shell [ $(CLANG_VERSION) -ge 80000 ] && echo "True" ) endif diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 2f7cc4077..01fe70f60 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -321,6 +321,17 @@ int usage_hf14_csave(void){ PrintAndLog(" hf mf csave 4 o filename"); return 0; } +int usage_hf14_nack(void) { + PrintAndLog("Test a mifare classic based card for the NACK bug."); + PrintAndLog(""); + PrintAndLog("Usage: hf mf nack [h] [v]"); + PrintAndLog("Options:"); + PrintAndLog(" h this help"); + PrintAndLog(" v verbose"); + PrintAndLog("samples:"); + PrintAndLog(" hf mf nack"); + return 0; +} int CmdHF14AMifare(const char *Cmd) { uint8_t blockno = 0, key_type = MIFARE_AUTH_KEYA; @@ -2851,43 +2862,15 @@ int CmdHf14AMfSetMod(const char *Cmd) { // Mifare NACK bug detection int CmdHf14AMfNack(const char *Cmd) { - UsbCommand c = {CMD_MIFARE_NACK_DETECT, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; + bool verbose = false; + char ctmp = param_getchar(Cmd, 0); + if ( ctmp == 'h' || ctmp == 'H' ) return usage_hf14_nack(); + if ( ctmp == 'v' || ctmp == 'V' ) verbose = true; + + if ( verbose ) + PrintAndLog("Started testing card for NACK bug. Press key to abort"); - PrintAndLog("Started testing card for NACK bug"); - - while (true) { - - printf("."); - fflush(stdout); - if (ukbhit()) { - int gc = getchar(); (void)gc; - return -1; - break; - } - - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - int32_t ok = resp.arg[0]; - uint32_t nacks = resp.arg[1]; - uint32_t auths = resp.arg[2]; - - PrintAndLog("\nNum of sent auth requestes : %u", auths); - PrintAndLog("Num of received NACK : %u", nacks); - switch( ok ) { - case -1 : PrintAndLog("Button pressed. Aborted."); return 1; - case -2 : PrintAndLog("Card answers NACK, (most likely a clone)"); return 1; - case -3 : PrintAndLog("Card random number generator is not predictable)."); return 1; - case -4 : PrintAndLog("Card random number generator seems to be based on the wellknown"); - PrintAndLog("generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return 1; - case 1 : PrintAndLog("Card has NACK bug."); return 1; - case 0 : PrintAndLog("Card has not NACK bug."); return 1; - default : PrintAndLog(" errorcode from device [%i]", ok); return 1; - } - break; - } - } + detect_classic_nackbug(verbose); return 0; } diff --git a/client/mifarehost.c b/client/mifarehost.c index 3c234d288..1e1cce3b1 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -81,8 +81,7 @@ static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_i return i; } -int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) -{ +int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; uint32_t nt = 0, nr = 0; uint64_t par_list = 0, ks_list = 0; @@ -852,15 +851,59 @@ bool detect_classic_prng(void){ uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); return validate_prng_nonce(nonce); } -/* Detect Mifare Classic NACK bug */ -bool detect_classic_nackbug(void){ +/* Detect Mifare Classic NACK bug + +returns: +0 = error during test / aborted +1 = has nack bug +2 = has not nack bug +3 = always leak nacks (clones) +*/ +int detect_classic_nackbug(bool verbose){ - // get nonce? + UsbCommand c = {CMD_MIFARE_NACK_DETECT, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; - // loop max 256 times, - // fixed nonce, different parity every call + if ( verbose ) + printf("Press pm3-button on the proxmark3 device to abort both proxmark3 and client.\n"); + + while (true) { + + printf("."); + fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + return -1; + break; + } - return false; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + int32_t ok = resp.arg[0]; + uint32_t nacks = resp.arg[1]; + uint32_t auths = resp.arg[2]; + if ( verbose ) { + PrintAndLog("\nNum of sent auth requests : %u", auths); + PrintAndLog("Num of received NACK : %u", nacks); + } + switch( ok ) { + case -1 : if (verbose) PrintAndLog("Button pressed. Aborted."); return 0; + case -3 : if (verbose) PrintAndLog("Card random number generator is not predictable)."); return 0; + case -4 : if (verbose) { + PrintAndLog("Card random number generator seems to be based on the well-known generating polynomial"); + PrintAndLog("with 16 effective bits only, but shows unexpected behavior, try again."); + return 0; + } + case 2 : PrintAndLog("Card always leak NACK."); return 3; + case 1 : PrintAndLog("Card has NACK bug."); return 1; + case 0 : PrintAndLog("Card has not NACK bug."); return 2; + default : PrintAndLog(" errorcode from device [%i]", ok); return 0; + } + break; + } + } + return 0; } /* try to see if card responses to "chinese magic backdoor" commands. */ void detect_classic_magic(void) { diff --git a/client/mifarehost.h b/client/mifarehost.h index 19e963b44..3c58583dc 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -67,8 +67,6 @@ typedef struct { //uint8_t foundKey[2]; } icesector_t; - - extern char logHexFileName[FILE_PATH_SIZE]; extern int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key); @@ -95,6 +93,6 @@ extern int saveTraceCard(void); extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); extern bool detect_classic_prng(void); -extern bool detect_classic_nackbug(void); +extern int detect_classic_nackbug(bool verbose); extern void detect_classic_magic(void); #endif \ No newline at end of file