|
#1
|
|||
|
|||
hashcat and HMAC-SHA256 ...
I was looking for a tool to brute forcing a password that use HMAC-SHA256.
My first thought was "hashcat", which easily supports HMAC-SHA256 but there is a problem: it does not support more than 256 characters as "message". My message is more than 7000 characters long. Exists a patch or other software similar to hashcat ? Now I use python for brute force but it is definitely slow ... |
#2
|
|||
|
|||
You might try to recompile hashcat with more chars allowed. Or read HMAC-SHA256 specification, as it should read "chunks", so You might be able to use this info with Your problem
|
The Following User Says Thank You to klvgen For This Useful Post: | ||
niculaita (08-19-2019) |
#3
|
|||
|
|||
I've already tried to recompile hashcat by increasing the number of possible characters from 256 to 8192, but the software crashes. I tried to figure out where the problem may be but without success.
I read the specification and it doesn't seem complicated, but I should put a lot of hashcat code back to adapt it to the new length. I thought there was something else like it, but I didn't find anything. Some idea ? |
#4
|
||||
|
||||
First off the relevant file for this change (assuming you are using the 1450 variant which it sure sounds like you are) - size independent files:
Quote:
Quote:
Code:
typedef struct pw { u32 i[64]; u32 pw_len; } pw_t; I don't know why it would crash - perhaps you can use a debugger and give the source code line upon which it crashes. I am assuming you are using OpenCL and not GPU though I would have imagined they would share definitional source. It does call sha256_hmac_init_swap which has special handling above 64 size - the truncation to 8 bytes looks strange and maybe this is untested given the buffer limitation? It in turn calls sha256_update_swap which looks like it handles any size. Based on the spec: Quote:
It would be nice to make a PR for this but because of the optimization sensitive nature of this project, more thinking about exactly how to do that is needed - maintain optimization while allowing the buffer size to change without annoying recompilations. Oh and finally this is likely the issue - code needs to be generalized in: Quote:
Code:
token.len_min[1] = SALT_MIN; token.len_max[1] = SALT_MAX; Code:
digest[0] = hex_to_u32 (hash_pos + 0); digest[1] = hex_to_u32 (hash_pos + 8); digest[2] = hex_to_u32 (hash_pos + 16); digest[3] = hex_to_u32 (hash_pos + 24); digest[4] = hex_to_u32 (hash_pos + 32); digest[5] = hex_to_u32 (hash_pos + 40); digest[6] = hex_to_u32 (hash_pos + 48); digest[7] = hex_to_u32 (hash_pos + 56); digest[0] = byte_swap_32 (digest[0]); digest[1] = byte_swap_32 (digest[1]); digest[2] = byte_swap_32 (digest[2]); digest[3] = byte_swap_32 (digest[3]); digest[4] = byte_swap_32 (digest[4]); digest[5] = byte_swap_32 (digest[5]); digest[6] = byte_swap_32 (digest[6]); digest[7] = byte_swap_32 (digest[7]); Last edited by chants; 08-19-2019 at 08:13. |
#5
|
|||
|
|||
The First/Next change:... is a mistake I meant to say it is easiest to change:
Quote:
Code:
#define SALT_MAX 256 #define SALT_MAX_OLD 51 Either way I have no forward and back traced this issue - and I am guessing you changed SALT_MAX (it looks like for whatever reason PW_MAX is not used in 1450, not sure why). But you probably forgot to change the pw_t structure size. If either PW_MAX or SALT_MAX is increased about 256, the pw_t->i member needs its array size increased to max(SALT_MAX, PW_MAX)/sizeof(u32) which is currently correctly set to its corresponding 256/4=64 value. Probably why the comment "// do not try to simply change this, it will not work" is sitting right above those constants. Too bad that macro is no easy to integrate in the source due to that file being dependency free. Also please take note: Code:
#define RP_PASSWORD_SIZE 256 Quote:
Last edited by chants; 08-19-2019 at 08:31. |
#6
|
|||
|
|||
Thanks chants,
the change I made was just this: Code:
#define SALT_MAX 8192 There are other calculations, looking in the chain of routines, which should be adjusted, but you risk doing only damage. The author should have put his hands in it, but I doubt he has the time and the will to do it. I'll do some more tests. |
#7
|
|||
|
|||
@debugasm I would really hope to hear the results needed for the change. Especially try:
Quote:
Code:
typedef struct pw { u32 i[2148]; u32 pw_len; } pw_t; Quote:
Code:
#define RP_PASSWORD_SIZE 8192 The problem is the authors did not want to use dynamic memory for any size - since this would slow down the project significantly. And to fix a huge buffer size like this would negatively impact a lot of copy and other operations making them slower significantly. So how exactly to fix this is an interesting question. Instead they hardcoded the size values, knowing but not caring that there is correlation between them, and forcing custom compilation for now. I think the optimized code needs to be built on the fly which is a somewhat tricky thing to achieve though in the future I expect to see this become a regular thing. |
The Following User Says Thank You to chants For This Useful Post: | ||
p4r4d0x (08-22-2019) |
#8
|
|||
|
|||
@chants with your change :
Code:
Hashfile 'message.txt' on line 1 (1d88f5...puNA+iZD2RbscVWiz1HusLS+dLyWwgQF): Token length exception No hashes loaded. Code:
\hashcat\include\common.h Code:
#define PW_MAX 8192 #define SALT_MAX 8192 Code:
\hashcat\src\interface.c Code:
static bool parse_and_store_generic_salt (u8 *out_buf, int *out_len, const u8 *in_buf, const int in_len, MAYBE_UNUSED hashconfig_t *hashconfig) { u32 tmp_u32[(2048 * 2) + 1] = { 0 }; if (in_len > 16384) return false; // 512 = 2 * 256 -- (2 * because of hex), 256 because of maximum salt length in salt_t if (hashconfig->opts_type & OPTS_TYPE_ST_HEX) { if (in_len < (int) (hashconfig->salt_min * 2)) return false; if (in_len > (int) (hashconfig->salt_max * 2)) return false; } else { if (in_len < (int) hashconfig->salt_min) return false; if (in_len > (int) hashconfig->salt_max) return false; } u8 *tmp_buf = (u8 *) tmp_u32; int tmp_len = 0; if (hashconfig->opts_type & OPTS_TYPE_ST_HEX) { if (tmp_len & 1) return false; tmp_len = in_len / 2; for (int i = 0, j = 0; i < tmp_len; i += 1, j += 2) { u8 p0 = in_buf[j + 0]; u8 p1 = in_buf[j + 1]; tmp_buf[i] = hex_convert (p1) << 0; tmp_buf[i] |= hex_convert (p0) << 4; } } else if (hashconfig->opts_type & OPTS_TYPE_ST_BASE64) { tmp_len = base64_decode (base64_to_int, (const u8 *) in_buf, in_len, tmp_buf); } else { if (in_len) memcpy (tmp_buf, in_buf, in_len); tmp_len = in_len; } if (hashconfig->opts_type & OPTS_TYPE_ST_UTF16LE) { if (tmp_len >= 128) return false; for (int i = 64 - 1; i >= 1; i -= 2) { const u32 v = tmp_u32[i / 2]; tmp_u32[i - 0] = ((v >> 8) & 0x00FF0000) | ((v >> 16) & 0x000000FF); tmp_u32[i - 1] = ((v << 8) & 0x00FF0000) | ((v >> 0) & 0x000000FF); } tmp_len = tmp_len * 2; } if (hashconfig->opts_type & OPTS_TYPE_ST_LOWER) { lowercase (tmp_buf, tmp_len); } if (hashconfig->opts_type & OPTS_TYPE_ST_UPPER) { uppercase (tmp_buf, tmp_len); } int tmp2_len = tmp_len; if (hashconfig->opts_type & OPTS_TYPE_ST_ADD80) { if (tmp2_len >= 8192) return false; tmp_buf[tmp2_len++] = 0x80; } if (hashconfig->opts_type & OPTS_TYPE_ST_ADD01) { if (tmp2_len >= 8192) return false; tmp_buf[tmp2_len++] = 0x01; } if (hashconfig->opts_type & OPTS_TYPE_ST_GENERATE_LE) { u32 max = tmp2_len / 4; if (tmp2_len % 4) max++; for (u32 i = 0; i < max; i++) { tmp_u32[i] = byte_swap_32 (tmp_u32[i]); } // Important: we may need to increase the length of memcpy since // we don't want to "loose" some swapped bytes (could happen if // they do not perfectly fit in the 4-byte blocks) // Memcpy does always copy the bytes in the BE order, but since // we swapped them, some important bytes could be in positions // we normally skip with the original len if (tmp2_len % 4) tmp2_len += 4 - (tmp2_len % 4); } memcpy (out_buf, tmp_buf, tmp2_len); *out_len = tmp_len; return true; } Code:
Hashfile 'message.txt' on line 1 (1d88f5...puNA+iZD2RbscVWiz1HusLS+dLyWwgQF): Salt-length exception No hashes loaded. The password is : 12345 |
The Following User Says Thank You to debugasm For This Useful Post: | ||
chants (08-23-2019) |
#9
|
|||
|
|||
The module has the failing check of PARSER_SALT_LENGTH ("Salt-length exception"):
Code:
const bool parse_rc = generic_salt_decode (hashconfig, salt_pos, salt_len, (u8 *) salt->salt_buf, (int *) &salt->salt_len); if (parse_rc == false) return (PARSER_SALT_LENGTH); Quote:
Code:
u32 tmp_u32[(64 * 2) + 1] = { 0 }; u8 *tmp_u8 = (u8 *) tmp_u32; if (in_len > 512) return false; // 512 = 2 * 256 -- (2 * because of hex), 256 because of maximum salt length in salt_t I also agree that PW_MAX and SALT_MAX should be the same value. It looks like you have an old version of the source or did not change shared.c where it really needs to be changed as your error indicates this change you presume to have made was not yet made. |
#10
|
|||
|
|||
I use the latest version available on the site:
https://hashcat.net/files/hashcat-5.1.0.tar.gz The code you mentioned does not exist in my "shared.c" ... Perhaps we are not synchronized with the same version. |
The Following User Says Thank You to debugasm For This Useful Post: | ||
chants (08-24-2019) |
#11
|
|||
|
|||
You are right - apparently they are restructuring the source significantly in the last 9 months... So forget the master branch on GitHub. I mean a ridiculous amount of redesign and refactoring has occurred. Better look at the 5.1 tagged branch here:
Quote:
Code:
int hashconfig_get_salt_max (hashcat_ctx_t *hashcat_ctx, const bool optimized_kernel) { const hashconfig_t *hashconfig = hashcat_ctx->hashconfig; // salt_max : this limit is only interessting for generic hash types that support a salt u32 salt_max = SALT_MAX; if (optimized_kernel == true) { salt_max = SALT_MAX_OLD; if ((hashconfig->opts_type & OPTS_TYPE_ST_UTF16LE) || (hashconfig->opts_type & OPTS_TYPE_ST_UTF16BE)) { salt_max /= 2; } } if (hashconfig->salt_type == SALT_TYPE_GENERIC) { if (hashconfig->opts_type & OPTS_TYPE_ST_HEX) { salt_max *= 2; } switch (hashconfig->hash_mode) { case 11000: salt_max = 56; break; case 12600: salt_max = 64; break; case 15000: salt_max = 64; break; } } return salt_max; } int hashconfig_get_pw_max (hashcat_ctx_t *hashcat_ctx, const bool optimized_kernel) has a lot of cases including the optimized_kernel most importantly also. But not needed for 1450 type. Without a doubt this change is incredibly close to working. |
The Following User Says Thank You to chants For This Useful Post: | ||
niculaita (08-24-2019) |
#12
|
|||
|
|||
I started again with this version:
Code:
https://github.com/hashcat/hashca/ tree/72319875d84c8bebf91647756448ae3991881688 Code:
// do not try to simply change this, it will not work #define PW_MIN 0 #define PW_MAX 8192 #define PW_MAX_OLD 8192 #define SALT_MIN 0 #define SALT_MAX 8192 #define SALT_MAX_OLD 8192 #define HCBUFSIZ_TINY 0x1000 #define HCBUFSIZ_LARGE 0xb0000 Code:
static bool parse_and_store_generic_salt (u8 *out_buf, int *out_len, const u8 *in_buf, const int in_len, MAYBE_UNUSED hashconfig_t *hashconfig) { u32 tmp_u32[(2048 * 2) + 1] = { 0 }; if (in_len > 16384) return false; // 512 = 2 * 256 -- (2 * because of hex), 256 because of maximum salt length in salt_t if (hashconfig->opts_type & OPTS_TYPE_ST_HEX) { if (in_len < (int) (hashconfig->salt_min * 2)) return false; if (in_len > (int) (hashconfig->salt_max * 2)) return false; } else { if (in_len < (int) hashconfig->salt_min) return false; if (in_len > (int) hashconfig->salt_max) return false; } u8 *tmp_buf = (u8 *) tmp_u32; int tmp_len = 0; if (hashconfig->opts_type & OPTS_TYPE_ST_HEX) { if (tmp_len & 1) return false; tmp_len = in_len / 2; for (int i = 0, j = 0; i < tmp_len; i += 1, j += 2) { u8 p0 = in_buf[j + 0]; u8 p1 = in_buf[j + 1]; tmp_buf[i] = hex_convert (p1) << 0; tmp_buf[i] |= hex_convert (p0) << 4; } } else if (hashconfig->opts_type & OPTS_TYPE_ST_BASE64) { tmp_len = base64_decode (base64_to_int, (const u8 *) in_buf, in_len, tmp_buf); } else { if (in_len) memcpy (tmp_buf, in_buf, in_len); tmp_len = in_len; } if (hashconfig->opts_type & OPTS_TYPE_ST_UTF16LE) { if (tmp_len >= 4096) return false; for (int i = 2048 - 1; i >= 1; i -= 2) { const u32 v = tmp_u32[i / 2]; tmp_u32[i - 0] = ((v >> 8) & 0x00FF0000) | ((v >> 16) & 0x000000FF); tmp_u32[i - 1] = ((v << 8) & 0x00FF0000) | ((v >> 0) & 0x000000FF); } tmp_len = tmp_len * 2; } if (hashconfig->opts_type & OPTS_TYPE_ST_LOWER) { lowercase (tmp_buf, tmp_len); } if (hashconfig->opts_type & OPTS_TYPE_ST_UPPER) { uppercase (tmp_buf, tmp_len); } int tmp2_len = tmp_len; if (hashconfig->opts_type & OPTS_TYPE_ST_ADD80) { if (tmp2_len >= 8192) return false; tmp_buf[tmp2_len++] = 0x80; } if (hashconfig->opts_type & OPTS_TYPE_ST_ADD01) { if (tmp2_len >= 8192) return false; tmp_buf[tmp2_len++] = 0x01; } if (hashconfig->opts_type & OPTS_TYPE_ST_GENERATE_LE) { u32 max = tmp2_len / 4; if (tmp2_len % 4) max++; for (u32 i = 0; i < max; i++) { tmp_u32[i] = byte_swap_32 (tmp_u32[i]); } // Important: we may need to increase the length of memcpy since // we don't want to "loose" some swapped bytes (could happen if // they do not perfectly fit in the 4-byte blocks) // Memcpy does always copy the bytes in the BE order, but since // we swapped them, some important bytes could be in positions // we normally skip with the original len if (tmp2_len % 4) tmp2_len += 4 - (tmp2_len % 4); } memcpy (out_buf, tmp_buf, tmp2_len); *out_len = tmp_len; return true; } Code:
int sha256s_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig) { u32 *digest = (u32 *) hash_buf->digest; salt_t *salt = hash_buf->salt; token_t token; token.token_cnt = 2; token.sep[0] = hashconfig->separator; token.len_min[0] = 64; token.len_max[0] = 8192; token.attr[0] = TOKEN_ATTR_VERIFY_LENGTH | TOKEN_ATTR_VERIFY_HEX; token.len_min[1] = SALT_MIN; token.len_max[1] = SALT_MAX; token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH; if (hashconfig->opts_type & OPTS_TYPE_ST_HEX) { token.len_min[1] *= 2; token.len_max[1] *= 2; token.attr[1] |= TOKEN_ATTR_VERIFY_HEX; } const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); u8 *hash_pos = token.buf[0]; digest[0] = hex_to_u32 (hash_pos + 0); digest[1] = hex_to_u32 (hash_pos + 8); digest[2] = hex_to_u32 (hash_pos + 16); digest[3] = hex_to_u32 (hash_pos + 24); digest[4] = hex_to_u32 (hash_pos + 32); digest[5] = hex_to_u32 (hash_pos + 40); digest[6] = hex_to_u32 (hash_pos + 48); digest[7] = hex_to_u32 (hash_pos + 56); digest[0] = byte_swap_32 (digest[0]); digest[1] = byte_swap_32 (digest[1]); digest[2] = byte_swap_32 (digest[2]); digest[3] = byte_swap_32 (digest[3]); digest[4] = byte_swap_32 (digest[4]); digest[5] = byte_swap_32 (digest[5]); digest[6] = byte_swap_32 (digest[6]); digest[7] = byte_swap_32 (digest[7]); if (hashconfig->opti_type & OPTI_TYPE_PRECOMPUTE_MERKLE) { digest[0] -= SHA256M_A; digest[1] -= SHA256M_B; digest[2] -= SHA256M_C; digest[3] -= SHA256M_D; digest[4] -= SHA256M_E; digest[5] -= SHA256M_F; digest[6] -= SHA256M_G; digest[7] -= SHA256M_H; } u8 *salt_pos = token.buf[1]; int salt_len = token.len[1]; const bool parse_rc = parse_and_store_generic_salt ((u8 *) salt->salt_buf, (int *) &salt->salt_len, salt_pos, salt_len, hashconfig); if (parse_rc == false) return (PARSER_SALT_LENGTH); return (PARSER_OK); } Code:
hashcat (v5.1.0) starting... OpenCL Platform #1: Advanced Micro Devices, Inc. ================================================ * Device #1: Intel(R) Core(TM) i7 CPU 950 @ 3.07GHz, 3069/12279 MB allocatable, 8MCU Hashes: 1 digests; 1 unique digests, 1 unique salts Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates Applicable optimizers: * Zero-Byte * Not-Iterated * Single-Hash * Single-Salt * Brute-Force Minimum password length supported by kernel: 0 Maximum password length supported by kernel: 8192 ATTENTION! Pure (unoptimized) OpenCL kernels selected. This enables cracking passwords and salts > length 32 but for the price of drastically reduced performance. If you want to switch to optimized OpenCL kernels, append -O to your commandline. Watchdog: Hardware monitoring interface not found on your system. Watchdog: Temperature abort trigger disabled. Initialized device kernels and memory... |
#13
|
|||
|
|||
As long as the -O (optimized kernel) option is not specified which above it is not, then the _OLD values do not need to be changed as far as I can tell.
The fact that only the constants cannot be changed really indicates a lot of code smells with hard coded values many of which we already have documented. They should be changed to these constants or macros based on them. Since it just stops, did you change the pw_t structure and RP_PASSWORD_SIZE per the above post? It would seem like something might yet be crashing in the launched processes. But at least we have moved out of the parsing and initialization phase and into the processing phase... If there is a fully documented proof of changes here, then without a doubt this will get fixed in the main branch so just changing one constant and recompiling should become possible. But this project has been in a state of flux probably why hard coded constants are lurking all over the place. |
|
|