diff --git a/docs/changes.txt b/docs/changes.txt index e9ecbb578..627e882a3 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -21,6 +21,12 @@ - Added hash-mode: sha384($salt.utf16le($pass)) - Added hash-mode: sha384(utf16le($pass).$salt) +## +## Features +## + +- Autodetect hash-type: performs an automatic analysis of the input hash(es), associating compatible algorithms, or executing the attack if only one compatible format is found. + ## ## Bugs ## diff --git a/example0.cmd b/example0.cmd index d348c9b88..38e05a590 100644 --- a/example0.cmd +++ b/example0.cmd @@ -1,2 +1,2 @@ -hashcat.exe -t 32 -a 7 example0.hash ?a?a?a?a example.dict +hashcat.exe -m 0 -t 32 -a 7 example0.hash ?a?a?a?a example.dict pause diff --git a/example0.sh b/example0.sh index 98ed7cf6f..1274e0b27 100755 --- a/example0.sh +++ b/example0.sh @@ -1 +1 @@ -./hashcat -t 32 -a 7 example0.hash ?a?a?a?a example.dict +./hashcat -m 0 -t 32 -a 7 example0.hash ?a?a?a?a example.dict diff --git a/include/types.h b/include/types.h index bb2820bea..62b242e5d 100644 --- a/include/types.h +++ b/include/types.h @@ -210,6 +210,7 @@ typedef enum status_rc STATUS_ABORTED_RUNTIME = 11, STATUS_ERROR = 13, STATUS_ABORTED_FINISH = 14, + STATUS_AUTODETECT = 16, } status_rc_t; @@ -443,6 +444,7 @@ typedef enum opts_type OPTS_TYPE_MP_MULTI_DISABLE = (1ULL << 52), // do not multiply the kernel-accel with the multiprocessor count per device to allow more fine-tuned workload settings OPTS_TYPE_NATIVE_THREADS = (1ULL << 53), // forces "native" thread count: CPU=1, GPU-Intel=8, GPU-AMD=64 (wavefront), GPU-NV=32 (warps) OPTS_TYPE_POST_AMP_UTF16LE = (1ULL << 54), // run the utf8 to utf16le conversion kernel after they have been processed from amplifiers + OPTS_TYPE_AUTODETECT_DISABLE = (1ULL << 55), // skip autodetect engine } opts_type_t; @@ -594,6 +596,7 @@ typedef enum user_options_defaults { ADVICE_DISABLE = false, ATTACK_MODE = ATTACK_MODE_STRAIGHT, + AUTODETECT = false, BENCHMARK_ALL = false, BENCHMARK = false, BITMAP_MAX = 18, @@ -1939,6 +1942,7 @@ typedef struct user_options char **hc_argv; bool attack_mode_chgd; + bool autodetect; #ifdef WITH_BRAIN bool brain_host_chgd; bool brain_port_chgd; diff --git a/include/usage.h b/include/usage.h index 4f3895973..35eaef562 100644 --- a/include/usage.h +++ b/include/usage.h @@ -9,7 +9,16 @@ #include #include +typedef struct usage_sort +{ + u32 hash_mode; + char *hash_name; + u32 hash_category; + +} usage_sort_t; + void usage_mini_print (const char *progname); void usage_big_print (hashcat_ctx_t *hashcat_ctx); +int sort_by_usage (const void *p1, const void *p2); #endif // _USAGE_H diff --git a/src/hashcat.c b/src/hashcat.c index 7fa047ca7..11a6b6c58 100644 --- a/src/hashcat.c +++ b/src/hashcat.c @@ -28,6 +28,7 @@ #include "event.h" #include "hashes.h" #include "hwmon.h" +#include "hlfmt.h" #include "induct.h" #include "interface.h" #include "logfile.h" @@ -47,6 +48,7 @@ #include "user_options.h" #include "wordlist.h" #include "hashcat.h" +#include "usage.h" #ifdef WITH_BRAIN #include "brain.h" @@ -1138,6 +1140,277 @@ int hashcat_session_init (hashcat_ctx_t *hashcat_ctx, const char *install_folder return 0; } +bool autodetect_hashmode_test (hashcat_ctx_t *hashcat_ctx) +{ + hashconfig_t *hashconfig = hashcat_ctx->hashconfig; + hashes_t *hashes = hashcat_ctx->hashes; + module_ctx_t *module_ctx = hashcat_ctx->module_ctx; + user_options_t *user_options = hashcat_ctx->user_options; + user_options_extra_t *user_options_extra = hashcat_ctx->user_options_extra; + + // check for file or hash on command line + // if file, find out if binary file + + if (hashconfig->opts_type & OPTS_TYPE_AUTODETECT_DISABLE) return false; + + if (hashconfig->opts_type & OPTS_TYPE_BINARY_HASHFILE) + { + if (hashconfig->opts_type & OPTS_TYPE_BINARY_HASHFILE_OPTIONAL) + { + hashes->hashlist_mode = (hc_path_exist (user_options_extra->hc_hash) == true) ? HL_MODE_FILE_PLAIN : HL_MODE_ARG; + + if (hashes->hashlist_mode == HL_MODE_FILE_PLAIN) + { + hashes->hashfile = user_options_extra->hc_hash; + } + } + else + { + hashes->hashlist_mode = HL_MODE_FILE_BINARY; + + if (hc_path_read (user_options_extra->hc_hash) == false) return false; + + hashes->hashfile = user_options_extra->hc_hash; + } + } + else + { + hashes->hashlist_mode = (hc_path_exist (user_options_extra->hc_hash) == true) ? HL_MODE_FILE_PLAIN : HL_MODE_ARG; + + if (hashes->hashlist_mode == HL_MODE_FILE_PLAIN) + { + hashes->hashfile = user_options_extra->hc_hash; + } + } + + /** + * load hashes, part I: find input mode + */ + + const char *hashfile = hashes->hashfile; + const u32 hashlist_mode = hashes->hashlist_mode; + + u32 hashlist_format = HLFMT_HASHCAT; + + if (hashlist_mode == HL_MODE_FILE_PLAIN) + { + HCFILE fp; + + if (hc_fopen (&fp, hashfile, "rb") == false) return false; + + hashlist_format = hlfmt_detect (hashcat_ctx, &fp, 100); + + hc_fclose (&fp); + } + + hashes->hashlist_format = hashlist_format; + + /** + * load hashes, part II: allocate required memory, set pointers + */ + + void *digest = hccalloc (1, hashconfig->dgst_size); + salt_t *salt = (salt_t *) hccalloc (1, sizeof (salt_t)); + void *esalt = NULL; + void *hook_salt = NULL; + + if (hashconfig->esalt_size > 0) + { + esalt = hccalloc (1, hashconfig->esalt_size); + } + + if (hashconfig->hook_salt_size > 0) + { + hook_salt = hccalloc (1, hashconfig->hook_salt_size); + } + + hashinfo_t *hash_info = (hashinfo_t *) hcmalloc (sizeof (hashinfo_t)); + + hash_info->user = (user_t *) hcmalloc (sizeof (user_t)); + hash_info->orighash = (char *) hcmalloc (256); + hash_info->split = (split_t *) hcmalloc (sizeof (split_t)); + + hash_t *hashes_buf = (hash_t *) hcmalloc (sizeof (hash_t)); + + hashes_buf->digest = digest; + hashes_buf->salt = salt; + hashes_buf->esalt = esalt; + hashes_buf->hook_salt = hook_salt; + + hashes->hashes_buf = hashes_buf; + hashes->digests_buf = digest; + hashes->salts_buf = salt; + hashes->esalts_buf = esalt; + hashes->hook_salts_buf = hook_salt; + + bool success = false; + + if (hashlist_mode == HL_MODE_ARG) + { + char *input_buf = user_options_extra->hc_hash; + + size_t input_len = strlen (input_buf); + + char *hash_buf = NULL; + int hash_len = 0; + + hlfmt_hash (hashcat_ctx, hashlist_format, input_buf, input_len, &hash_buf, &hash_len); + + bool hash_fmt_error = false; + + if (hash_len < 1) hash_fmt_error = true; + if (hash_buf == NULL) hash_fmt_error = true; + + if (hash_fmt_error) return false; + + const int parser_status = module_ctx->module_hash_decode (hashconfig, digest, salt, esalt, hook_salt, hash_info, hash_buf, hash_len); + + if (parser_status == PARSER_OK) success = true; + } + else if (hashlist_mode == HL_MODE_FILE_PLAIN) + { + HCFILE fp; + + if (hc_fopen (&fp, hashfile, "rb") == false) return false; + + char *line_buf = (char *) hcmalloc (HCBUFSIZ_LARGE); + + while (!hc_feof (&fp)) + { + const size_t line_len = fgetl (&fp, line_buf, HCBUFSIZ_LARGE); + + if (line_len == 0) continue; + + char *hash_buf = NULL; + int hash_len = 0; + + hlfmt_hash (hashcat_ctx, hashlist_format, line_buf, line_len, &hash_buf, &hash_len); + + bool hash_fmt_error = false; + + if (hash_len < 1) hash_fmt_error = true; + if (hash_buf == NULL) hash_fmt_error = true; + + if (hash_fmt_error) continue; + + int parser_status = module_ctx->module_hash_decode (hashconfig, digest, salt, esalt, hook_salt, hash_info, hash_buf, hash_len); + + if (parser_status == PARSER_OK) + { + success = true; + + break; + } + } + + hcfree (line_buf); + + hc_fclose (&fp); + } + else if (hashlist_mode == HL_MODE_FILE_BINARY) + { + char *input_buf = user_options_extra->hc_hash; + + size_t input_len = strlen (input_buf); + + if (module_ctx->module_hash_binary_parse != MODULE_DEFAULT) + { + const int hashes_parsed = module_ctx->module_hash_binary_parse (hashconfig, user_options, user_options_extra, hashes); + + if (hashes_parsed > 0) success = true; + } + else + { + const int parser_status = module_ctx->module_hash_decode (hashconfig, digest, salt, esalt, hook_salt, hash_info, input_buf, input_len); + + if (parser_status == PARSER_OK) success = true; + } + } + + hcfree (digest); + hcfree (salt); + hcfree (hash_info); + hcfree (hashes_buf); + + if (hashconfig->esalt_size > 0) + { + hcfree (esalt); + } + + if (hashconfig->hook_salt_size > 0) + { + hcfree (hook_salt); + } + + hashes->digests_buf = NULL; + hashes->salts_buf = NULL; + hashes->esalts_buf = NULL; + hashes->hook_salts_buf = NULL; + + return success; +} + +int autodetect_hashmodes (hashcat_ctx_t *hashcat_ctx, usage_sort_t *usage_sort_buf) +{ + folder_config_t *folder_config = hashcat_ctx->folder_config; + user_options_t *user_options = hashcat_ctx->user_options; + + int usage_sort_cnt = 0; + + // save quiet state so we can restore later + + const bool quiet_sav = user_options->quiet; + + user_options->quiet = true; + + char *modulefile = (char *) hcmalloc (HCBUFSIZ_TINY); + + if (modulefile == NULL) return -1; + + // brute force all the modes + + for (int i = 0; i < MODULE_HASH_MODES_MAXIMUM; i++) + { + user_options->hash_mode = i; + + // this is just to find out of that hash-mode exists or not + + module_filename (folder_config, i, modulefile, HCBUFSIZ_TINY); + + if (hc_path_exist (modulefile) == false) continue; + + // we know it exists, so load the plugin + + const int hashconfig_init_rc = hashconfig_init (hashcat_ctx); + + if (hashconfig_init_rc == 0) + { + const bool test_rc = autodetect_hashmode_test (hashcat_ctx); + + if (test_rc == true) + { + usage_sort_buf[usage_sort_cnt].hash_mode = hashcat_ctx->hashconfig->hash_mode; + usage_sort_buf[usage_sort_cnt].hash_name = hcstrdup (hashcat_ctx->hashconfig->hash_name); + usage_sort_buf[usage_sort_cnt].hash_category = hashcat_ctx->hashconfig->hash_category; + + usage_sort_cnt++; + } + } + + // clean up + + hashconfig_destroy (hashcat_ctx); + } + + hcfree (modulefile); + + qsort (usage_sort_buf, usage_sort_cnt, sizeof (usage_sort_t), sort_by_usage); + + user_options->quiet = quiet_sav; + + return usage_sort_cnt; +} + int hashcat_session_execute (hashcat_ctx_t *hashcat_ctx) { folder_config_t *folder_config = hashcat_ctx->folder_config; @@ -1170,6 +1443,65 @@ int hashcat_session_execute (hashcat_ctx_t *hashcat_ctx) dictstat_read (hashcat_ctx); + // autodetect + + if (user_options->autodetect == true) + { + status_ctx->devices_status = STATUS_AUTODETECT; + + usage_sort_t *usage_sort_buf = (usage_sort_t *) hccalloc (MODULE_HASH_MODES_MAXIMUM, sizeof (usage_sort_t)); + + if (usage_sort_buf == NULL) return -1; + + const int modes_cnt = autodetect_hashmodes (hashcat_ctx, usage_sort_buf); + + if (modes_cnt <= 0) + { + if (user_options->show == false) event_log_error (hashcat_ctx, "No hash-mode matches the structure of the input hash."); + + return -1; + } + + if (modes_cnt > 1) + { + event_log_info (hashcat_ctx, "The following %d hash-mode match the structure of your input hash:", modes_cnt); + event_log_info (hashcat_ctx, NULL); + event_log_info (hashcat_ctx, " # | Name | Category"); + event_log_info (hashcat_ctx, " ======+=====================================================+======================================"); + + for (int i = 0; i < modes_cnt; i++) + { + event_log_info (hashcat_ctx, "%7u | %-51s | %s", usage_sort_buf[i].hash_mode, usage_sort_buf[i].hash_name, strhashcategory (usage_sort_buf[i].hash_category)); + + hcfree (usage_sort_buf[i].hash_name); + } + + event_log_info (hashcat_ctx, NULL); + + event_log_error (hashcat_ctx, "Please specify the hash-mode by argument (-m)."); + + hcfree (usage_sort_buf); + + return -1; + } + + // modes_cnt == 1 + + event_log_warning (hashcat_ctx, "You have not specified -m to select the correct hash-mode."); + event_log_warning (hashcat_ctx, "It was automatically selected by hashcat because it was the only hash-mode matching your input hash:"); + event_log_warning (hashcat_ctx, "\n%u | %s | %s\n", usage_sort_buf[0].hash_mode, usage_sort_buf[0].hash_name, strhashcategory (usage_sort_buf[0].hash_category)); + event_log_warning (hashcat_ctx, "Under no circumstances it is not to be understood as a guarantee this is the right hash-mode."); + event_log_warning (hashcat_ctx, "Do not report hashcat issues if you do not know exactly how the hash was extracted."); + event_log_warning (hashcat_ctx, NULL); + + user_options->autodetect = false; + + user_options->hash_mode = usage_sort_buf[0].hash_mode; + + hcfree (usage_sort_buf[0].hash_name); + hcfree (usage_sort_buf); + } + /** * outer loop */ diff --git a/src/interface.c b/src/interface.c index c49f30610..8b0d4c8a3 100644 --- a/src/interface.c +++ b/src/interface.c @@ -259,7 +259,7 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) { if ((hashconfig->opts_type & OPTS_TYPE_KEYBOARD_MAPPING) == 0) { - event_log_error (hashcat_ctx, "Parameter --keyboard-layout-mapping not valid for hash-type %u", hashconfig->hash_mode); + if (user_options->autodetect == false) event_log_error (hashcat_ctx, "Parameter --keyboard-layout-mapping not valid for hash-type %u", hashconfig->hash_mode); return -1; } @@ -288,7 +288,7 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) } else { - event_log_error (hashcat_ctx, "Parameter --hex-salt not valid for hash-type %u", hashconfig->hash_mode); + if (user_options->autodetect == false) event_log_error (hashcat_ctx, "Parameter --hex-salt not valid for hash-type %u", hashconfig->hash_mode); return -1; } @@ -302,7 +302,7 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) { if (hashconfig->opts_type & OPTS_TYPE_SUGGEST_KG) { - if (user_options->quiet == false) + if (user_options->quiet == false && user_options->autodetect == false) { event_log_warning (hashcat_ctx, "This hash-mode is known to emit multiple valid password candidates for the same hash."); event_log_warning (hashcat_ctx, "Use --keep-guessing to prevent hashcat from shutdown after the hash has been cracked."); diff --git a/src/modules/module_02000.c b/src/modules/module_02000.c index 0c21c8862..19fb60394 100644 --- a/src/modules/module_02000.c +++ b/src/modules/module_02000.c @@ -20,7 +20,8 @@ static const u32 HASH_CATEGORY = HASH_CATEGORY_PLAIN; static const char *HASH_NAME = "STDOUT"; static const u64 KERN_TYPE = 2000; static const u32 OPTI_TYPE = 0; -static const u64 OPTS_TYPE = OPTS_TYPE_SELF_TEST_DISABLE; +static const u64 OPTS_TYPE = OPTS_TYPE_SELF_TEST_DISABLE + | OPTS_TYPE_AUTODETECT_DISABLE; static const u32 SALT_TYPE = SALT_TYPE_NONE; static const char *ST_PASS = "hashcat"; static const char *ST_HASH = "hashcat"; diff --git a/src/modules/module_09000.c b/src/modules/module_09000.c index a6341d273..5b7c87c07 100644 --- a/src/modules/module_09000.c +++ b/src/modules/module_09000.c @@ -21,7 +21,8 @@ static const char *HASH_NAME = "Password Safe v2"; static const u64 KERN_TYPE = 9000; static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE; static const u64 OPTS_TYPE = OPTS_TYPE_PT_GENERATE_LE - | OPTS_TYPE_BINARY_HASHFILE; + | OPTS_TYPE_BINARY_HASHFILE + | OPTS_TYPE_AUTODETECT_DISABLE; static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; static const char *ST_PASS = "hashcat"; static const char *ST_HASH = "0a3f352686e5eb5be173e668a4fff5cd5df420927e1da2d5d4052340160637e3e6a5a92841a188ed240e13b919f3d91694bd4c0acba79271e9c08a83ea5ad387cbb74d5884066a1cb5a8caa80d847079168f84823847c631dbe3a834f1bc496acfebac3bff1608bf1c857717f8f428e07b5e2cb12aaeddfa83d7dcb6d840234d08b84f8ca6c6e562af73eea13148f7902bcaf0220d3e36eeeff1d37283dc421483a2791182614ebb"; diff --git a/src/modules/module_99999.c b/src/modules/module_99999.c index 959786003..e079aa7ba 100644 --- a/src/modules/module_99999.c +++ b/src/modules/module_99999.c @@ -30,7 +30,8 @@ static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE | OPTI_TYPE_RAW_HASH; static const u64 OPTS_TYPE = OPTS_TYPE_PT_GENERATE_LE | OPTS_TYPE_PT_ADD80 - | OPTS_TYPE_PT_ADDBITS14; + | OPTS_TYPE_PT_ADDBITS14 + | OPTS_TYPE_AUTODETECT_DISABLE; static const u32 SALT_TYPE = SALT_TYPE_NONE; static const char *ST_PASS = "hashcat"; static const char *ST_HASH = "hashcat"; diff --git a/src/status.c b/src/status.c index dc39cf71e..93d4404fe 100644 --- a/src/status.c +++ b/src/status.c @@ -34,6 +34,7 @@ static const char *ST_0012 = "Running (Checkpoint Quit requested)"; static const char *ST_0013 = "Error"; static const char *ST_0014 = "Aborted (Finish)"; static const char *ST_0015 = "Running (Quit after attack requested)"; +static const char *ST_0016 = "Autodetect"; static const char *ST_9999 = "Unknown! Bug!"; static const char UNITS[7] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E' }; @@ -292,6 +293,7 @@ const char *status_get_status_string (const hashcat_ctx_t *hashcat_ctx) case STATUS_ABORTED_RUNTIME: return ST_0011; case STATUS_ERROR: return ST_0013; case STATUS_ABORTED_FINISH: return ST_0014; + case STATUS_AUTODETECT: return ST_0016; } return ST_9999; diff --git a/src/terminal.c b/src/terminal.c index b8206bd32..da80ebcf9 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -68,6 +68,11 @@ void welcome_screen (hashcat_ctx_t *hashcat_ctx, const char *version_tag) event_log_info (hashcat_ctx, "%s (%s) starting in progress-only mode...", PROGNAME, version_tag); event_log_info (hashcat_ctx, NULL); } + else if (user_options->hash_mode == 0 && user_options->hash_mode_chgd == false) + { + event_log_info (hashcat_ctx, "%s (%s) starting in autodetect mode...", PROGNAME, version_tag); + event_log_info (hashcat_ctx, NULL); + } else { event_log_info (hashcat_ctx, "%s (%s) starting...", PROGNAME, version_tag); diff --git a/src/usage.c b/src/usage.c index 8202121c8..b63e417c2 100644 --- a/src/usage.c +++ b/src/usage.c @@ -26,7 +26,7 @@ static const char *const USAGE_BIG_PRE_HASHMODES[] = "", " Options Short / Long | Type | Description | Example", "================================+======+======================================================+=======================", - " -m, --hash-type | Num | Hash-type, see references below | -m 1000", + " -m, --hash-type | Num | Hash-type, references below (otherwise autodetect) | -m 1000", " -a, --attack-mode | Num | Attack-mode, see references below | -a 3", " -V, --version | | Print version |", " -h, --help | | Print help |", @@ -239,15 +239,7 @@ static const char *const USAGE_BIG_POST_HASHMODES[] = NULL }; -typedef struct usage_sort -{ - u32 hash_mode; - char *hash_name; - u32 hash_category; - -} usage_sort_t; - -static int sort_by_usage (const void *p1, const void *p2) +int sort_by_usage (const void *p1, const void *p2) { const usage_sort_t *u1 = (const usage_sort_t *) p1; const usage_sort_t *u2 = (const usage_sort_t *) p2; diff --git a/src/user_options.c b/src/user_options.c index ce4d93a24..4abddd4cc 100644 --- a/src/user_options.c +++ b/src/user_options.c @@ -157,6 +157,7 @@ int user_options_init (hashcat_ctx_t *hashcat_ctx) user_options->advice_disable = ADVICE_DISABLE; user_options->attack_mode = ATTACK_MODE; + user_options->autodetect = AUTODETECT; user_options->backend_devices = NULL; user_options->backend_ignore_cuda = BACKEND_IGNORE_CUDA; user_options->backend_ignore_opencl = BACKEND_IGNORE_OPENCL; @@ -1892,6 +1893,14 @@ void user_options_preprocess (hashcat_ctx_t *hashcat_ctx) { user_options->potfile_disable = true; } + + if (user_options->stdout_flag == false && user_options->benchmark == false && user_options->keyspace == false) + { + if (user_options->hash_mode == 0 && user_options->hash_mode_chgd == false) + { + user_options->autodetect = true; + } + } } void user_options_postprocess (hashcat_ctx_t *hashcat_ctx)