diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f2103ac..21c43c33c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Fixed truncated FPGA upload due to incorrect integer size variable (@d18c7db) + - Changed `usart btfactory` - handles the new BT board with version "BT SPP V3.0" (@iceman1001) + - Changed `hf mf eview --sk` - now can extract keys and save to file (@iceman1001) + - Changed `hf mf view --sk` - now can extract keys and save to file (@iceman1001) - Changed `hf mf sim` - reduce 6ms threshold to 4ms for reset to idle #1974 (@net147) - Rebuilt the Spartan-2 `fpga_*.bit` files to include the `hi_iso14443a.v` update (@d18c7db) - Added minor orphaned change from `hi_iso14443a.v` in `fpga-xc3s100e` to `hi_iso14443a.v` in `fpga-xc2s30` (@d18c7db) @@ -22,7 +26,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty) - Added iClass credit key to default iClass key table and reorganized key order (@GuruSteve) - Changed `hf mf value` - ability to use transfer on different block (@AloneLiberty) - - Change `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001) + - Changed `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001) - Added `hf mf gdmsetcfg` - Supprt Gen4 GDM write configuration block (@iceman1001) - Added `hf mf gdmcfg` - Support Gen4 GDM read configuration block (@iceman1001) - Changed magic note to include a section about GDM tags (@iceman1001) @@ -62,13 +66,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf legic info` command for other sources (@0xdeb) - Added `hf legic einfo` - views emulator menory (@0xdeb) - Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb) - - Now `script run hf_mf_ultimatecard.lua -u` supports 10bytes UID (@alejandro12120) - - Update documentation for installation on macOS with MacPorts (@linuxgemini) + - Changed `script run hf_mf_ultimatecard.lua -u` to support 10bytes UID (@alejandro12120) + - Updated documentation for installation on macOS with MacPorts (@linuxgemini) - Added possible Paxton id to hitag2 tag info output - Changed `hf mf sim` - reduce 50ms threshold to 6ms for reset to idle #1974 (@net147) - Update `amiibo_tools.lua` with new identifiers and create a python script `update_amiibo_tools_lua.py` to automate the process in the future. (@CorySolovewicz) - Added `lf paradox sim --fc --cn` - Simulates Paradox fob from facility code and card number (jerji) + ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) - Fixed some coverity fixes (@iceman1001) diff --git a/armsrc/Makefile b/armsrc/Makefile index 5024aa1de..5203b0292 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -77,7 +77,7 @@ else endif ifneq (,$(findstring WITH_EM4x50,$(APP_CFLAGS))) - SRC_EM4x50 = em4x50.c + SRC_EM4x50 = em4x50.c bruteforce.c else SRC_EM4x50 = endif diff --git a/armsrc/appmain.c b/armsrc/appmain.c index f68739da2..184307042 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -2014,11 +2014,16 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t waittime; } PACKED; struct p *payload = (struct p *) &packet->data.asBytes; + uint16_t available; uint16_t pre_available = 0; uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); uint32_t wait = payload->waittime; + + StartTicks(); + uint32_t ti = GetTickCount(); + while (true) { WaitMS(50); available = usart_rxdata_available(); @@ -2039,6 +2044,8 @@ static void PacketReceived(PacketCommandNG *packet) { } else { reply_ng(CMD_USART_RX, PM3_ENODATA, NULL, 0); } + + StopTicks(); BigBuf_free(); LED_B_OFF(); break; @@ -2051,11 +2058,16 @@ static void PacketReceived(PacketCommandNG *packet) { } PACKED; struct p *payload = (struct p *) &packet->data.asBytes; usart_writebuffer_sync(payload->data, packet->length - sizeof(payload)); + uint16_t available; uint16_t pre_available = 0; uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); uint32_t wait = payload->waittime; + + StartTicks(); + uint32_t ti = GetTickCount(); + while (true) { WaitMS(50); available = usart_rxdata_available(); @@ -2070,12 +2082,15 @@ static void PacketReceived(PacketCommandNG *packet) { if (GetTickCountDelta(ti) > wait) break; } + if (available > 0) { uint16_t len = usart_read_ng(dest, available); reply_ng(CMD_USART_TXRX, PM3_SUCCESS, dest, len); } else { reply_ng(CMD_USART_TXRX, PM3_ENODATA, NULL, 0); } + + StopTicks(); BigBuf_free(); LED_B_OFF(); break; @@ -2718,9 +2733,6 @@ void __attribute__((noreturn)) AppMain(void) { } #endif -#ifdef WITH_FPC_USART - usart_init(USART_BAUD_RATE, USART_PARITY); -#endif #ifdef WITH_FLASH // If flash is not present, BUSY_TIMEOUT kicks in, let's do it after USB @@ -2733,6 +2745,10 @@ void __attribute__((noreturn)) AppMain(void) { rdv40_spiffs_check(); #endif +#ifdef WITH_FPC_USART + usart_init(USART_BAUD_RATE, USART_PARITY); +#endif + // This is made as late as possible to ensure enumeration without timeout // against device such as http://www.hobbytronics.co.uk/usb-host-board-v2 // In other words, keep the interval between usb_enable() and the main loop as short as possible. diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 86d44cd9a..d43a6a15f 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -27,6 +27,7 @@ #include "BigBuf.h" #include "spiffs.h" #include "appmain.h" // tear +#include "bruteforce.h" // Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) // TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz @@ -632,12 +633,21 @@ static int login(uint32_t password) { return PM3_EFAILED; } -// searching for password in given range -static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { +// searching for password using chosen bruteforce algorithm +static bool brute(em4x50_data_t *etd, uint32_t *pwd) { + + generator_context_t ctx; bool pwd_found = false; + int generator_ret = 0; int cnt = 0; - for (*pwd = start; *pwd <= stop; (*pwd)++) { + bf_generator_init(&ctx, etd->bruteforce_mode); + + if (etd->bruteforce_mode == BRUTEFORCE_MODE_CHARSET) + bf_generator_set_charset(&ctx, etd->bruteforce_charset); + + while ((generator_ret = bf_generate32(&ctx)) == GENERATOR_NEXT) { + *pwd = ctx.current_key32; WDT_HIT(); @@ -702,7 +712,7 @@ void em4x50_login(uint32_t *password, bool ledcontrol) { reply_ng(CMD_LF_EM4X50_LOGIN, status, NULL, 0); } -// envoke password search +// invoke password search void em4x50_brute(em4x50_data_t *etd, bool ledcontrol) { em4x50_setup_read(); @@ -714,7 +724,7 @@ void em4x50_brute(em4x50_data_t *etd, bool ledcontrol) { LED_C_OFF(); LED_D_ON(); } - bsuccess = brute(etd->password1, etd->password2, &pwd); + bsuccess = brute(etd, &pwd); } if (ledcontrol) LEDsoff(); diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 802c77558..6f8dd7a0b 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -393,7 +393,7 @@ static int bitparse_find_section(int bitstream_version, char section_name, uint3 while (numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { char current_name = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); numbytes++; - uint16_t current_length = 0; + uint32_t current_length = 0; if (current_name < 'a' || current_name > 'e') { /* Strange section name, abort */ break; @@ -423,7 +423,7 @@ static int bitparse_find_section(int bitstream_version, char section_name, uint3 break; } - for (uint16_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { + for (uint32_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); numbytes++; } diff --git a/armsrc/usart.c b/armsrc/usart.c index f92e9c701..915d4a445 100644 --- a/armsrc/usart.c +++ b/armsrc/usart.c @@ -46,8 +46,8 @@ void usart_close(void) { } */ -static uint8_t us_inbuf1[USART_BUFFLEN]; -static uint8_t us_inbuf2[USART_BUFFLEN]; +static uint8_t us_in_a[USART_BUFFLEN]; +static uint8_t us_in_b[USART_BUFFLEN]; static uint8_t *usart_cur_inbuf = NULL; static uint16_t usart_cur_inbuf_off = 0; static uint8_t us_rxfifo[USART_FIFOLEN]; @@ -56,7 +56,9 @@ static size_t us_rxfifo_high = 0; static void usart_fill_rxfifo(void) { - uint16_t rxfifo_free ; + + uint16_t rxfifo_free = 0; + if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used if (us_rxfifo_low > us_rxfifo_high) @@ -79,19 +81,22 @@ static void usart_fill_rxfifo(void) { pUS1->US_RNCR = USART_BUFFLEN; // Swap current buff - if (usart_cur_inbuf == us_inbuf1) - usart_cur_inbuf = us_inbuf2; + if (usart_cur_inbuf == us_in_a) + usart_cur_inbuf = us_in_b; else - usart_cur_inbuf = us_inbuf1; + usart_cur_inbuf = us_in_a; usart_cur_inbuf_off = 0; } else { // Take only what we have room for available = rxfifo_free; for (uint16_t i = 0; i < available; i++) { + us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i]; - if (us_rxfifo_high == sizeof(us_rxfifo)) + + if (us_rxfifo_high == sizeof(us_rxfifo)) { us_rxfifo_high = 0; + } } usart_cur_inbuf_off += available; return; @@ -101,18 +106,20 @@ static void usart_fill_rxfifo(void) { if (pUS1->US_RCR < USART_BUFFLEN - usart_cur_inbuf_off) { // Current buffer partially filled if (us_rxfifo_low > us_rxfifo_high) - rxfifo_free = us_rxfifo_low - us_rxfifo_high; + rxfifo_free = (us_rxfifo_low - us_rxfifo_high); else - rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low; + rxfifo_free = (sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low); - uint16_t available = USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off; + uint16_t available = (USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off); if (available > rxfifo_free) available = rxfifo_free; + for (uint16_t i = 0; i < available; i++) { us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i]; - if (us_rxfifo_high == sizeof(us_rxfifo)) + if (us_rxfifo_high == sizeof(us_rxfifo)) { us_rxfifo_high = 0; + } } usart_cur_inbuf_off += available; } @@ -121,9 +128,9 @@ static void usart_fill_rxfifo(void) { uint16_t usart_rxdata_available(void) { usart_fill_rxfifo(); if (us_rxfifo_low <= us_rxfifo_high) - return us_rxfifo_high - us_rxfifo_low; + return (us_rxfifo_high - us_rxfifo_low); else - return sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high; + return (sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high); } uint32_t usart_read_ng(uint8_t *data, size_t len) { @@ -143,9 +150,10 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { uint32_t maxtry = 10 * (3000000 / USART_BAUD_RATE) + tryconstant; while (len) { - uint32_t available = usart_rxdata_available(); + uint32_t available = usart_rxdata_available(); uint32_t packetSize = MIN(available, len); + if (available > 0) { // Dbprintf_usb("Dbg USART ask %d bytes, available %d bytes, packetsize %d bytes", len, available, packetSize); // highest_observed_try = MAX(highest_observed_try, try); @@ -153,8 +161,9 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } len -= packetSize; while (packetSize--) { - if (us_rxfifo_low == sizeof(us_rxfifo)) + if (us_rxfifo_low == sizeof(us_rxfifo)) { us_rxfifo_low = 0; + } data[bytes_rcv++] = us_rxfifo[us_rxfifo_low++]; } if (try++ == maxtry) { @@ -183,10 +192,13 @@ int usart_writebuffer_sync(uint8_t *data, size_t len) { void usart_init(uint32_t baudrate, uint8_t parity) { - if (baudrate != 0) + if (baudrate != 0) { g_usart_baudrate = baudrate; - if ((parity == 'N') || (parity == 'O') || (parity == 'E')) + } + + if ((parity == 'N') || (parity == 'O') || (parity == 'E')) { g_usart_parity = parity; + } // For a nice detailed sample, interrupt driven but still relevant. // See https://www.sparkfun.com/datasheets/DevTools/SAM7/at91sam7%20serial%20communications.pdf @@ -262,11 +274,11 @@ void usart_init(uint32_t baudrate, uint8_t parity) { pUS1->US_TCR = 0; pUS1->US_TNPR = (uint32_t)0; pUS1->US_TNCR = 0; - pUS1->US_RPR = (uint32_t)us_inbuf1; + pUS1->US_RPR = (uint32_t)us_in_a; pUS1->US_RCR = USART_BUFFLEN; - usart_cur_inbuf = us_inbuf1; + usart_cur_inbuf = us_in_a; usart_cur_inbuf_off = 0; - pUS1->US_RNPR = (uint32_t)us_inbuf2; + pUS1->US_RNPR = (uint32_t)us_in_b; pUS1->US_RNCR = USART_BUFFLEN; // Initialize our fifo diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index ee1ddcc25..3fd255997 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -586,6 +586,17 @@ if (MINGW) set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}") + # GCC 10 has issues with false positives on stringop-overflow, + # let's disable them for now (cf https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92955, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94335) + # beware these flags didn't exist for GCC < 7 + if(CMAKE_COMPILER_IS_GNUCXX) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_GREATER 10.0 OR GCC_VERSION VERSION_EQUAL 10.0) + set(CMAKE_C_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") + endif() + endif(CMAKE_COMPILER_IS_GNUCXX) + # link Winsock2 set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK}) endif (MINGW) diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index b5b36595c..7766b28d3 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -182,7 +182,7 @@ crack_states_thread(void *x) { } else { if (!thread_arg->silent) { char progress_text[80]; - snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%%\t", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states)); + snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%% ", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states)); float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested / 2; hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, remaining_bruteforce, 5000); } diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 7ae6ead7f..63598f78b 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -18,6 +18,19 @@ A5A4A3A2A1A0 # MAD access key B 89ECA97F8C2A # +# Mifare 1k EV1 (S50) hidden blocks, Signature data +# 16 A +5C8FF9990DA2 +# +# 17 A +75CCB59C9BED +# +# 16 B +D01AFEEB890A +# +# 17 B +4B791BEA7BCC +# # B0B1B2B3B4B5 C0C1C2C3C4C5 @@ -111,7 +124,7 @@ F1D83F964314 # Access control system 605F5E5D5C5B # -#NSP Global keys A and B (uk housing access control) +# NSP Global keys A and B (uk housing access control) 199404281970 199404281998 # @@ -263,16 +276,25 @@ E3429281EFC1 460722122510 # # 3dprinter -# EPI Envisionte# 3dprinter +# EPI Envisionte AAFB06045877 # # gym +# # Fysiken A 3E65E4FB65B3 # # Fysiken B 25094DF6F148 # +# +# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ +# https://mattionline.de/milazycracker/ +# gym wistband A, same as Fysiken A +# gym wistband B +81CC25EBBB6A +195DC63DB3A3 +# # CleverFit A05DBD98E0FC # @@ -280,6 +302,10 @@ A05DBD98E0FC AA4DDA458EBB EAB8066C7479 # +# Nordic Wellness A, same as Fysiken A +# Nordic Wellness B +E5519E1CC92B +# # Hotel KeyCard D3B595E9DD63 AFBECD121004 @@ -626,19 +652,6 @@ A8844B0BCA06 564C505F4D41 BA5B895DA162 # -# Vigik mystery Keys Mifare 1k EV1 (S50) -# 16 A -5C8FF9990DA2 -# -# 17 A -75CCB59C9BED -# -# 16 B -D01AFEEB890A -# -# 17 B -4B791BEA7BCC -# # BTCINO UNDETERMINED SPREAKD 0x01->0x13 key 021209197591 # diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index bdc122e58..2ac10f871 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -585,6 +585,18 @@ if (MINGW) set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}") + + # GCC 10 has issues with false positives on stringop-overflow, + # let's disable them for now (cf https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92955, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94335) + # beware these flags didn't exist for GCC < 7 + if(CMAKE_COMPILER_IS_GNUCXX) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_GREATER 10.0 OR GCC_VERSION VERSION_EQUAL 10.0) + set(CMAKE_C_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") + endif() + endif(CMAKE_COMPILER_IS_GNUCXX) + endif (MINGW) target_include_directories(pm3rrg_rdv4 PRIVATE diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 45df737e5..f5387ff45 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -367,5 +367,21 @@ "Name": "University of Ljubljana Student ID", "Description": "", "Type": "student" + }, + { + "AID": "27E178", + "Vendor": "Disney", + "Country": "US", + "Name": "Disney MagicBand", + "Description": "", + "Type": "payment system" + }, + { + "AID": "44434C", + "Vendor": "Disney", + "Country": "US", + "Name": "Disney MagicBand", + "Description": "AID found on MagicBand desfire cards", + "Type": "payment system" } ] diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index a1923d588..e84b3e597 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -531,7 +531,7 @@ static int CmdFlashMemSpiFFSView(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "SPIFFS file to view"), - arg_int0("c", "cols", "", "column breaks (def 32)"), + arg_int0("c", "cols", "", "column breaks (def 16)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -540,7 +540,7 @@ static int CmdFlashMemSpiFFSView(const char *Cmd) { char fn[32] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)fn, 32, &fnlen); - int breaks = arg_get_int_def(ctx, 2, 32); + int breaks = arg_get_int_def(ctx, 2, 16); CLIParserFree(ctx); uint8_t *dump = NULL; diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 17ad30184..3dee09d3b 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -308,7 +308,6 @@ static int mf_print_keys(uint16_t n, uint8_t *d) { } for (uint16_t i = 0; i < n; i++) { - if (mfIsSectorTrailer(i)) { e_sector[mfSectorNum(i)].foundKey[0] = 1; e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); @@ -321,6 +320,52 @@ static int mf_print_keys(uint16_t n, uint8_t *d) { return PM3_SUCCESS; } +// MFC dump , extract and save the keys to key file +static int mf_save_keys_from_arr(uint16_t n, uint8_t *d) { + uint8_t sectors = 0; + switch (n) { + case MIFARE_MINI_MAXBLOCK: + sectors = MIFARE_MINI_MAXSECTOR; + break; + case MIFARE_2K_MAXBLOCK: + sectors = MIFARE_2K_MAXSECTOR; + break; + case MIFARE_4K_MAXBLOCK: + sectors = MIFARE_4K_MAXSECTOR; + break; + case MIFARE_1K_MAXBLOCK: + default: + sectors = MIFARE_1K_MAXSECTOR; + break; + } + + uint16_t keysize = 2 * MIFARE_KEY_SIZE * sectors; + + uint8_t *keys = calloc(keysize, sizeof(uint8_t)); + if (keys == NULL) { + return PM3_EMALLOC; + } + + uint8_t sector = 0; + for (uint16_t i = 0; i < n; i++) { + if (mfIsSectorTrailer(i)) { + // key A offset in ST block + memcpy(keys + (MIFARE_KEY_SIZE * sector), d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); + + // key B offset in ST block + memcpy(keys + (MIFARE_KEY_SIZE * sectors) + (MIFARE_KEY_SIZE * sector), d + (i * MFBLOCK_SIZE) + 10, MIFARE_KEY_SIZE); + + sector++; + } + } + + char fn[FILE_PATH_SIZE] = {0}; + snprintf(fn, sizeof(fn), "hf-mf-%s-keys", sprint_hex_inrow(d, 4)); + saveFile(fn, ".bin", keys, keysize); + free(keys); + return PM3_SUCCESS; +} + /* static void mf_print_values(uint16_t n, uint8_t *d) { @@ -1211,52 +1256,41 @@ static int CmdHF14AMfRestore(const char *Cmd) { free(fptr); } - FILE *f; - if ((f = fopen(keyfilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyfilename); - return PM3_EFILE; - } - - // key arrays - uint8_t keyA[40][6]; - uint8_t keyB[40][6]; - - // read key file - size_t bytes_read; - for (uint8_t s = 0; s < sectors; s++) { - bytes_read = fread(keyA[s], 1, 6, f); - if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); - fclose(f); - return PM3_EFILE; + // + size_t alen = 0, blen = 0; + uint8_t *keyA, *keyB; + if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + if (keyA) { + free(keyA); } + return PM3_ESOFT; } - for (uint8_t s = 0; s < sectors; s++) { - bytes_read = fread(keyB[s], 1, 6, f); - if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); - fclose(f); - return PM3_EFILE; - } - } - fclose(f); + PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyfilename); // try reading card uid and create filename if (datafnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); - if (fptr == NULL) + if (fptr == NULL) { + if (keyA) { + free(keyA); + } + if (keyB) { + free(keyB); + } return PM3_ESOFT; - + } strcpy(datafilename, fptr); free(fptr); } // read dump file uint8_t *dump = NULL; - bytes_read = 0; + size_t bytes_read = 0; int res = pm3_load_dump(datafilename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); if (res != PM3_SUCCESS) { + free(keyA); + free(keyB); return res; } @@ -1281,19 +1315,10 @@ static int CmdHF14AMfRestore(const char *Cmd) { if (mfNumBlocksPerSector(s) - 1 == b) { if (use_keyfile_for_auth == false) { // replace KEY A - bldata[0] = (keyA[s][0]); - bldata[1] = (keyA[s][1]); - bldata[2] = (keyA[s][2]); - bldata[3] = (keyA[s][3]); - bldata[4] = (keyA[s][4]); - bldata[5] = (keyA[s][5]); + memcpy(bldata, keyA + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + // replace KEY B - bldata[10] = (keyB[s][0]); - bldata[11] = (keyB[s][1]); - bldata[12] = (keyB[s][2]); - bldata[13] = (keyB[s][3]); - bldata[14] = (keyB[s][4]); - bldata[15] = (keyB[s][5]); + memcpy(bldata + 10, keyB + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); } // ensure access right isn't messed up. @@ -1327,12 +1352,12 @@ static int CmdHF14AMfRestore(const char *Cmd) { for (int8_t kt = MF_KEY_B; kt > -1; kt--) { if (use_keyfile_for_auth) { if (kt == MF_KEY_A) - memcpy(wdata, keyA[s], 6); + memcpy(wdata, keyA + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); else - memcpy(wdata, keyB[s], 6); + memcpy(wdata, keyB + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); } else { // use default key to authenticate for the write command - memcpy(wdata, default_key, 6); + memcpy(wdata, default_key, MIFARE_KEY_SIZE); } PrintAndLogEx(INFO, "block %3d: %s", mfFirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); @@ -1359,6 +1384,8 @@ static int CmdHF14AMfRestore(const char *Cmd) { } // end loop S free(ref_dump); + free(keyA); + free(keyB); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -2155,26 +2182,27 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { uint64_t foundkey = 0; int16_t isOK = mfnestedhard(blockno, keytype, key, trg_blockno, trg_keytype, known_target_key ? trg_key : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); + switch (isOK) { + case PM3_ETIMEOUT : + PrintAndLogEx(ERR, "Error: No response from Proxmark3\n"); + break; + case PM3_EOPABORTED: + PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); + break; + case PM3_ESTATIC_NONCE: + PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + break; + case PM3_EFAILED: { + PrintAndLogEx(FAILED, "\nFailed to recover a key..."); + break; + } + default : + break; + } if ((tests == 0) && IfPm3Iso14443a()) { DropField(); } - - if (isOK) { - switch (isOK) { - case PM3_ETIMEOUT : - PrintAndLogEx(ERR, "Error: No response from Proxmark3\n"); - break; - case PM3_EOPABORTED: - PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); - break; - case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); - break; - default : - break; - } - } return isOK; } @@ -2389,14 +2417,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (is_ev1) { PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected"); - // Store the keys - e_sector[16].Key[MF_KEY_A] = bytes_to_num((uint8_t *)g_mifare_signature_key_a, sizeof(g_mifare_signature_key_a)); - e_sector[16].foundKey[MF_KEY_A] = 'D'; - - e_sector[17].Key[MF_KEY_A] = bytes_to_num((uint8_t *)g_mifare_signature_key_a, sizeof(g_mifare_signature_key_a)); - e_sector[17].foundKey[MF_KEY_A] = 'D'; - e_sector[17].Key[MF_KEY_B] = bytes_to_num((uint8_t *)g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b)); - e_sector[17].foundKey[MF_KEY_B] = 'D'; // use found key if not supplied if (known_key == false) { @@ -2431,7 +2451,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False"); PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); - PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex(key, sizeof(key))); + PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); if (has_staticnonce == NONCE_STATIC) PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC")); @@ -2458,10 +2478,10 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } if (mfCheckKeys(mfFirstBlockOfSector(sectorno), keytype, true, 1, key, &key64) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "target sector %3u key type %c -- using valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)", + PrintAndLogEx(INFO, "target sector %3u key type %c -- using valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', - sprint_hex(key, sizeof(key)) + sprint_hex_inrow(key, sizeof(key)) ); // Store the key for the nested / hardnested attack (if supplied by the user) @@ -2474,7 +2494,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector"_RED_("%3d") " key type "_RED_("%c") " key " _RED_("%s"), sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', - sprint_hex(key, sizeof(key)) + sprint_hex_inrow(key, sizeof(key)) ); PrintAndLogEx(WARNING, "falling back to dictionary"); } @@ -2482,31 +2502,34 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Check if the user supplied key is used by other sectors for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { - if (e_sector[i].foundKey[j] == 0) { - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num(key, 6); - e_sector[i].foundKey[j] = 'U'; - // If the user supplied secctor / keytype was wrong --> just be nice and correct it ;) - if (known_key == false) { - num_to_bytes(e_sector[i].Key[j], 6, key); - known_key = true; - sectorno = i; - keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - } - ++num_found_keys; + if (e_sector[i].foundKey[j]) { + continue; + } + + if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) { + e_sector[i].Key[j] = bytes_to_num(key, 6); + e_sector[i].foundKey[j] = 'U'; + + // If the user supplied secctor / keytype was wrong --> just be nice and correct it ;) + if (known_key == false) { + num_to_bytes(e_sector[i].Key[j], 6, key); + known_key = true; + sectorno = i; + keytype = j; + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", + i, + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(key, sizeof(key)) + ); + } else { + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + i, + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(key, sizeof(key)) + ); } + ++num_found_keys; } } } @@ -2551,7 +2574,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, "." NOLF); // Check all the sectors for (int i = 0; i < sector_cnt; i++) { - for (int j = 0; j < 2; j++) { + for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the key is known if (e_sector[i].foundKey[j] == 0) { for (uint32_t k = 0; k < key_cnt; k++) { @@ -2608,28 +2631,30 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Analyse the dictionary attack for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { - if (e_sector[i].foundKey[j] == 1) { - e_sector[i].foundKey[j] = 'D'; - num_to_bytes(e_sector[i].Key[j], 6, tmp_key); + if (e_sector[i].foundKey[j] != 1) { + continue; + } - // Store valid credentials for the nested / hardnested attack if none exist - if (known_key == false) { - num_to_bytes(e_sector[i].Key[j], 6, key); - known_key = true; - sectorno = i; - keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(tmp_key, sizeof(tmp_key)) - ); - } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(tmp_key, sizeof(tmp_key)) - ); - } + e_sector[i].foundKey[j] = 'D'; + num_to_bytes(e_sector[i].Key[j], 6, tmp_key); + + // Store valid credentials for the nested / hardnested attack if none exist + if (known_key == false) { + num_to_bytes(e_sector[i].Key[j], 6, key); + known_key = true; + sectorno = i; + keytype = j; + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", + i, + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) + ); + } else { + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + i, + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) + ); } } } @@ -2690,7 +2715,8 @@ noValidKeyFound: // Iterate over each sector and key(A/B) for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { - for (current_key_type_i = 0; current_key_type_i < 2; current_key_type_i++) { + + for (current_key_type_i = MF_KEY_A; current_key_type_i <= MF_KEY_B; current_key_type_i++) { // If the key is already known, just skip it if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { @@ -2863,7 +2889,7 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack foundkey = 0; isOK = mfnestedhard(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL); DropField(); - if (isOK) { + if (isOK != PM3_SUCCESS) { switch (isOK) { case PM3_ETIMEOUT: { PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); @@ -2886,6 +2912,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack PrintAndLogEx(NORMAL, ""); break; } + case PM3_EFAILED: { + PrintAndLogEx(FAILED, "\nFailed to recover a key..."); + continue; + } default: { break; } @@ -4394,6 +4424,7 @@ static int CmdHF14AMfEView(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "sk", "Save extracted keys to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -4402,6 +4433,7 @@ static int CmdHF14AMfEView(const char *Cmd) { bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); bool verbose = arg_get_lit(ctx, 5); + bool save_keys = arg_get_lit(ctx, 6); CLIParserFree(ctx); // validations @@ -4447,6 +4479,11 @@ static int CmdHF14AMfEView(const char *Cmd) { if (verbose) { mf_print_keys(block_cnt, dump); } + + if (save_keys) { + mf_save_keys_from_arr(block_cnt, dump); + } + free(dump); return PM3_SUCCESS; } @@ -5864,8 +5901,9 @@ int CmdHFMFNDEFRead(const char *Cmd) { CLIParserFree(ctx); uint16_t ndef_aid = NDEF_MFC_AID; - if (aidlen == 2) + if (aidlen == 2) { ndef_aid = (aid[0] << 8) + aid[1]; + } uint8_t ndefkey[6] = {0}; memcpy(ndefkey, g_mifare_ndef_key, 6); @@ -6079,39 +6117,27 @@ int CmdHFMFNDEFFormat(const char *Cmd) { DropField(); } + // load key file if exist if (strlen(keyFilename)) { - - FILE *f; - if ((f = fopen(keyFilename, "rb")) == NULL) { - // PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); + // + size_t alen = 0, blen = 0; + uint8_t *tmpA, *tmpB; + if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen) != PM3_SUCCESS) { + if (tmpA) { + free(tmpA); + } goto skipfile; } PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); - // Read keys A from file - size_t bytes_read; - for (uint8_t i = 0; i < numSectors; i++) { - bytes_read = fread(keyA[i], 1, MFKEY_SIZE, f); - if (bytes_read != MFKEY_SIZE) { - PrintAndLogEx(ERR, "File reading error."); - fclose(f); - return PM3_EFILE; - } + for (int i = 0; i < numSectors; i++) { + memcpy(keyA[i], tmpA + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + memcpy(keyB[i], tmpB + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); } - - // Read keys B from file - for (uint8_t i = 0; i < numSectors; i++) { - bytes_read = fread(keyB[i], 1, MFKEY_SIZE, f); - if (bytes_read != MFKEY_SIZE) { - PrintAndLogEx(ERR, "File reading error."); - fclose(f); - return PM3_EFILE; - } - } - - fclose(f); + free(tmpA); + free(tmpB); } skipfile: @@ -6154,7 +6180,6 @@ skipfile: } break; } - } // write to card, try B key first @@ -6169,7 +6194,6 @@ skipfile: } PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; } @@ -6952,6 +6976,7 @@ static int CmdHF14AMfWipe(const char *Cmd) { PrintAndLogEx(INFO, "Forcing overwrite of sector 0 / block 0 "); else PrintAndLogEx(INFO, "Skipping sector 0 / block 0"); + PrintAndLogEx(NORMAL, ""); uint8_t zeros[MFBLOCK_SIZE] = {0}; @@ -6963,7 +6988,12 @@ static int CmdHF14AMfWipe(const char *Cmd) { for (uint8_t b = 0; b < mfNumBlocksPerSector(s); b++) { - // Skipp write to manufacture block if not enforced + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + goto out; + } + + // Skip write to manufacture block if not enforced if (s == 0 && b == 0 && gen2 == false) { continue; } @@ -7026,6 +7056,7 @@ static int CmdHF14AMfView(const char *Cmd) { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "sk", "Save extracted keys to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -7033,6 +7064,7 @@ static int CmdHF14AMfView(const char *Cmd) { char filename[FILE_PATH_SIZE]; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool verbose = arg_get_lit(ctx, 2); + bool save_keys = arg_get_lit(ctx, 3); CLIParserFree(ctx); // read dump file @@ -7063,6 +7095,10 @@ static int CmdHF14AMfView(const char *Cmd) { mf_analyse_acl(block_cnt, dump); } + if (save_keys) { + mf_save_keys_from_arr(block_cnt, dump); + } + int sector = DetectHID(dump, 0x4910); if (sector > -1) { // decode it diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index 4cb7873af..c885e41c9 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -34,7 +34,6 @@ #include "commonutil.h" // ARRAYLEN #include "comms.h" - #include "proxmark3.h" #include "ui.h" #include "util_posix.h" @@ -48,7 +47,8 @@ #define NUM_CHECK_BITFLIPS_THREADS (num_CPUs()) #define NUM_REDUCTION_WORKING_THREADS (num_CPUs()) -#define IGNORE_BITFLIP_THRESHOLD 0.99 // ignore bitflip arrays which have nearly only valid states +// ignore bitflip arrays which have nearly only valid states +#define IGNORE_BITFLIP_THRESHOLD 0.9901 #define STATE_FILES_DIRECTORY "hardnested_tables/" #define STATE_FILE_TEMPLATE "bitflip_%d_%03" PRIx16 "_states.bin.bz2" @@ -57,7 +57,11 @@ // #define DEBUG_REDUCTION // possible sum property values -static uint16_t sums[NUM_SUMS] = {0, 32, 56, 64, 80, 96, 104, 112, 120, 128, 136, 144, 152, 160, 176, 192, 200, 224, 256}; +static uint16_t sums[NUM_SUMS] = { + 0, 32, 56, 64, 80, 96, 104, 112, + 120, 128, 136, 144, 152, 160, 176, 192, + 200, 224, 256 +}; // number of possible partial sum property values #define NUM_PART_SUMS 9 @@ -505,8 +509,11 @@ static void free_sum_bitarrays(void) { static char failstr[250] = ""; #endif -static const float p_K0[NUM_SUMS] = { // the probability that a random nonce has a Sum Property K - 0.0290, 0.0083, 0.0006, 0.0339, 0.0048, 0.0934, 0.0119, 0.0489, 0.0602, 0.4180, 0.0602, 0.0489, 0.0119, 0.0934, 0.0048, 0.0339, 0.0006, 0.0083, 0.0290 +// the probability that a random nonce has a Sum Property K +static const float p_K0[NUM_SUMS] = { + 0.0290, 0.0083, 0.0006, 0.0339, 0.0048, 0.0934, 0.0119, 0.0489, + 0.0602, 0.4180, 0.0602, 0.0489, 0.0119, 0.0934, 0.0048, 0.0339, + 0.0006, 0.0083, 0.0290 }; static float my_p_K[NUM_SUMS]; static const float *p_K; @@ -1275,6 +1282,7 @@ static void apply_sum_a0(void) { } static void simulate_MFplus_RNG(uint32_t test_cuid, uint64_t test_key, uint32_t *nt_enc, uint8_t *par_enc) { + struct Crypto1State sim_cs = {0, 0}; // init cryptostate with key: @@ -1285,12 +1293,20 @@ static void simulate_MFplus_RNG(uint32_t test_cuid, uint64_t test_key, uint32_t *par_enc = 0; uint32_t nt = (rand() & 0xff) << 24 | (rand() & 0xff) << 16 | (rand() & 0xff) << 8 | (rand() & 0xff); + for (int8_t byte_pos = 3; byte_pos >= 0; byte_pos--) { + uint8_t nt_byte_dec = (nt >> (8 * byte_pos)) & 0xff; - uint8_t nt_byte_enc = crypto1_byte(&sim_cs, nt_byte_dec ^ (test_cuid >> (8 * byte_pos)), false) ^ nt_byte_dec; // encode the nonce byte + + // encode the nonce byte + uint8_t nt_byte_enc = crypto1_byte(&sim_cs, nt_byte_dec ^ (test_cuid >> (8 * byte_pos)), false) ^ nt_byte_dec; *nt_enc = (*nt_enc << 8) | nt_byte_enc; - uint8_t ks_par = filter(sim_cs.odd); // the keystream bit to encode/decode the parity bit - uint8_t nt_byte_par_enc = ks_par ^ oddparity8(nt_byte_dec); // determine the nt byte's parity and encode it + + // the keystream bit to encode/decode the parity bit + uint8_t ks_par = filter(sim_cs.odd); + + // determine the nt byte's parity and encode it + uint8_t nt_byte_par_enc = ks_par ^ oddparity8(nt_byte_dec); *par_enc = (*par_enc << 1) | nt_byte_par_enc; } } @@ -2460,6 +2476,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_bitarray(all_bitflips_bitarray[EVEN_STATE]); free_sum_bitarrays(); free_part_sum_bitarrays(); + + return (key_found) ? PM3_SUCCESS : PM3_EFAILED; } + return PM3_SUCCESS; } diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 9b5bac06b..07b1f91b0 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -19,6 +19,7 @@ #include "cliparser.h" #include "cmdlfem4x50.h" #include +#include #include "cmdparser.h" // command_t #include "util_posix.h" // msclock #include "fileutils.h" @@ -349,54 +350,118 @@ int CmdEM4x50Brute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 brute", "Tries to bruteforce the password of a EM4x50 card.\n" - "Function can be stopped by pressing pm3 button.", - "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n" + "Function can be stopped by pressing pm3 button.\n", + + "lf em 4x50 brute --mode range --begin 12330000 --end 12340000 -> tries pwds from 0x12330000 to 0x12340000\n" + "lf em 4x50 brute --mode charset --digits --uppercase -> tries all combinations of ASCII codes for digits and uppercase letters\n" ); void *argtable[] = { arg_param_begin, - arg_str1(NULL, "first", "", "first password (start), 4 bytes, lsb"), - arg_str1(NULL, "last", "", "last password (stop), 4 bytes, lsb"), + arg_str1(NULL, "mode", "", "Bruteforce mode (range|charset)"), + arg_str0(NULL, "begin", "", "Range mode - start of the key range"), + arg_str0(NULL, "end", "", "Range mode - end of the key range"), + arg_lit0(NULL, "digits", "Charset mode - include ASCII codes for digits"), + arg_lit0(NULL, "uppercase", "Charset mode - include ASCII codes for uppercase letters"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - int first_len = 0; - uint8_t first[4] = {0, 0, 0, 0}; - CLIGetHexWithReturn(ctx, 1, first, &first_len); - int last_len = 0; - uint8_t last[4] = {0, 0, 0, 0}; - CLIGetHexWithReturn(ctx, 2, last, &last_len); - CLIParserFree(ctx); - - if (first_len != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes"); - return PM3_EINVARG; - } - if (last_len != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes"); - return PM3_EINVARG; - } em4x50_data_t etd; - etd.password1 = BYTES2UINT32_BE(first); - etd.password2 = BYTES2UINT32_BE(last); + memset(&etd, 0, sizeof(etd)); + + int mode_len = 64; + char mode[64]; + CLIGetStrWithReturn(ctx, 1, (uint8_t *) mode, &mode_len); + PrintAndLogEx(INFO, "Chosen mode: %s", mode); + + if (strcmp(mode, "range") == 0) { + etd.bruteforce_mode = BRUTEFORCE_MODE_RANGE; + } else if (strcmp(mode, "charset") == 0) { + etd.bruteforce_mode = BRUTEFORCE_MODE_CHARSET; + } else { + PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (etd.bruteforce_mode == BRUTEFORCE_MODE_RANGE) { + int begin_len = 0; + uint8_t begin[4] = {0x0}; + CLIGetHexWithReturn(ctx, 2, begin, &begin_len); + + int end_len = 0; + uint8_t end[4] = {0x0}; + CLIGetHexWithReturn(ctx, 3, end, &end_len); + + if (begin_len != 4) { + PrintAndLogEx(FAILED, "'begin' parameter must be 4 bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (end_len != 4) { + PrintAndLogEx(FAILED, "'end' parameter must be 4 bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + etd.password1 = BYTES2UINT32_BE(begin); + etd.password2 = BYTES2UINT32_BE(end); + } else if (etd.bruteforce_mode == BRUTEFORCE_MODE_CHARSET) { + bool enable_digits = arg_get_lit(ctx, 4); + bool enable_uppercase = arg_get_lit(ctx, 5); + + if (enable_digits) + etd.bruteforce_charset |= CHARSET_DIGITS; + if (enable_uppercase) + etd.bruteforce_charset |= CHARSET_UPPERCASE; + + if (etd.bruteforce_charset == 0) { + PrintAndLogEx(FAILED, "Please enable at least one charset when using charset bruteforce mode."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "Enabled charsets: %s%s", + enable_digits ? "digits " : "", + enable_uppercase ? "uppercase " : ""); + + } + + CLIParserFree(ctx); // 27 passwords/second (empirical value) const int speed = 27; + int no_iter = 0; + + if (etd.bruteforce_mode == BRUTEFORCE_MODE_RANGE) { + no_iter = etd.password2 - etd.password1 + 1; + PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]" + , no_iter + , etd.password1 + , etd.password2 + ); + } else if (etd.bruteforce_mode == BRUTEFORCE_MODE_CHARSET) { + unsigned int digits = 0; + + if (etd.bruteforce_charset & CHARSET_DIGITS) + digits += CHARSET_DIGITS_SIZE; + + if (etd.bruteforce_charset & CHARSET_UPPERCASE) + digits += CHARSET_UPPERCASE_SIZE; + + no_iter = pow(digits, 4); + } // print some information - int no_iter = etd.password2 - etd.password1 + 1; int dur_s = no_iter / speed; int dur_h = dur_s / 3600; int dur_m = (dur_s - dur_h * 3600) / 60; dur_s -= dur_h * 3600 + dur_m * 60; - PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]" - , no_iter - , etd.password1 - , etd.password2 - ); + PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s); // start @@ -1190,7 +1255,7 @@ int CmdEM4x50Sim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, - {"brute", CmdEM4x50Brute, IfPm3EM4x50, "Simple bruteforce attack to find password"}, + {"brute", CmdEM4x50Brute, IfPm3EM4x50, "Bruteforce attack to find password"}, {"chk", CmdEM4x50Chk, IfPm3EM4x50, "Check passwords from dictionary"}, {"dump", CmdEM4x50Dump, IfPm3EM4x50, "Dump EM4x50 tag"}, {"info", CmdEM4x50Info, IfPm3EM4x50, "Tag information"}, diff --git a/client/src/cmdusart.c b/client/src/cmdusart.c index d67ae9bf1..0d33ef374 100644 --- a/client/src/cmdusart.c +++ b/client/src/cmdusart.c @@ -68,15 +68,20 @@ static int usart_txrx(uint8_t *srcdata, size_t srclen, uint8_t *dstdata, size_t struct payload_header header; uint8_t data[PM3_CMD_DATA_SIZE - sizeof(uint32_t)]; } PACKED payload; + payload.header.waittime = waittime; - if (srclen >= sizeof(payload.data)) + + if (srclen >= sizeof(payload.data)) { return PM3_EOVFLOW; + } + memcpy(payload.data, srcdata, srclen); SendCommandNG(CMD_USART_TXRX, (uint8_t *)&payload, srclen + sizeof(payload.header)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_TXRX, &resp, waittime + 500)) { + if (WaitForResponseTimeout(CMD_USART_TXRX, &resp, waittime + 500) == false) { return PM3_ETIMEOUT; } + if (resp.status == PM3_SUCCESS) { *dstlen = resp.length; memcpy(dstdata, resp.data.asBytes, resp.length); @@ -154,10 +159,11 @@ static int usart_bt_testcomm(uint32_t baudrate, uint8_t parity) { PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s at %u 8%c1", strlen(string), (int)strlen(string), string, baudrate, parity); - ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); // such large timeout needed + // 1000, such large timeout needed + ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "hc01.comV2.0") == 0) { + if (str_startswith((char *)data, "hc01.comV2.0") || str_startswith((char *)data, "BT SPP V3.0")) { PrintAndLogEx(SUCCESS, "Add-on " _GREEN_("found!")); return PM3_SUCCESS; } diff --git a/client/src/comms.c b/client/src/comms.c index a8ed8be4c..64bea9f65 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -30,8 +30,8 @@ #include "util_posix.h" // msclock #include "util_darwin.h" // en/dis-ableNapp(); -//#define COMMS_DEBUG -//#define COMMS_DEBUG_RAW +// #define COMMS_DEBUG +// #define COMMS_DEBUG_RAW // Serial port that we are communicating with the PM3 on. static serial_port sp = NULL; @@ -369,6 +369,7 @@ __attribute__((force_align_arg_pointer)) } res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen); + if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) { rx.magic = rx_raw.pre.magic; uint16_t length = rx_raw.pre.length; @@ -380,6 +381,7 @@ __attribute__((force_align_arg_pointer)) PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length); error = true; } + if ((!error) && (length > 0)) { // Get the variable length payload res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen); @@ -418,10 +420,10 @@ __attribute__((force_align_arg_pointer)) rx.length = 0; // set received length to 0 else { // old frames can't be empty PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)"); - error = true; } } + if (!error) { // Get the postamble res = uart_receive(sp, (uint8_t *)&rx_raw.foopost, sizeof(PacketResponseNGPostamble), &rxlen); if ((res != PM3_SUCCESS) || (rxlen != sizeof(PacketResponseNGPostamble))) { @@ -429,6 +431,7 @@ __attribute__((force_align_arg_pointer)) error = true; } } + if (!error) { // Check CRC, accept MAGIC as placeholder rx.crc = rx_raw.foopost.crc; if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) { diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 3c904ac70..8fd06456e 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -805,7 +805,7 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_ fflush(f); fclose(f); PrintAndLogEx(SUCCESS, "Found keys have been dumped to " _YELLOW_("%s"), fileName); - PrintAndLogEx(INFO, "FYI! --> " _YELLOW_("0xFFFFFFFFFFFF") " <-- has been inserted for unknown keys where " _YELLOW_("res") " is " _YELLOW_("0")); + PrintAndLogEx(INFO, "FYI! --> " _YELLOW_("0xFFFFFFFFFFFF") " <-- has been inserted for unknown keys where " _YELLOW_("res") " is " _RED_("0")); free(fileName); return PM3_SUCCESS; } diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index f42111ba8..ec8d1a6bf 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -530,7 +530,7 @@ uint8_t mfSectorTrailerOfSector(uint8_t sectorNo) { } // assumes blockno is 0-255.. -uint8_t mfSectorTrailer(uint8_t blockNo) { +uint8_t mfSectorTrailer(uint16_t blockNo) { if (blockNo < 32 * 4) { return (blockNo | 0x03); } else { @@ -539,15 +539,15 @@ uint8_t mfSectorTrailer(uint8_t blockNo) { } // assumes blockno is 0-255.. -bool mfIsSectorTrailer(uint8_t blockNo) { +bool mfIsSectorTrailer(uint16_t blockNo) { return (blockNo == mfSectorTrailer(blockNo)); } // assumes blockno is 0-255.. -uint8_t mfSectorNum(uint8_t blockNo) { +uint8_t mfSectorNum(uint16_t blockNo) { if (blockNo < 32 * 4) - return blockNo / 4; + return (blockNo / 4); else - return 32 + (blockNo - 32 * 4) / 16; + return (32 + (blockNo - 32 * 4) / 16); } diff --git a/client/src/mifare/mifare4.h b/client/src/mifare/mifare4.h index 489add9f3..80d55db7b 100644 --- a/client/src/mifare/mifare4.h +++ b/client/src/mifare/mifare4.h @@ -77,9 +77,9 @@ const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data); uint8_t mfNumBlocksPerSector(uint8_t sectorNo); uint8_t mfFirstBlockOfSector(uint8_t sectorNo); uint8_t mfSectorTrailerOfSector(uint8_t sectorNo); -uint8_t mfSectorTrailer(uint8_t blockNo); -bool mfIsSectorTrailer(uint8_t blockNo); -uint8_t mfSectorNum(uint8_t blockNo); +uint8_t mfSectorTrailer(uint16_t blockNo); +bool mfIsSectorTrailer(uint16_t blockNo); +uint8_t mfSectorNum(uint16_t blockNo); #endif // mifare4.h diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index 941abf41c..e3590329d 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -45,7 +45,10 @@ static const uint64_t g_mifare_default_keys[] = { 0xffffffffffff, // Default key (first key used by program if no user defined key) 0xa0a1a2a3a4a5, // NFCForum MAD key 0xd3f7d3f7d3f7, // NDEF public key - 0x4b791bea7bcc, // MFC EV1 Signature B + 0x4b791bea7bcc, // MFC EV1 Signature 17 B + 0x5C8FF9990DA2, // MFC EV1 Signature 16 A + 0xD01AFEEB890A, // MFC EV1 Signature 16 B + 0x75CCB59C9BED, // MFC EV1 Signature 17 A 0xfc00018778f7, // Public Transport 0x6471a5ef2d1a, // SimonsVoss 0x4E3552426B32, // ID06 @@ -72,6 +75,11 @@ static const uint64_t g_mifare_default_keys[] = { 0x199404281998, // NSP B 0x6A1987C40A21, // SALTO 0x7F33625BC129, // SALTO + 0x484944204953, // HID + 0x204752454154, // HID + 0x3B7E4FD575AD, // HID + 0x11496F97752A, // HID + 0x3E65E4FB65B3, // Gym 0x000000000000, // Blank key 0xb0b1b2b3b4b5, 0xaabbccddeeff, diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index b2a858e09..86d76317b 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -285,6 +285,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { // Try to retrieve the old (current) terminal info struct if (tcgetattr(sp->fd, &sp->tiOld) == -1) { + PrintAndLogEx(ERR, "error: UART get terminal info attribute"); uart_close(sp); return INVALID_SERIAL_PORT; } @@ -305,6 +306,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { // Try to set the new terminal info struct if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) { + PrintAndLogEx(ERR, "error: UART set terminal info attribute"); + perror("tcsetattr() error"); uart_close(sp); return INVALID_SERIAL_PORT; } diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index 9cdcc41d1..cb8b92a73 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -98,6 +98,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { free(prefix); if (strlen(pcPortName) <= 4) { + PrintAndLogEx(ERR, "error: tcp port name length too short"); free(sp); return INVALID_SERIAL_PORT; } diff --git a/common/bruteforce.c b/common/bruteforce.c new file mode 100644 index 000000000..891796690 --- /dev/null +++ b/common/bruteforce.c @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- + +#include "bruteforce.h" +#include +#include + +uint8_t charset_digits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', +}; + +uint8_t charset_uppercase[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', + 'X', 'Y', 'Z' +}; + +void bf_generator_init(generator_context_t *ctx, uint8_t mode) { + memset(ctx, 0, sizeof(generator_context_t)); + ctx->mode = mode; +} + +int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets) { + if (ctx->mode != BRUTEFORCE_MODE_CHARSET) { + return -1; + } + + if (charsets & CHARSET_DIGITS) { + memcpy(ctx->charset, charset_digits, sizeof(charset_digits)); + ctx->charset_length += sizeof(charset_digits); + } + + if (charsets & CHARSET_UPPERCASE) { + memcpy(ctx->charset + ctx->charset_length, charset_uppercase, sizeof(charset_uppercase)); + ctx->charset_length += sizeof(charset_uppercase); + } + + return 0; +} + +int bf_generate32(generator_context_t *ctx) { + + switch (ctx->mode) { + case BRUTEFORCE_MODE_RANGE: + return _bf_generate_mode_range32(ctx); + case BRUTEFORCE_MODE_CHARSET: + return _bf_generate_mode_charset32(ctx); + } + + return GENERATOR_ERROR; +} + +int _bf_generate_mode_range32(generator_context_t *ctx) { + + if (ctx->current_key32 >= ctx->range_high) { + return GENERATOR_END; + } + + // we use flag1 as indicator if value of range_low was already emitted + // so the range generated is + if (ctx->current_key32 <= ctx->range_low && ctx->flag1 == false) { + ctx->current_key32 = ctx->range_low; + ctx->pos[0] = true; + return GENERATOR_NEXT; + } + + ctx->current_key32++; + return GENERATOR_NEXT; +} + +int _bf_generate_mode_charset32(generator_context_t *ctx) { + + if (ctx->flag1) + return GENERATOR_END; + + ctx->current_key32 = ctx->charset[ctx->pos[0]] << 24 | ctx->charset[ctx->pos[1]] << 16 | + ctx->charset[ctx->pos[2]] << 8 | ctx->charset[ctx->pos[3]]; + + + if (bf_array_increment(ctx->pos, 4, ctx->charset_length) == -1) + // set flag1 to emit value last time and end generation + ctx->flag1 = true; + + return GENERATOR_NEXT; +} + +// increments values in array with carryover using modulo limit for each byte +// this is used to iterate each byte in key over charset table +// returns -1 if incrementing reaches its end +int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) { + + uint8_t prev_value; + + // check if we reached max value already + uint8_t i; + for (i = 0; i < data_len; i++) + if (data[i] < modulo - 1) + break; + + if (i == data_len) + return -1; + + for (uint8_t pos = data_len - 1;; pos--) { + prev_value = ++data[pos]; + data[pos] = data[pos] % modulo; + if (prev_value == data[pos]) + return 0; + else if (pos == 0) { + // we cannot carryover to next byte + // with the max value check in place before, we should not reach this place + return -1; + } + } + + return 0; +} diff --git a/common/bruteforce.h b/common/bruteforce.h new file mode 100644 index 000000000..91e01172d --- /dev/null +++ b/common/bruteforce.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// functions for bruteforcing card keys - key generators +//----------------------------------------------------------------------------- + +#ifndef BRUTEFORCE_H__ +#define BRUTEFORCE_H__ + +#include "common.h" + +typedef uint8_t bruteforce_mode_t; +// bruteforcing all keys sequentially between X and Y +#define BRUTEFORCE_MODE_RANGE 1 + +// try keys based on limited charset/passphrases +// some payment systems use user-provided passphrase as system key +#define BRUTEFORCE_MODE_CHARSET 2 + +// "smart" mode - try some predictable patterns +#define BRUTEFORCE_MODE_SMART 3 + + +typedef uint8_t bruteforce_charset_t; +// bit flags - can be used together using logical OR +#define CHARSET_DIGITS 1 +#define CHARSET_UPPERCASE 2 + +#define GENERATOR_END 0 +#define GENERATOR_NEXT 1 +#define GENERATOR_ERROR 2 + +#define CHARSET_DIGITS_SIZE 10 +#define CHARSET_UPPERCASE_SIZE 25 + +extern uint8_t charset_digits[]; +extern uint8_t charset_uppercase[]; + +// structure to hold key generator temporary data +typedef struct { + // position of each of 4 bytes in 32 bit key in charset mode + // add more bytes to support larger keys + // pos[0] is most significant byte - all maths avoid relying on little/big endian memory layout + uint8_t pos[4]; + uint32_t current_key32; + uint8_t mode; + uint8_t charset[ + CHARSET_DIGITS_SIZE + + CHARSET_UPPERCASE_SIZE + ]; + uint8_t charset_length; + + uint32_t range_low; + uint32_t range_high; + // flags to use internally by generators as they wish + bool flag1, flag2, flag3; + +} generator_context_t; + +void bf_generator_init(generator_context_t *ctx, uint8_t mode); +int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets); +int bf_generate32(generator_context_t *ctx); +int _bf_generate_mode_range32(generator_context_t *ctx); +int _bf_generate_mode_charset32(generator_context_t *ctx); +int _bf_generate_mode_smart32(generator_context_t *ctx); +int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo); +#endif // BRUTEFORCE_H__ diff --git a/doc/commands.json b/doc/commands.json index 5fc6d2285..efb03edd2 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -4370,9 +4370,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--sk Save extracted keys to file" ], - "usage": "hf mf eview [-hv] [--mini] [--1k] [--2k] [--4k]" + "usage": "hf mf eview [-hv] [--mini] [--1k] [--2k] [--4k] [--sk]" }, "hf mf fchk": { "command": "hf mf fchk", @@ -4950,9 +4951,10 @@ "options": [ "-h, --help This help", "-f, --file filename of dump", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--sk Save extracted keys to file" ], - "usage": "hf mf view [-hv] -f " + "usage": "hf mf view [-hv] -f [--sk]" }, "hf mf wipe": { "command": "hf mf wipe", @@ -8061,15 +8063,19 @@ "command": "lf em 4x50 brute", "description": "Tries to bruteforce the password of a EM4x50 card. Function can be stopped by pressing pm3 button.", "notes": [ - "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000" + "lf em 4x50 brute --mode range --begin 12330000 --end 12340000 -> tries pwds from 0x12330000 to 0x12340000", + "lf em 4x50 brute --mode charset --digits --uppercase -> tries all combinations of ASCII codes for digits and uppercase letters" ], "offline": false, "options": [ "-h, --help This help", - "--first first password (start), 4 bytes, lsb", - "--last last password (stop), 4 bytes, lsb" + "--mode Bruteforce mode (range|charset)", + "--begin Range mode - start of the key range", + "--end Range mode - end of the key range", + "--digits Charset mode - include ASCII codes for digits", + "--uppercase Charset mode - include ASCII codes for uppercase letters" ], - "usage": "lf em 4x50 brute [-h] --first --last " + "usage": "lf em 4x50 brute [-h] --mode [--begin ] [--end ] [--digits] [--uppercase]" }, "lf em 4x50 chk": { "command": "lf em 4x50 chk", @@ -10896,7 +10902,7 @@ "options": [ "-h, --help This help", "-f, --file SPIFFS file to view", - "-c, --cols column breaks (def 32)" + "-c, --cols column breaks (def 16)" ], "usage": "mem spiffs view [-h] -f [-c ]" }, @@ -12019,5 +12025,6 @@ "commands_extracted": 755, "extracted_by": "PM3Help2JSON v1.00", "extracted_on": "2023-06-04T15:36:56" + } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index c11a548e8..938413fa8 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -875,7 +875,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em 4x50 help `|Y |`This help` -|`lf em 4x50 brute `|N |`Simple bruteforce attack to find password` +|`lf em 4x50 brute `|N |`Bruteforce attack to find password` |`lf em 4x50 chk `|N |`Check passwords from dictionary` |`lf em 4x50 dump `|N |`Dump EM4x50 tag` |`lf em 4x50 info `|N |`Tag information` diff --git a/doc/uart_notes.md b/doc/uart_notes.md index 5c8cb65b6..eeee8a1d0 100644 --- a/doc/uart_notes.md +++ b/doc/uart_notes.md @@ -199,7 +199,7 @@ The ROLE command takes its parameter after an equal sign: |Command |Description |Reply | |-------------------|------------------------------------------------------|--------------| -|`AT+VERSION` |print firmware version |`hc01.comV2.0`| +|`AT+VERSION` |print firmware version |`hc01.comV2.0` or `BT SPP V3.0`| |`AT+BAUD8` |set baudrate to 115200 |`OK115200` | |`AT+PIN1234` |set PINCODE to 1234. (must be numbers) |`OKsetPIN` | |`AT+NAMEPM3_RDV4.0`|set device name (as shown to BT hosts) to `PM3_RDV4.0`|`OKsetname` | diff --git a/fpga-xc2s30/fpga_felica.bit b/fpga-xc2s30/fpga_felica.bit index 83009b499..30ea16e33 100644 Binary files a/fpga-xc2s30/fpga_felica.bit and b/fpga-xc2s30/fpga_felica.bit differ diff --git a/fpga-xc2s30/fpga_hf.bit b/fpga-xc2s30/fpga_hf.bit index 06a4569f9..72f3d619e 100644 Binary files a/fpga-xc2s30/fpga_hf.bit and b/fpga-xc2s30/fpga_hf.bit differ diff --git a/fpga-xc2s30/fpga_hf_15.bit b/fpga-xc2s30/fpga_hf_15.bit index 0e59dd3d0..5d99a0032 100644 Binary files a/fpga-xc2s30/fpga_hf_15.bit and b/fpga-xc2s30/fpga_hf_15.bit differ diff --git a/fpga-xc2s30/fpga_lf.bit b/fpga-xc2s30/fpga_lf.bit index 93a26841a..f5e5ce630 100644 Binary files a/fpga-xc2s30/fpga_lf.bit and b/fpga-xc2s30/fpga_lf.bit differ diff --git a/fpga-xc2s30/hi_iso14443a.v b/fpga-xc2s30/hi_iso14443a.v index c9bc64f94..d52ab4000 100644 --- a/fpga-xc2s30/hi_iso14443a.v +++ b/fpga-xc2s30/hi_iso14443a.v @@ -222,7 +222,7 @@ reg signed [10:0] rx_mod_falling_edge_max; reg signed [10:0] rx_mod_rising_edge_max; reg curbit; -`define EDGE_DETECT_THRESHOLD 3 +`define EDGE_DETECT_THRESHOLD 5 `define EDGE_DETECT_THRESHOLDHIGH 20 always @(negedge adc_clk) diff --git a/include/em4x50.h b/include/em4x50.h index bed01901c..9bf1ffc24 100644 --- a/include/em4x50.h +++ b/include/em4x50.h @@ -20,6 +20,7 @@ #define EM4X50_H__ #include "common.h" +#include "bruteforce.h" #define EM4X50_NO_WORDS 34 @@ -62,6 +63,8 @@ typedef struct { uint32_t password2; uint32_t word; uint32_t addresses; + bruteforce_mode_t bruteforce_mode; + bruteforce_charset_t bruteforce_charset; } PACKED em4x50_data_t; typedef struct { diff --git a/pm3 b/pm3 index 7c1d0e289..d59d10ea6 100755 --- a/pm3 +++ b/pm3 @@ -115,7 +115,7 @@ function get_pm3_list_Linux { if $FINDBTDIRECT; then # check if the MAC of a Proxmark3 was registered in the known devices for MAC in $(dbus-send --system --print-reply --type=method_call --dest='org.bluez' '/' org.freedesktop.DBus.ObjectManager.GetManagedObjects 2>/dev/null|\ - awk '/"Address"/{getline;gsub(/"/,"",$3);a=$3}/Name/{getline;if (/PM3_RDV4/) print a}'); do + awk '/"Address"/{getline;gsub(/"/,"",$3);a=$3}/Name/{getline;if (/PM3_RDV4/ || /Proxmark3 SE/) print a}'); do PM3LIST+=("bt:$MAC") done # we don't probe the device so there is no guarantee the device is actually present diff --git a/tools/btaddon/requirements.txt b/tools/btaddon/requirements.txt new file mode 100644 index 000000000..3452b96e3 --- /dev/null +++ b/tools/btaddon/requirements.txt @@ -0,0 +1 @@ +pyserial==3.5.0