From f172064f988ebd871ce2e941cae1bee905a638c5 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 17 Feb 2017 13:45:36 +0100 Subject: [PATCH 1/6] Port miLazyCracker patch: fix 4k and Mini cf https://github.com/iAmNotSuperman/miLazyCracker/blob/39658a2ac4d2d672720223c6f12533a78937a35d/mfoc_fix_4k_and_mini.diff --- src/mfoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mfoc.c b/src/mfoc.c index 32ef6f7..babbe15 100644 --- a/src/mfoc.c +++ b/src/mfoc.c @@ -680,7 +680,7 @@ int main(int argc, char *const argv[]) } // Finally save all keys + data to file - uint16_t dump_size = (t.num_blocks + 1) * t.num_sectors; + uint16_t dump_size = (t.num_blocks + 1) * 16; if (fwrite(&mtDump, 1, dump_size, pfDump) != dump_size) { fprintf(stdout, "Error, cannot write dump\n"); fclose(pfDump); From e36025bb254d05c8f5d280122ee636076c7d449c Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 17 Feb 2017 13:46:27 +0100 Subject: [PATCH 2/6] Port miLazyCracker patch: support tnp cf https://github.com/iAmNotSuperman/miLazyCracker/blob/39658a2ac4d2d672720223c6f12533a78937a35d/mfoc_support_tnp.diff --- src/mfoc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mfoc.c b/src/mfoc.c index babbe15..92d4ef3 100644 --- a/src/mfoc.c +++ b/src/mfoc.c @@ -256,7 +256,7 @@ int main(int argc, char *const argv[]) } // Test if a compatible MIFARE tag is used - if ((t.nt.nti.nai.btSak & 0x08) == 0) { + if (((t.nt.nti.nai.btSak & 0x08) == 0) && (t.nt.nti.nai.btSak != 0x01)) { ERR("only Mifare Classic is supported"); goto error; } @@ -267,6 +267,7 @@ int main(int argc, char *const argv[]) // see http://www.nxp.com/documents/application_note/AN10833.pdf Section 3.2 switch (t.nt.nti.nai.btSak) { + case 0x01: case 0x08: case 0x88: printf("Found Mifare Classic 1k tag\n"); From 0970559b971def927b6bb03bb6f423d8aa05b549 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 17 Feb 2017 13:48:48 +0100 Subject: [PATCH 3/6] Port miLazyCracker patch: support 2k cf https://github.com/iAmNotSuperman/miLazyCracker/blob/39658a2ac4d2d672720223c6f12533a78937a35d/mfoc_support_2k.diff --- src/mfoc.c | 75 +++++++++++++++++++++++++++++++++++++++++------------- src/mfoc.h | 5 ++++ 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/mfoc.c b/src/mfoc.c index 92d4ef3..6ebae6d 100644 --- a/src/mfoc.c +++ b/src/mfoc.c @@ -56,15 +56,17 @@ #include "slre.h" #include "slre.c" +#define MAX_FRAME_LEN 264 + +static const nfc_modulation nm = { +.nmt = NMT_ISO14443A, +.nbr = NBR_106, +}; + nfc_context *context; int main(int argc, char *const argv[]) { - const nfc_modulation nm = { - .nmt = NMT_ISO14443A, - .nbr = NBR_106, - }; - int ch, i, k, n, j, m; int key, block; int succeed = 1; @@ -270,9 +272,15 @@ int main(int argc, char *const argv[]) case 0x01: case 0x08: case 0x88: - printf("Found Mifare Classic 1k tag\n"); - t.num_sectors = NR_TRAILERS_1k; - t.num_blocks = NR_BLOCKS_1k; + if (get_rats_is_2k(t, r)) { + printf("Found Mifare Plus 2k tag\n"); + t.num_sectors = NR_TRAILERS_2k; + t.num_blocks = NR_BLOCKS_2k; + } else { + printf("Found Mifare Classic 1k tag\n"); + t.num_sectors = NR_TRAILERS_1k; + t.num_blocks = NR_BLOCKS_1k; + } break; case 0x09: printf("Found Mifare Classic Mini tag\n"); @@ -781,11 +789,6 @@ void mf_configure(nfc_device *pdi) void mf_select_tag(nfc_device *pdi, nfc_target *pnt) { - // Poll for a ISO14443A (MIFARE) tag - const nfc_modulation nm = { - .nmt = NMT_ISO14443A, - .nbr = NBR_106, - }; if (nfc_initiator_select_passive_target(pdi, nm, NULL, 0, pnt) < 0) { ERR("Unable to connect to the MIFARE Classic tag"); nfc_close(pdi); @@ -828,10 +831,6 @@ int find_exploit_sector(mftag t) void mf_anticollision(mftag t, mfreader r) { - const nfc_modulation nm = { - .nmt = NMT_ISO14443A, - .nbr = NBR_106, - }; if (nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) < 0) { nfc_perror(r.pdi, "nfc_initiator_select_passive_target"); ERR("Tag has been removed"); @@ -839,6 +838,48 @@ void mf_anticollision(mftag t, mfreader r) } } + +bool +get_rats_is_2k(mftag t, mfreader r) +{ + int res; + uint8_t abtRx[MAX_FRAME_LEN]; + int szRxBits; + uint8_t abtRats[2] = { 0xe0, 0x50}; + // Use raw send/receive methods + if (nfc_device_set_property_bool(r.pdi, NP_EASY_FRAMING, false) < 0) { + nfc_perror(r.pdi, "nfc_configure"); + return false; + } + res = nfc_initiator_transceive_bytes(r.pdi, abtRats, sizeof(abtRats), abtRx, sizeof(abtRx), 0); + if (res > 0) { + // ISO14443-4 card, turn RF field off/on to access ISO14443-3 again + if (nfc_device_set_property_bool(r.pdi, NP_ACTIVATE_FIELD, false) < 0) { + nfc_perror(r.pdi, "nfc_configure"); + return false; + } + if (nfc_device_set_property_bool(r.pdi, NP_ACTIVATE_FIELD, true) < 0) { + nfc_perror(r.pdi, "nfc_configure"); + return false; + } + } + // Reselect tag + if (nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) <= 0) { + printf("Error: tag disappeared\n"); + nfc_close(r.pdi); + nfc_exit(context); + exit(EXIT_FAILURE); + } + if (res >= 10) { + printf("ATS %02X%02X%02X%02X%02X|%02X%02X%02X%02X\n", res, abtRx[0], abtRx[1], abtRx[2], abtRx[3], abtRx[4], abtRx[5], abtRx[6], abtRx[7], abtRx[8]); + return ((abtRx[5] == 0xc1) && (abtRx[6] == 0x05) + && (abtRx[7] == 0x2f) && (abtRx[8] == 0x2f) + && ((t.nt.nti.nai.abtAtqa[1] & 0x02) == 0x00)); + } else { + return false; + } +} + int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d, pKeys *pk, char mode, bool dumpKeysA) { struct Crypto1State *pcs; diff --git a/src/mfoc.h b/src/mfoc.h index 532e834..9ea7547 100644 --- a/src/mfoc.h +++ b/src/mfoc.h @@ -8,6 +8,8 @@ #define NR_TRAILERS_MINI (5) // Mifare Classic 4k 32x64b + 8*256b = 40 #define NR_TRAILERS_4k (40) +// Mifare Classic 2k 32x64b +#define NR_TRAILERS_2k (32) // Number of blocks // Mifare Classic 1k @@ -16,6 +18,8 @@ #define NR_BLOCKS_MINI 0x13 // Mifare Classic 4k #define NR_BLOCKS_4k 0xff +// Mifare Classic 2k +#define NR_BLOCKS_2k 0x7f #define MAX_FRAME_LEN 264 @@ -85,6 +89,7 @@ void mf_select_tag(nfc_device *pdi, nfc_target *pnt); int trailer_block(uint32_t block); int find_exploit_sector(mftag t); void mf_anticollision(mftag t, mfreader r); +bool get_rats_is_2k(mftag t, mfreader r); int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d, pKeys *pk, char mode, bool dumpKeysA); uint32_t median(denonce d); int compar_int(const void *a, const void *b); From 2316ad0815a39e9982e4675f94f68cee1b8e393a Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 17 Feb 2017 15:37:03 +0100 Subject: [PATCH 4/6] Replace non-ANSI-C getline by fgets --- src/mfoc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/mfoc.c b/src/mfoc.c index 6ebae6d..9fb931c 100644 --- a/src/mfoc.c +++ b/src/mfoc.c @@ -122,9 +122,9 @@ int main(int argc, char *const argv[]) //File pointers for the keyfile FILE * fp; - char * line = NULL; + char line[20]; size_t len = 0; - ssize_t read; + char * read; //Regexp declarations static const char *regex = "([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])"; @@ -157,9 +157,8 @@ int main(int argc, char *const argv[]) fprintf(stderr, "Cannot open keyfile: %s, exiting\n", optarg); exit(EXIT_FAILURE); } - while ((read = getline(&line, &len, fp)) != -1) { + while ((read = fgets(line, sizeof(line), fp)) != NULL) { int i, j = 0, str_len = strlen(line); - while (j < str_len && (i = slre_match(regex, line + j, str_len - j, caps, 500, 1)) > 0) { //We've found a key, let's add it to the structure. @@ -177,8 +176,6 @@ int main(int argc, char *const argv[]) j += i; } } - if (line) - free(line); break; case 'k': // Add this key to the default keys From 34d42e5e4758517a0f9250cd2ae0e12357608bf4 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 17 Feb 2017 15:41:22 +0100 Subject: [PATCH 5/6] Port miLazyCracker patch: test PRNG cf https://github.com/iAmNotSuperman/miLazyCracker/blob/39658a2ac4d2d672720223c6f12533a78937a35d/mfoc_test_prng.diff --- src/mfoc.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/src/mfoc.c b/src/mfoc.c index 9fb931c..052014c 100644 --- a/src/mfoc.c +++ b/src/mfoc.c @@ -65,6 +65,48 @@ static const nfc_modulation nm = { nfc_context *context; +uint64_t knownKey = 0; +char knownKeyLetter = 'A'; +uint32_t knownSector = 0; +uint32_t unknownSector = 0; +char unknownKeyLetter = 'A'; +uint32_t unexpected_random = 0; + +// Determine the distance between two nonces. +// Assume that the difference is small, but we don't know which is first. +// Therefore try in alternating directions. +int32_t dist_nt(uint32_t nt1, uint32_t nt2) { + + if (nt1 == nt2) return 0; + + uint16_t i; + uint32_t nttmp1 = nt1; + uint32_t nttmp2 = nt2; + + for (i = 1; i < (32768/8); ++i) { + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -i; + + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+1; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+1); + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+2; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+2); + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+3; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+3); + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+4; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+4); + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+5; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+5); + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+6; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+6); + nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+7; + nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+7); + } + // either nt1 or nt2 are invalid nonces + return(-99999); +} + + int main(int argc, char *const argv[]) { int ch, i, k, n, j, m; @@ -119,6 +161,7 @@ int main(int argc, char *const argv[]) mifare_cmd mc; FILE *pfDump = NULL; + FILE *pfKey = NULL; //File pointers for the keyfile FILE * fp; @@ -199,6 +242,14 @@ int main(int argc, char *const argv[]) } // fprintf(stdout, "Output file: %s\n", optarg); break; + case 'D': + // Partial File output + if (!(pfKey = fopen(optarg, "w"))) { + fprintf(stderr, "Cannot open: %s, exiting\n", optarg); + exit(EXIT_FAILURE); + } + // fprintf(stdout, "Output file: %s\n", optarg); + break; case 'h': usage(stdout, 0); break; @@ -427,14 +478,28 @@ int main(int argc, char *const argv[]) fprintf(stdout, "\n"); for (i = 0; i < (t.num_sectors); ++i) { - if(t.sectors[i].foundKeyA) + if(t.sectors[i].foundKeyA){ fprintf(stdout, "Sector %02d - Found Key A: %012llx ", i, bytes_to_num(t.sectors[i].KeyA, sizeof(t.sectors[i].KeyA))); - else + memcpy(&knownKey, t.sectors[i].KeyA, 6); + knownKeyLetter = 'A'; + knownSector = i; + } + else{ fprintf(stdout, "Sector %02d - Unknown Key A ", i); - if(t.sectors[i].foundKeyB) + unknownSector = i; + unknownKeyLetter = 'A'; + } + if(t.sectors[i].foundKeyB){ fprintf(stdout, "Found Key B: %012llx\n", bytes_to_num(t.sectors[i].KeyB, sizeof(t.sectors[i].KeyB))); - else + knownKeyLetter = 'B'; + memcpy(&knownKey, t.sectors[i].KeyB, 6); + knownSector = i; + } + else{ fprintf(stdout, "Unknown Key B\n"); + unknownSector = i; + unknownKeyLetter = 'B'; + } } fflush(stdout); @@ -510,7 +575,18 @@ int main(int argc, char *const argv[]) // Max probes for auth for each sector for (k = 0; k < probes; ++k) { // Try to authenticate to exploit sector and determine distances (filling denonce.distances) - mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode + int authresult = mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode + if(authresult == -99999){ + //for now we return the last sector that is unknown + nfc_close(r.pdi); + nfc_exit(context); + if(pfKey) { + fprintf(pfKey, "%012llx;%d;%c;%d;%c", knownKey, knownSector, knownKeyLetter, unknownSector, unknownKeyLetter); + fclose(pfKey); + } + return 9; + } + printf("Sector: %d, type %c, probe %d, distance %d ", j, (dumpKeysA ? 'A' : 'B'), k, d.median); // Configure device to the previous state mf_configure(r.pdi); @@ -726,6 +802,7 @@ void usage(FILE *stream, int errno) fprintf(stream, " T nonce tolerance half-range, instead of default of 20\n (i.e., 40 for the total range, in both directions)\n"); // fprintf(stream, " s specify the list of sectors to crack, for example -s 0,1,3,5\n"); fprintf(stream, " O file in which the card contents will be written (REQUIRED)\n"); + fprintf(stream, " D file in which partial card info will be written in case PRNG is not vulnerable\n"); fprintf(stream, "\n"); fprintf(stream, "Example: mfoc -O mycard.mfd\n"); fprintf(stream, "Example: mfoc -k ffffeeeedddd -O mycard.mfd\n"); @@ -1018,7 +1095,21 @@ int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d // Save the determined nonces distance d->distances[m] = nonce_distance(Nt, NtLast); - // fprintf(stdout, "distance: %05d\n", d->distances[m]); + int checkForValidPRNG = dist_nt(Nt, NtLast); + //printf("NT distance: %d\n", checkForValidPRNG); + + // if no distance between, then we are in sync. + if (checkForValidPRNG == 0) { + printf("NT Distance is zero..........\n"); + } else { + if (checkForValidPRNG == -99999) { // invalid nonce received + ++unexpected_random; + if (unexpected_random > 4) { + printf("PRNG is not vulnerable to nested attack\n"); + return -99999; + } + } + } // Again, prepare and send {At} for (i = 0; i < 4; i++) { From 9d9f01fba4ee145bc873a85898b74041ca6d8831 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 17 Feb 2017 16:14:01 +0100 Subject: [PATCH 6/6] Simplify PRNG validation --- src/crapto1.c | 6 ++++++ src/crapto1.h | 2 ++ src/mfoc.c | 55 +++++---------------------------------------------- 3 files changed, 13 insertions(+), 50 deletions(-) diff --git a/src/crapto1.c b/src/crapto1.c index 94995f3..e94840d 100644 --- a/src/crapto1.c +++ b/src/crapto1.c @@ -374,6 +374,12 @@ int nonce_distance(uint32_t from, uint32_t to) } return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; } +bool validate_prng_nonce(uint32_t nonce) +{ + // init prng table: + nonce_distance(nonce, nonce); + return ((65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535) == 16; +} static uint32_t fastfwd[2][8] = { diff --git a/src/crapto1.h b/src/crapto1.h index 4b8c90c..525a02f 100644 --- a/src/crapto1.h +++ b/src/crapto1.h @@ -20,6 +20,7 @@ #ifndef CRAPTO1_INCLUDED #define CRAPTO1_INCLUDED #include +#include #ifdef __cplusplus extern "C" { #endif @@ -39,6 +40,7 @@ extern "C" { void lfsr_rollback(struct Crypto1State *s, uint32_t in, int fb); uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb); int nonce_distance(uint32_t from, uint32_t to); + bool validate_prng_nonce(uint32_t nonce); #define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\ uint32_t __n = 0,__M = 0, N = 0;\ int __i;\ diff --git a/src/mfoc.c b/src/mfoc.c index 052014c..6e3538b 100644 --- a/src/mfoc.c +++ b/src/mfoc.c @@ -72,41 +72,6 @@ uint32_t unknownSector = 0; char unknownKeyLetter = 'A'; uint32_t unexpected_random = 0; -// Determine the distance between two nonces. -// Assume that the difference is small, but we don't know which is first. -// Therefore try in alternating directions. -int32_t dist_nt(uint32_t nt1, uint32_t nt2) { - - if (nt1 == nt2) return 0; - - uint16_t i; - uint32_t nttmp1 = nt1; - uint32_t nttmp2 = nt2; - - for (i = 1; i < (32768/8); ++i) { - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -i; - - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+1; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+1); - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+2; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+2); - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+3; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+3); - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+4; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+4); - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+5; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+5); - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+6; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+6); - nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i+7; - nttmp2 = prng_successor(nttmp2, 1); if (nttmp2 == nt1) return -(i+7); - } - // either nt1 or nt2 are invalid nonces - return(-99999); -} - - int main(int argc, char *const argv[]) { int ch, i, k, n, j, m; @@ -1093,23 +1058,13 @@ int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d } NtLast = bytes_to_num(Rx, 4) ^ crypto1_word(pcs, bytes_to_num(Rx, 4) ^ t.authuid, 1); + // Make sure the card is using the known PRNG + if (! validate_prng_nonce(NtLast)) { + printf("Card is not vulnerable to nested attack\n"); + return -99999; + } // Save the determined nonces distance d->distances[m] = nonce_distance(Nt, NtLast); - int checkForValidPRNG = dist_nt(Nt, NtLast); - //printf("NT distance: %d\n", checkForValidPRNG); - - // if no distance between, then we are in sync. - if (checkForValidPRNG == 0) { - printf("NT Distance is zero..........\n"); - } else { - if (checkForValidPRNG == -99999) { // invalid nonce received - ++unexpected_random; - if (unexpected_random > 4) { - printf("PRNG is not vulnerable to nested attack\n"); - return -99999; - } - } - } // Again, prepare and send {At} for (i = 0; i < 4; i++) {