// MIFARE bruteforce tool // It's Multi threaded and supports all DES/2TDEA/3TDEA/AES crypto authentication modes. // also supports six different LCG random generators. // as a consequece this tools also work on MIFARE Ultralight-C challenges // // // Based upon the bruteforcer from X41 D-Sec Gmbh // // Copyright Iceman 2022 // // 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 3 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, see . // #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include //#include #include "util_posix.h" #include "randoms.h" #include "aes-ni.h" #if defined(__APPLE__) || defined(__MACH__) #else #include "detectaes.h" #endif #define AEND "\x1b[0m" #define _RED_(s) "\x1b[31m" s AEND #define _GREEN_(s) "\x1b[32m" s AEND #define _YELLOW_(s) "\x1b[33m" s AEND #define _CYAN_(s) "\x1b[36m" s AEND static generator_t generators[] = { {"Borland", make_key_borland_n}, {"Recipies", make_key_recipies_n}, {"GlibC", make_key_glibc_n}, {"AnsiC", make_key_ansic_n}, {"Turbo Pascal", make_key_turbopascal_n}, {"posix rand_r", make_key_posix_rand_r_n}, {"MS Visual/Quick C/C++", make_key_ms_rand_r_n}, {NULL, NULL} }; #define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0])) // a global mutex to prevent interlaced printing from different threads pthread_mutex_t print_lock; static int global_found = 0; static int thread_count = 2; typedef struct thread_args { int thread; int idx; uint8_t generator_idx; uint8_t algo; uint64_t starttime; uint64_t stoptime; uint8_t tag[16]; uint8_t rdr[32]; } targs; // source https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption#Decrypting_the_Message static void decrypt_aes(uint8_t ciphertext[], int ciphertext_len, uint8_t key[], uint8_t iv[], uint8_t plaintext[]) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); // EVP_CIPHER_CTX_set_padding(ctx, 0); int len = 0; EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); EVP_DecryptFinal_ex(ctx, plaintext + len, &len); EVP_CIPHER_CTX_free(ctx); } static void decrypt_3kdes(uint8_t ciphertext[], int ciphertext_len, uint8_t key[], uint8_t iv[], uint8_t plaintext[]) { EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, key, iv); EVP_CIPHER_CTX_set_padding(ctx, 0); int len = 0; EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); EVP_DecryptFinal_ex(ctx, plaintext + len, &len); EVP_CIPHER_CTX_free(ctx); } static void decrypt_2kdes(uint8_t ciphertext[], int ciphertext_len, uint8_t key[], uint8_t iv[], uint8_t plaintext[]) { EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_des_ede_cbc(), NULL, key, iv); EVP_CIPHER_CTX_set_padding(ctx, 0); int len = 0; EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); EVP_DecryptFinal_ex(ctx, plaintext + len, &len); EVP_CIPHER_CTX_free(ctx); } static void decrypt_des(uint8_t ciphertext[], int ciphertext_len, uint8_t key[], uint8_t iv[], uint8_t plaintext[]) { EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_des_cbc(), NULL, key, iv); EVP_CIPHER_CTX_set_padding(ctx, 0); int len = 0; EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); EVP_DecryptFinal_ex(ctx, plaintext + len, &len); EVP_CIPHER_CTX_free(ctx); } static int hexstr_to_byte_array(char hexstr[], uint8_t bytes[], size_t byte_len) { size_t hexstr_len = strlen(hexstr); if (hexstr_len % 16) { return 1; } if (byte_len < (hexstr_len / 2)) { return 2; } char *pos = &hexstr[0]; for (size_t count = 0; *pos != 0; count++) { sscanf(pos, "%2hhx", &bytes[count]); pos += 2; } return 0; } static void print_hex(const uint8_t *data, const size_t len) { if (data == NULL || len == 0) return; for (size_t i = 0; i < len; i++) { printf("%02X", data[i]); } printf("\n"); } static void print_time(uint64_t at) { time_t t = at; struct tm lt; #if defined(_WIN32) (void)localtime_s(<, &t); #else (void)localtime_r(&t, <); #endif char res[70]; #if defined(__MINGW32__) || defined(__MINGW64__) strftime(res, sizeof(res), "('%Y-%m-%d %H:%M:%S')", <); #else strftime(res, sizeof(res), "%s ('%Y-%m-%d %H:%M:%S')", <); #endif printf("%s\n", res); } static void *brute_thread(void *arguments) { //const bool support_aesni = platform_aes_hw_available(); struct thread_args *args = (struct thread_args *) arguments; uint64_t starttime = args->starttime; uint64_t stoptime = args->stoptime; uint8_t local_algo = args->algo; uint8_t gidx = args->generator_idx; uint8_t local_tag[16]; uint8_t local_rdr[32]; uint8_t keylen = 16; if (local_algo == 0) { memcpy(local_tag, args->tag, 8); memcpy(local_rdr, args->rdr, 16); keylen = 8; } else if (local_algo == 1) { memcpy(local_tag, args->tag, 8); memcpy(local_rdr, args->rdr, 16); keylen = 16; } else if (local_algo == 2) { memcpy(local_tag, args->tag, 16); memcpy(local_rdr, args->rdr, 32); keylen = 24; } else if (local_algo == 3) { memcpy(local_tag, args->tag, 16); memcpy(local_rdr, args->rdr, 32); keylen = 16; } for (uint64_t i = starttime + args->idx; i < stoptime; i += thread_count) { if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { break; } uint8_t key[keylen]; generators[gidx].Parse(i, key, keylen); //make_key_borland_n(i, key, keylen); uint8_t iv[keylen << 1]; memset(iv, 0, sizeof(iv)); uint8_t dec_tag[16] = {0x00}; uint8_t dec_rdr[32] = {0x00}; if (local_algo == 0) { decrypt_des(local_tag, 8, key, iv, dec_tag); decrypt_des(local_rdr, 16, key, local_tag, dec_rdr); // check rol byte first if (dec_tag[0] != dec_rdr[15]) continue; // compare rest if (dec_tag[1] != dec_rdr[8]) continue; if (dec_tag[2] != dec_rdr[9]) continue; if (dec_tag[3] != dec_rdr[10]) continue; if (dec_tag[4] != dec_rdr[11]) continue; if (dec_tag[5] != dec_rdr[12]) continue; if (dec_tag[6] != dec_rdr[13]) continue; if (dec_tag[7] != dec_rdr[14]) continue; } else if (local_algo == 1) { decrypt_2kdes(local_tag, 8, key, iv, dec_tag); decrypt_2kdes(local_rdr, 16, key, local_tag, dec_rdr); // check rol byte first if (dec_tag[0] != dec_rdr[15]) continue; // compare rest if (dec_tag[1] != dec_rdr[8]) continue; if (dec_tag[2] != dec_rdr[9]) continue; if (dec_tag[3] != dec_rdr[10]) continue; if (dec_tag[4] != dec_rdr[11]) continue; if (dec_tag[5] != dec_rdr[12]) continue; if (dec_tag[6] != dec_rdr[13]) continue; if (dec_tag[7] != dec_rdr[14]) continue; } else if (local_algo == 2) { decrypt_3kdes(local_tag, 16, key, iv, dec_tag); decrypt_3kdes(local_rdr, 32, key, local_tag, dec_rdr); // check rol byte first if (dec_tag[0] != dec_rdr[31]) continue; // compare rest if (dec_tag[1] != dec_rdr[16]) continue; if (dec_tag[2] != dec_rdr[17]) continue; if (dec_tag[3] != dec_rdr[18]) continue; if (dec_tag[4] != dec_rdr[19]) continue; if (dec_tag[5] != dec_rdr[20]) continue; if (dec_tag[6] != dec_rdr[21]) continue; if (dec_tag[7] != dec_rdr[22]) continue; if (dec_tag[8] != dec_rdr[23]) continue; if (dec_tag[9] != dec_rdr[24]) continue; if (dec_tag[10] != dec_rdr[25]) continue; if (dec_tag[11] != dec_rdr[26]) continue; if (dec_tag[12] != dec_rdr[27]) continue; if (dec_tag[13] != dec_rdr[28]) continue; if (dec_tag[14] != dec_rdr[29]) continue; if (dec_tag[15] != dec_rdr[30]) continue; } else if (local_algo == 3) { decrypt_aes(local_tag, 16, key, iv, dec_tag); decrypt_aes(local_rdr, 32, key, local_tag, dec_rdr); // check rol byte first if (dec_tag[0] != dec_rdr[31]) continue; // compare rest if (dec_tag[1] != dec_rdr[16]) continue; if (dec_tag[2] != dec_rdr[17]) continue; if (dec_tag[3] != dec_rdr[18]) continue; if (dec_tag[4] != dec_rdr[19]) continue; if (dec_tag[5] != dec_rdr[20]) continue; if (dec_tag[6] != dec_rdr[21]) continue; if (dec_tag[7] != dec_rdr[22]) continue; if (dec_tag[8] != dec_rdr[23]) continue; if (dec_tag[9] != dec_rdr[24]) continue; if (dec_tag[10] != dec_rdr[25]) continue; if (dec_tag[11] != dec_rdr[26]) continue; if (dec_tag[12] != dec_rdr[27]) continue; if (dec_tag[13] != dec_rdr[28]) continue; if (dec_tag[14] != dec_rdr[29]) continue; if (dec_tag[15] != dec_rdr[30]) continue; } __sync_fetch_and_add(&global_found, 1); // lock this section to avoid interlacing prints from different threats pthread_mutex_lock(&print_lock); printf("Found timestamp........ "); print_time(i); printf("Key.................... \x1b[32m"); print_hex(key, keylen); printf(AEND); pthread_mutex_unlock(&print_lock); break; } free(args); return NULL; } static int usage(const char *s) { printf("\n"); printf(_CYAN_("Multi Brute tool\n")); printf("Works on authentication challenges from MIFARE DESfire, MIFARE UL-C.\n"); printf("If the key was generated by taking the Unixstamp as seed to a LCG random generator this software might find it.\n"); printf("This version is multi-threaded, multi-crypto support and multi LCG generator support.\n"); printf("\n"); printf(_CYAN_("syntax") "\n"); printf(" %s <16 byte tag challenge> <32 byte reader response challenge>\n\n", s); printf(" crypt algo - \n"); printf(" generator - <0-5>\n"); printf("\n"); printf(_CYAN_("samples") "\n"); printf(" %s DES 0 1599999999 118565f6e5e6c839 d570fd1578079e6b22aaa187b99f0a2a\n", s); printf(" %s 2TDEA 0 1599999999 02bdc73fd33cc07d 0e2281d59686bda6a6c5ad218dbfaa8c\n", s); printf(" %s 3TDEA 0 1599999999 1fe1f0330e9da5407cd2bc9294e56a7e 920037b5e02872b2fd9a070eade2b172ddc0fe6b10e5e55dd32cebdcc94747b4 \n", s); printf(" %s AES 0 1599999999 bb6aea729414a5b1eff7b16328ce37fd 82f5f498dbc29f7570102397a2e5ef2b6dc14a864f665b3c54d11765af81e95c\n", s); printf("\n"); return 1; } int main(int argc, char *argv[]) { if (argc != 6) { return usage(argv[0]); } char *algostr = argv[1]; if (strlen(algostr) > 5 || strlen(algostr) < 3) { printf("No valid crypto algo\n"); return 1; } int8_t algo = -1; if (strcasecmp(algostr, "des") == 0) { algo = 0; } else if (strcasecmp(algostr, "2tdea") == 0) { algo = 1; } else if (strcasecmp(algostr, "3tdea") == 0) { algo = 2; } else if (strcasecmp(algostr, "aes") == 0) { algo = 3; } if (algo == -1) { printf("No valid crypto algo\n"); return 1; } uint8_t g_idx = atoi(argv[2]); // -2 (zero index and last item is NULL); if (g_idx > ARRAYLEN(generators) - 2) { printf("generator index is out-of-range\n"); return 1; } uint64_t start_time = 0; sscanf(argv[3], "%"PRIu64, &start_time); printf("Crypto algo............ " _GREEN_("%s") "\n", algostr); printf("LCR Random generator... " _GREEN_("%s") "\n", generators[g_idx].Name); #if defined(__APPLE__) || defined(__MACH__) #else bool support_aesni = platform_aes_hw_available(); printf("AES-NI detected........ " _GREEN_("%s") "\n", (support_aesni) ? "yes" : "no"); #endif printf("Starting timestamp..... "); print_time(start_time); uint8_t tag_challenge[16] = {0x00}; uint8_t rdr_resp_challenge[32] = {0x00}; if (algo == 0) { if (hexstr_to_byte_array(argv[4], tag_challenge, 8)) return 2; if (hexstr_to_byte_array(argv[5], rdr_resp_challenge, 16)) return 3; printf("Tag Challenge.......... "); print_hex(tag_challenge, 8); printf("Rdr Resp & Challenge... "); print_hex(rdr_resp_challenge, 16); } else if (algo == 1) { if (hexstr_to_byte_array(argv[4], tag_challenge, 8)) return 2; if (hexstr_to_byte_array(argv[5], rdr_resp_challenge, 16)) return 3; printf("Tag Challenge.......... "); print_hex(tag_challenge, 8); printf("Rdr Resp & Challenge... "); print_hex(rdr_resp_challenge, 16); } else if (algo == 2) { if (hexstr_to_byte_array(argv[4], tag_challenge, 16)) return 2; if (hexstr_to_byte_array(argv[5], rdr_resp_challenge, 32)) return 3; printf("Tag Challenge.......... "); print_hex(tag_challenge, 16); printf("Rdr Resp & Challenge... "); print_hex(rdr_resp_challenge, 32); } else if (algo == 3) { if (hexstr_to_byte_array(argv[4], tag_challenge, 16)) return 2; if (hexstr_to_byte_array(argv[5], rdr_resp_challenge, 32)) return 3; printf("Tag Challenge.......... "); print_hex(tag_challenge, 16); printf("Rdr Resp & Challenge... "); print_hex(rdr_resp_challenge, 32); } uint64_t t1 = msclock(); #if !defined(_WIN32) || !defined(__WIN32__) thread_count = sysconf(_SC_NPROCESSORS_CONF); if (thread_count < 2) thread_count = 2; #endif /* _WIN32 */ printf("\nBruteforce using " _YELLOW_("%d") " threads\n", thread_count); pthread_t threads[thread_count]; void *res; // create a mutex to avoid interlacing print commands from our different threads pthread_mutex_init(&print_lock, NULL); // threads uint64_t stop_time = time(NULL); for (int i = 0; i < thread_count; ++i) { struct thread_args *a = calloc(1, sizeof(struct thread_args)); a->thread = i; a->idx = i; a->generator_idx = g_idx; a->algo = (uint8_t)algo; a->starttime = start_time; a->stoptime = stop_time; if (algo == 0) { memcpy(a->tag, tag_challenge, 8); memcpy(a->rdr, rdr_resp_challenge, 16); } else if (algo == 1) { memcpy(a->tag, tag_challenge, 8); memcpy(a->rdr, rdr_resp_challenge, 16); } else if (algo == 2) { memcpy(a->tag, tag_challenge, 16); memcpy(a->rdr, rdr_resp_challenge, 32); } else if (algo == 3) { memcpy(a->tag, tag_challenge, 16); memcpy(a->rdr, rdr_resp_challenge, 32); } pthread_create(&threads[i], NULL, brute_thread, (void *)a); } // wait for threads to terminate: for (int i = 0; i < thread_count; ++i) { pthread_join(threads[i], &res); free(res); } if (global_found == false) { printf("\n" _RED_("!!!") " failed to find a key\n\n"); } t1 = msclock() - t1; if (t1 > 0) { printf("Execution time " _YELLOW_("%.2f") " sec\n", (float)t1 / 1000.0); } // clean up mutex pthread_mutex_destroy(&print_lock); return 0; }