From f9c1dcd9f6e68a8c07cffed697a9c4c8caed6015 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Thu, 26 Jan 2017 18:16:10 +1100 Subject: [PATCH 1/2] Adds random nonce (r) option to `hf mf sim`. This makes the PM3 generate pseudo-random nonces rather than sequential nonces, to make it act a bit more like a "real" MFC card. A reader would otherwise be able to detect the PM3 probing based on the predictable nonces and throw different authentication challenges (or refuse to authenticate at all). The code includes an implementation of a rand-like function (prand), similar to the one from libc, which is seeded automatically based on the time it takes between the PM3 starting up and the first call to the RNG. This isn't cryptographically random, but should be "good enough" to be able to evade basic detection. --- armsrc/iso14443a.c | 19 +++++++++++++++++-- armsrc/util.c | 16 ++++++++++++++++ armsrc/util.h | 2 ++ client/cmdhfmf.c | 6 ++++++ include/usb_cmd.h | 1 + 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 70dc54f1..07bbd37d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2329,6 +2329,7 @@ typedef struct { * FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that * FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished * FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later + * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... * (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) */ @@ -2387,7 +2388,12 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * uint8_t mM = 0; //moebius_modifier for collection storage // Authenticate response - nonce - uint32_t nonce = bytes_to_num(rAUTH_NT, 4); + uint32_t nonce; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + nonce = bytes_to_num(rAUTH_NT, 4); + } //-- Determine the UID // Can be set from emulator memory, incoming data @@ -2535,6 +2541,11 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * LED_C_OFF(); crypto1_destroy(pcs); cardAUTHKEY = 0xff; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + nonce++; + } continue; } @@ -2656,7 +2667,11 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // switch to moebius collection gettingMoebius = true; mM = ATTACK_KEY_COUNT; - nonce = nonce*7; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + nonce = nonce*7; + } break; } } else { diff --git a/armsrc/util.c b/armsrc/util.c index 1dd8dc75..be41bad8 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -431,3 +431,19 @@ uint32_t RAMFUNC GetCountSspClk(){ } } +static uint64_t next_random = 1; + +/* Generates a (non-cryptographically secure) 32-bit random number. + * + * We don't have an implementation of the "rand" function or a clock to seed it + * with, so we just call GetTickCount the first time to seed ourselves. + */ +uint32_t prand() { + if (next_random == 1) { + next_random = GetTickCount(); + } + + next_random = next_random * 6364136223846793005 + 1; + return (uint32_t)(next_random >> 32) % 0xffffffff; +} + diff --git a/armsrc/util.h b/armsrc/util.h index bf5d0cc8..e919764b 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -54,4 +54,6 @@ uint32_t RAMFUNC GetDeltaCountUS(); void StartCountSspClk(); uint32_t RAMFUNC GetCountSspClk(); +uint32_t prand(); + #endif diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 36c8e6c3..2b14c763 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1100,6 +1100,7 @@ int usage_hf14_mf1ksim(void) { PrintAndLog(" x (Optional) Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s)"); PrintAndLog(" e (Optional) set keys found from 'reader attack' to emulator memory (implies x and i)"); PrintAndLog(" f (Optional) get UIDs to use for 'reader attack' from file 'f ' (implies x and i)"); + PrintAndLog(" r (Optional) Generate random nonces instead of sequential nonces."); PrintAndLog("samples:"); PrintAndLog(" hf mf sim u 0a0a0a0a"); PrintAndLog(" hf mf sim u 11223344556677"); @@ -1164,6 +1165,11 @@ int CmdHF14AMf1kSim(const char *Cmd) { exitAfterNReads = param_get8(Cmd, pnr+1); cmdp += 2; break; + case 'r': + case 'R': + flags |= FLAG_RANDOM_NONCE; + cmdp++; + break; case 'u': case 'U': param_gethex_ex(Cmd, cmdp+1, uid, &uidlen); diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 66e6cf91..16d1c5dd 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -217,6 +217,7 @@ typedef struct{ #define FLAG_7B_UID_IN_DATA 0x04 #define FLAG_10B_UID_IN_DATA 0x08 #define FLAG_NR_AR_ATTACK 0x10 +#define FLAG_RANDOM_NONCE 0x20 //Iclass reader flags From 5b5489baf42989b21e0d946aab3f945bc0af0384 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Thu, 26 Jan 2017 20:27:08 +1100 Subject: [PATCH 2/2] hf mf sim: Multiple fixes from review of PR #209. - Don't increment the nonce when random mode is disabled (this breaks the standard attack). - Don't attempt the standard attack when random mode is enabled (there's no point as it won't work, per comments from @pwpiwi). - Attempt the moebius attack if the standard attack fails. --- armsrc/iso14443a.c | 4 +--- client/cmdhfmf.c | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 07bbd37d..83907fce 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2329,7 +2329,7 @@ typedef struct { * FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that * FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished * FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later - * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data + * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data (only allows moebius attack) *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... * (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) */ @@ -2543,8 +2543,6 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * cardAUTHKEY = 0xff; if (flags & FLAG_RANDOM_NONCE) { nonce = prand(); - } else { - nonce++; } continue; } diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 2b14c763..cdac6476 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1016,7 +1016,7 @@ int CmdHF14AMfChk(const char *Cmd) return 0; } -void readerAttack(nonces_t ar_resp[], bool setEmulatorMem) { +void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack) { #define ATTACK_KEY_COUNT 8 // keep same as define in iso14443a.c -> Mifare1ksim() uint64_t key = 0; typedef struct { @@ -1034,7 +1034,7 @@ void readerAttack(nonces_t ar_resp[], bool setEmulatorMem) { for (uint8_t i = 0; i 0) { //PrintAndLog("DEBUG: Trying sector %d, cuid %08x, nt %08x, ar %08x, nr %08x, ar2 %08x, nr2 %08x",ar_resp[i].sector, ar_resp[i].cuid,ar_resp[i].nonce,ar_resp[i].ar,ar_resp[i].nr,ar_resp[i].ar2,ar_resp[i].nr2); - if (mfkey32(ar_resp[i], &key)) { + if (doStandardAttack && mfkey32(ar_resp[i], &key)) { PrintAndLog(" Found Key%s for sector %02d: [%04x%08x]", (ar_resp[i].keytype) ? "B" : "A", ar_resp[i].sector, (uint32_t) (key>>32), (uint32_t) (key &0xFFFFFFFF)); for (uint8_t ii = 0; ii' (implies x and i)"); - PrintAndLog(" r (Optional) Generate random nonces instead of sequential nonces."); + PrintAndLog(" r (Optional) Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works."); PrintAndLog("samples:"); PrintAndLog(" hf mf sim u 0a0a0a0a"); PrintAndLog(" hf mf sim u 11223344556677"); @@ -1252,7 +1280,8 @@ int CmdHF14AMf1kSim(const char *Cmd) { //got a response nonces_t ar_resp[ATTACK_KEY_COUNT*2]; memcpy(ar_resp, resp.d.asBytes, sizeof(ar_resp)); - readerAttack(ar_resp, setEmulatorMem); + // We can skip the standard attack if we have RANDOM_NONCE set. + readerAttack(ar_resp, setEmulatorMem, !(flags & FLAG_RANDOM_NONCE)); if ((bool)resp.arg[1]) { PrintAndLog("Device button pressed - quitting"); fclose(f); @@ -1284,7 +1313,8 @@ int CmdHF14AMf1kSim(const char *Cmd) { if (flags & FLAG_NR_AR_ATTACK) { nonces_t ar_resp[ATTACK_KEY_COUNT*2]; memcpy(ar_resp, resp.d.asBytes, sizeof(ar_resp)); - readerAttack(ar_resp, setEmulatorMem); + // We can skip the standard attack if we have RANDOM_NONCE set. + readerAttack(ar_resp, setEmulatorMem, !(flags & FLAG_RANDOM_NONCE)); } } }