From fc90bfc00c46d1a1b7f69ce9816c0df929773d49 Mon Sep 17 00:00:00 2001 From: jsteube Date: Thu, 24 Jan 2019 13:43:07 +0100 Subject: [PATCH] Add module and unit test for hash-mode 15300 --- include/interface_migrate.h | 36 --- src/interface_migrate.c | 292 ----------------------- src/modules/module_15300.c | 435 +++++++++++++++++++++++++++++++++++ tools/test_modules/m15300.pm | 411 +++++++++++++++++++++++++++++++++ 4 files changed, 846 insertions(+), 328 deletions(-) create mode 100644 src/modules/module_15300.c create mode 100644 tools/test_modules/m15300.pm diff --git a/include/interface_migrate.h b/include/interface_migrate.h index 06ca1907a..2481cde83 100644 --- a/include/interface_migrate.h +++ b/include/interface_migrate.h @@ -8,7 +8,6 @@ static const float MIN_SUFFICIENT_ENTROPY_FILE = 7.0f; * algo specific */ - typedef struct itunes_backup { u32 wpky[10]; @@ -233,26 +232,6 @@ typedef struct psafe3 } psafe3_t; -typedef struct dpapimk -{ - u32 context; - - u32 SID[32]; - u32 SID_len; - u32 SID_offset; - - /* here only for possible - forward compatibiliy - */ - // u8 cipher_algo[16]; - // u8 hash_algo[16]; - - u32 iv[4]; - u32 contents_len; - u32 contents[128]; - -} dpapimk_t; - typedef struct jks_sha1 { u32 checksum[5]; @@ -664,17 +643,6 @@ typedef struct keepass_tmp } keepass_tmp_t; -typedef struct dpapimk_tmp_v1 -{ - u32 ipad[5]; - u32 opad[5]; - u32 dgst[10]; - u32 out[10]; - - u32 userKey[5]; - -} dpapimk_tmp_v1_t; - typedef struct dpapimk_tmp_v2 { u64 ipad64[8]; @@ -795,7 +763,6 @@ typedef enum hash_type HASH_TYPE_ITUNES_BACKUP_10 = 57, HASH_TYPE_SKIP32 = 58, HASH_TYPE_CHACHA20 = 60, - HASH_TYPE_DPAPIMK = 61, HASH_TYPE_JKS_SHA1 = 62, HASH_TYPE_TACACS_PLUS = 63, HASH_TYPE_APPLE_SECURE_NOTES = 64, @@ -956,7 +923,6 @@ typedef enum kern_type KERN_TYPE_SKIP32 = 14900, KERN_TYPE_FILEZILLA_SERVER = 15000, KERN_TYPE_NETBSD_SHA1CRYPT = 15100, - KERN_TYPE_DPAPIMK_V1 = 15300, KERN_TYPE_CHACHA20 = 15400, KERN_TYPE_JKS_SHA1 = 15500, KERN_TYPE_ETHEREUM_PBKDF2 = 15600, @@ -1044,7 +1010,6 @@ typedef enum rounds_count ROUNDS_ITUNES102_BACKUP = 10000, ROUNDS_ATLASSIAN = 10000, ROUNDS_NETBSD_SHA1CRYPT = 20000, - ROUNDS_DPAPIMK_V1 = 24000 - 1, // from 4000 to 24000 (possibly more) ROUNDS_DPAPIMK_V2 = 8000 - 1, // from 4000 to 24000 (possibly more) ROUNDS_ETHEREUM_PBKDF2 = 262144 - 1, ROUNDS_APPLE_SECURE_NOTES = 20000, @@ -1207,7 +1172,6 @@ int sha256b64s_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_bu int filezilla_server_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); int netbsd_sha1crypt_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); int atlassian_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); -int dpapimk_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); int jks_sha1_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); int ethereum_pbkdf2_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); int ethereum_scrypt_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig); diff --git a/src/interface_migrate.c b/src/interface_migrate.c index afdc7be70..07969a248 100644 --- a/src/interface_migrate.c +++ b/src/interface_migrate.c @@ -105,7 +105,6 @@ " 11500 | CRC32 | Checksums", " 1100 | Domain Cached Credentials (DCC), MS Cache | Operating Systems", " 2100 | Domain Cached Credentials 2 (DCC2), MS Cache 2 | Operating Systems", - " 15300 | DPAPI masterkey file v1 | Operating Systems", " 15900 | DPAPI masterkey file v2 | Operating Systems", " 12800 | MS-AzureSync PBKDF2-HMAC-SHA256 | Operating Systems", " 12400 | BSDi Crypt, Extended DES | Operating Systems", @@ -445,7 +444,6 @@ static const char *ST_HASH_14900 = "7090b6b9:04223875"; static const char *ST_HASH_15000 = "bfa9fe5a404faff8b0d200385e26b783a163e475869336029d3ebaccaf02b5f16e4949279e8a33b942ab647f8f19a83dbe89a6d39dd6d8f84812de7d2e556767:6422386434050716105781561510557063652302782465168686858312232148"; static const char *ST_HASH_15100 = "$sha1$20000$75552156$HhYMDdaEHiK3eMIzTldOFPnw.s2Q"; static const char *ST_HASH_15200 = "$blockchain$v2$5000$288$324724252428471806184866704068819419467b2b32fd9593fd1a274e0b68bf2c72e5a1f5e748fd319056d1e47ca7b40767136a2d97d7133d14faaeca50986f66cdbc0faec0a3fabbd0ba5d08d5322b6b53da021aacfc439c45bec0e9fe02ad81db82f94e9bd36a7d4d76b505c2339fcd46565d3abab958fbeb1de8bfc53beb96cde8fe44128965477c9ef0762c62bbb1d66532b4888e174ea949db54374a2ed9686a63eb0b5b17ae293f7410bb4ae5106f108314a259c5fd097d558515d79350713412159103a8a174cd384a14f3da45efe18044e1146036000231f6042577d0add98fc959d265368e398dc1550b0bc693e9023cd9d51b40e701bd786e19c3a281a90465aa6ea3f9e756d430164ab2eb43be5b6796d7ac15b2fe99217410f2"; -static const char *ST_HASH_15300 = "$DPAPImk$1*1*S-15-21-466364039-425773974-453930460-1925*des3*sha1*24000*b038489dee5ad04e3e3cab4d957258b5*208*cb9b5b7d96a0d2a00305ca403d3fd9c47c561e35b4b2cf3aebfd1d3199a6481d56972be7ebd6c291b199e6f1c2ffaee91978706737e9b1209e6c7d3aa3d8c3c3e38ad1ccfa39400d62c2415961c17fd0bd6b0f7bbd49cc1de1a394e64b7237f56244238da8d37d78"; static const char *ST_HASH_15400 = "$chacha20$*0400000000000003*35*0200000000000001*3961626364656667*8a152c57a7a856a8"; static const char *ST_HASH_15500 = "$jksprivk$*338BD2FBEBA7B3EF198A4CBFC6E18AFF1E229367*5225850113575146134463704406336350011656*D5253EB151EB92DC73E542D8C0A4D7A848A5B0C0E370E625E6547D4E6F23416FC85A27BC295731B8021CDFBD003551C66C434FFBC87DACAD1FDF39022320034A2F86E779F2B1B3325428A666518FA89507AD63E15FD9C57B9E36EF5B642A2F448A9A3F09B79AD93D65F46B8692CD07539FD140146F8F219DC262971AF019E18EDC16C3C240569E1673F4D98BC818CCF28298D5A7BFF038A663DD10FE5E48643C3217C237D342164E2D41EF15075431FBD5B34800E5AE7EB80FAA5AE9982A55F35379AA7B31217E7F1C5F1964A15024A305AE4B3981FE1C80C163BC38ECA5581F11867E5C34C5D124D0367B3737E5E5BB14D2CAB26A698C8DAAB755C82BA6B823BCAECDD4A89C831651ACE5A6029FD0D3515C5D1D53AD8B9062CE8C445373862035CBBF60D490CA2E4975EE6E0358EC32E871FAB15347E3032E21F30F543BAAB01D779BA833CA0B8C7591B42C7C59A8FDD46D7DECEC0E91ADBF331177605E7830ABED62FAD7D5D806D8EFD01C38765940B7F97168FC72C39BF4C98F944FFC310CA8F4EB1D0F960F352CC5E2BB23A1EB221072A5471EDA2CE81C04595B8D37088CFB5C14F6A4A881AD12125DEFBB8154EB4C130AB7FD9933FD36DF1A6A26B51AB169866788678FCED988C8E017CA84354F487A5508210181AFB8B3AD0753E3E28BE674DFBD4E4FBDFD1E30D592F4EA3A77A2F0F5CF9A175DBC590EF5D42971A39918F12B92DCD8BFD56BE9A3459856B5587603C7B53062663A4C8894BBC9894FB1663BF30F32D907664328138B7A50EAC7F8E3183D74562A5C90FE1889AC4C5FE43EBEB8974563B6682F92591ECA4FA0DA72236C3851DA102DB6BA0CC07BFD32F7E962AB0EDCF4A8DEA6525174F5BB5C021E2A9A3F7F761E9CA90B6E27FB7E55CD91DA184FAC5E534E8AD25314C56CE5796506A0CA70881782F9C5147D87705065D68BD67D2B0344205BA6445D562273690004CA5A303274FB283A75F49BA968D7947943AA98F2AF9CB8253B425B86225E7395A331AC4CB1B1700C64D4F458D5D642C54148AE6DA41D9E26657D331B157D76042C2CF3057B83997C23D8BF68FB3C7337CAFB8B324AD0DF7A80B554B4D7F9AD6ED527E7932F1741A573C152A41610F6517E3F4A3BC6B66685871A7CE3795C559BD47CDB8E34CB2C1DFE980518D79E2078C258C54F312EB38609F640E7DC013E0F2A16A25BB5971882B4308D27930CA99FEC231AE927B62215A1B56098C362B7F20593953B29428681875070E84BF5B60BEA3948127151634123DA77C814AAD54CE10905763C8C19BC191C0C40458C809402E1957C4C05C4EAE27576B2D30593F7FDCC9A248DB5DB23CF2FA22A92C016090F611690BF0AB5B8B2866ED25F345EFE85DF3311C9E91C37CEE709CF16E7CB09D01BECD2961D094C02D42EC85BF47FAB1B67A13B9A1741C15F7156D57A71BFFABB03B71E69707913A5C136B3D69CE3F71ABFE376F0A21D723FFA2E60AC180689D3E8AF4348C9F555CD897387327FC8BA2B9C51A7298547E556A11A60441EF5331A1BFB847A3D23DD9F7C50E636A2C6309BC82E1A8852F5A8569B6D93*14*78D6A2424484CF5149932B7EA8BF*test"; static const char *ST_HASH_15600 = "$ethereum$p*1024*38353131353831333338313138363430*a8b4dfe92687dbc0afeb5dae7863f18964241e96b264f09959903c8c924583fc*0a9252861d1e235994ce33dbca91c98231764d8ecb4950015a8ae20d6415b986"; @@ -608,7 +606,6 @@ static const char *HT_14900 = "Skip32 (PT = $salt, key = $pass)"; static const char *HT_15000 = "FileZilla Server >= 0.9.55"; static const char *HT_15100 = "Juniper/NetBSD sha1crypt"; static const char *HT_15200 = "Blockchain, My Wallet, V2"; -static const char *HT_15300 = "DPAPI masterkey file v1"; static const char *HT_15400 = "ChaCha20"; static const char *HT_15500 = "JKS Java Key Store Private Keys (SHA1)"; static const char *HT_15600 = "Ethereum Wallet, PBKDF2-HMAC-SHA256"; @@ -707,7 +704,6 @@ static const char *SIGNATURE_CRAM_MD5_DOVECOT = "{CRAM-MD5}"; static const char *SIGNATURE_DCC2 = "$DCC2$"; static const char *SIGNATURE_DJANGOPBKDF2 = "pbkdf2_sha256"; static const char *SIGNATURE_DJANGOSHA1 = "sha1$"; -static const char *SIGNATURE_DPAPIMK = "$DPAPImk$"; static const char *SIGNATURE_DRUPAL7 = "$S$"; static const char *SIGNATURE_ECRYPTFS = "$ecryptfs$"; static const char *SIGNATURE_EPISERVER = "$episerver$"; @@ -2815,187 +2811,6 @@ int dcc2_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSE return (PARSER_OK); } -int dpapimk_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; - - dpapimk_t *dpapimk = (dpapimk_t *) hash_buf->esalt; - - token_t token; - - token.token_cnt = 10; - - token.signatures_cnt = 1; - token.signatures_buf[0] = SIGNATURE_DPAPIMK; - - // signature - token.len[0] = 9; - token.attr[0] = TOKEN_ATTR_FIXED_LENGTH - | TOKEN_ATTR_VERIFY_SIGNATURE; - - // version - token.len_min[1] = 1; - token.len_max[1] = 1; - token.sep[1] = '*'; - token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_DIGIT; - - // context - token.len_min[2] = 1; - token.len_max[2] = 1; - token.sep[2] = '*'; - token.attr[2] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_DIGIT; - - // sid - token.len_min[3] = 10; - token.len_max[3] = 60; - token.sep[3] = '*'; - token.attr[3] = TOKEN_ATTR_VERIFY_LENGTH; - - // cipher - token.len_min[4] = 4; - token.len_max[4] = 6; - token.sep[4] = '*'; - token.attr[4] = TOKEN_ATTR_VERIFY_LENGTH; - - // hash - token.len_min[5] = 4; - token.len_max[5] = 6; - token.sep[5] = '*'; - token.attr[5] = TOKEN_ATTR_VERIFY_LENGTH; - - // iterations - token.len_min[6] = 1; - token.len_max[6] = 6; - token.sep[6] = '*'; - token.attr[6] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_DIGIT; - - // iv - token.len_min[7] = 32; - token.len_max[7] = 32; - token.sep[7] = '*'; - token.attr[7] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_HEX; - - // content len - token.len_min[8] = 1; - token.len_max[8] = 6; - token.sep[8] = '*'; - token.attr[8] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_DIGIT; - - // content - token.len_min[9] = 0; - token.len_max[9] = 1024; - token.attr[9] = TOKEN_ATTR_VERIFY_LENGTH - | TOKEN_ATTR_VERIFY_HEX; - - const int rc_tokenizer = input_tokenizer (input_buf, input_len, &token); - - if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); - - const u8 *version_pos = token.buf[1]; - const u8 *context_pos = token.buf[2]; - const u8 *SID_pos = token.buf[3]; - const u8 *rounds_pos = token.buf[6]; - const u8 *iv_pos = token.buf[7]; - const u8 *contents_len_pos = token.buf[8]; - const u8 *contents_pos = token.buf[9]; - - /** - * content verification - */ - - const int version = hc_strtoul ((const char *) version_pos, NULL, 10); - const int contents_len = hc_strtoul ((const char *) contents_len_pos, NULL, 10); - - if (version == 1) - { - if (contents_len != 208) return (PARSER_SALT_LENGTH); - } - else if (version == 2) - { - if (contents_len != 288) return (PARSER_SALT_LENGTH); - } - else - { - return (PARSER_SALT_VALUE); - } - - if (contents_len != token.len[9]) return (PARSER_SALT_LENGTH); - - dpapimk->contents_len = contents_len; - - dpapimk->context = hc_strtoul ((const char *) context_pos, NULL, 10); - - // division by 4 should be fine because contents_len is either 208 or 288 - - for (u32 i = 0; i < dpapimk->contents_len / 4; i++) - { - dpapimk->contents[i] = hex_to_u32 ((const u8 *) &contents_pos[i * 8]); - - dpapimk->contents[i] = byte_swap_32 (dpapimk->contents[i]); - } - - // SID - - const int SID_len = token.len[3]; - - u8 SID_utf16le[128] = { 0 }; - - for (int i = 0; i < SID_len; i++) - { - SID_utf16le[i * 2] = SID_pos[i]; - } - - /* Specific to DPAPI: needs trailing '\0' while computing hash */ - - dpapimk->SID_len = (SID_len + 1) * 2; - - SID_utf16le[dpapimk->SID_len] = 0x80; - - memcpy ((u8 *) dpapimk->SID, SID_utf16le, sizeof (SID_utf16le)); - - for (u32 i = 0; i < 32; i++) - { - dpapimk->SID[i] = byte_swap_32 (dpapimk->SID[i]); - } - - // iv - - dpapimk->iv[0] = hex_to_u32 ((const u8 *) &iv_pos[ 0]); - dpapimk->iv[1] = hex_to_u32 ((const u8 *) &iv_pos[ 8]); - dpapimk->iv[2] = hex_to_u32 ((const u8 *) &iv_pos[16]); - dpapimk->iv[3] = hex_to_u32 ((const u8 *) &iv_pos[24]); - - dpapimk->iv[0] = byte_swap_32 (dpapimk->iv[0]); - dpapimk->iv[1] = byte_swap_32 (dpapimk->iv[1]); - dpapimk->iv[2] = byte_swap_32 (dpapimk->iv[2]); - dpapimk->iv[3] = byte_swap_32 (dpapimk->iv[3]); - - digest[0] = dpapimk->iv[0]; - digest[1] = dpapimk->iv[1]; - digest[2] = dpapimk->iv[2]; - digest[3] = dpapimk->iv[3]; - - salt->salt_buf[0] = dpapimk->iv[0]; - salt->salt_buf[1] = dpapimk->iv[1]; - salt->salt_buf[2] = dpapimk->iv[2]; - salt->salt_buf[3] = dpapimk->iv[3]; - - salt->salt_len = 16; - - // iter - - salt->salt_iter = hc_strtoul ((const char *) rounds_pos, NULL, 10) - 1; - - return (PARSER_OK); -} - int psafe2_parse_hash (u8 *input_buf, u32 input_len, hash_t *hash_buf, MAYBE_UNUSED hashconfig_t *hashconfig) { u32 *digest = (u32 *) hash_buf->digest; @@ -16054,8 +15869,6 @@ void hashconfig_benchmark_defaults (hashcat_ctx_t *hashcat_ctx, salt_t *salt, vo break; case 15200: salt->salt_iter = ROUNDS_MYWALLETV2; break; - case 15300: salt->salt_iter = ROUNDS_DPAPIMK_V1; - break; case 15600: salt->salt_iter = ROUNDS_ETHEREUM_PBKDF2; break; case 15700: salt->salt_iter = 1; @@ -18429,91 +18242,6 @@ int ascii_digest (hashcat_ctx_t *hashcat_ctx, char *out_buf, const int out_size, snprintf (out_buf, out_size, "%s", hash_buf); } - else if (hash_mode == 15300) - { - dpapimk_t *dpapimks = (dpapimk_t *) esalts_buf; - - dpapimk_t *dpapimk = &dpapimks[digest_cur]; - - u32 version = 1; - u32 context = dpapimk->context; - u32 rounds = salt.salt_iter + 1; - u32 contents_len = dpapimk->contents_len; - u32 SID_len = dpapimk->SID_len; - u32 iv_len = 32; - - u8 cipher_algorithm[8] = { 0 }; - u8 hash_algorithm[8] = { 0 }; - u8 SID[512] = { 0 }; - u8* SID_tmp; - - u32 *ptr_SID = (u32 *) dpapimk->SID; - u32 *ptr_iv = (u32 *) dpapimk->iv; - u32 *ptr_contents = (u32 *) dpapimk->contents; - - u32 u32_iv[4]; - u8 iv[32 + 1]; - - // convert back SID - - SID_tmp = (u8 *) hcmalloc ((SID_len + 1) * sizeof(u8)); - - for (u32 i = 0; i < (SID_len / 4) + 1; i++) - { - u8 hex[8] = { 0 }; - u32_to_hex (byte_swap_32 (ptr_SID[i]), hex); - - for (u32 j = 0, k = 0; j < 8; j += 2, k++) - { - SID_tmp[i * 4 + k] = hex_to_u8 (&hex[j]); - } - } - // overwrite trailing 0x80 - SID_tmp[SID_len] = 0; - - for (u32 i = 0, j = 0 ; j < SID_len ; i++, j += 2) - { - SID[i] = SID_tmp[j]; - } - - hcfree(SID_tmp); - - for (u32 i = 0; i < iv_len / 8; i++) - { - u32_iv[i] = byte_swap_32 (ptr_iv[i]); - u32_to_hex (u32_iv[i], iv + i * 8); - } - iv[32] = 0; - - u32 u32_contents[36]; - u8 contents[288 + 1]; - - for (u32 i = 0; i < contents_len / 8; i++) - { - u32_contents[i] = byte_swap_32 (ptr_contents[i]); - u32_to_hex (u32_contents[i], contents + i * 8); - } - - contents[208] = 0; - - if (contents_len == 208) - { - memcpy(cipher_algorithm, "des3", strlen("des3")); - memcpy(hash_algorithm, "sha1", strlen("sha1")); - } - - snprintf (out_buf, out_size, "%s%u*%u*%s*%s*%s*%u*%s*%u*%s", - SIGNATURE_DPAPIMK, - version, - context, - SID, - cipher_algorithm, - hash_algorithm, - rounds, - iv, - contents_len, - contents); - } else if (hash_mode == 15500) { jks_sha1_t *jks_sha1s = (jks_sha1_t *) esalts_buf; @@ -23215,23 +22943,6 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) hashconfig->st_pass = ST_PASS_HASHCAT_PLAIN; break; - case 15300: hashconfig->hash_type = HASH_TYPE_DPAPIMK; - hashconfig->salt_type = SALT_TYPE_EMBEDDED; - hashconfig->attack_exec = ATTACK_EXEC_OUTSIDE_KERNEL; - hashconfig->opts_type = OPTS_TYPE_PT_GENERATE_LE; - hashconfig->kern_type = KERN_TYPE_DPAPIMK_V1; - hashconfig->dgst_size = DGST_SIZE_4_4; - hashconfig->parse_func = dpapimk_parse_hash; - hashconfig->opti_type = OPTI_TYPE_ZERO_BYTE - | OPTI_TYPE_SLOW_HASH_SIMD_LOOP; - hashconfig->dgst_pos0 = 0; - hashconfig->dgst_pos1 = 1; - hashconfig->dgst_pos2 = 2; - hashconfig->dgst_pos3 = 3; - hashconfig->st_hash = ST_HASH_15300; - hashconfig->st_pass = ST_PASS_HASHCAT_PLAIN; - break; - case 15400: hashconfig->hash_type = HASH_TYPE_CHACHA20; hashconfig->salt_type = SALT_TYPE_EMBEDDED; hashconfig->attack_exec = ATTACK_EXEC_INSIDE_KERNEL; @@ -23704,7 +23415,6 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) case 14600: hashconfig->esalt_size = sizeof (luks_t); break; case 14700: hashconfig->esalt_size = sizeof (itunes_backup_t); break; case 14800: hashconfig->esalt_size = sizeof (itunes_backup_t); break; - case 15300: hashconfig->esalt_size = sizeof (dpapimk_t); break; case 15400: hashconfig->esalt_size = sizeof (chacha20_t); break; case 15500: hashconfig->esalt_size = sizeof (jks_sha1_t); break; case 15600: hashconfig->esalt_size = sizeof (ethereum_pbkdf2_t); break; @@ -23811,7 +23521,6 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) case 14800: hashconfig->tmp_size = sizeof (pbkdf2_sha256_tmp_t); break; case 15100: hashconfig->tmp_size = sizeof (pbkdf1_sha1_tmp_t); break; case 15200: hashconfig->tmp_size = sizeof (mywallet_tmp_t); break; - case 15300: hashconfig->tmp_size = sizeof (dpapimk_tmp_v1_t); break; case 15600: hashconfig->tmp_size = sizeof (pbkdf2_sha256_tmp_t); break; case 15900: hashconfig->tmp_size = sizeof (dpapimk_tmp_v2_t); break; case 16200: hashconfig->tmp_size = sizeof (apple_secure_notes_tmp_t); break; @@ -24004,7 +23713,6 @@ u32 default_pw_max (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED co case 14800: pw_max = PW_MAX; break; case 14900: pw_max = 10; break; // Underlaying Skip32 fixed case 15100: pw_max = PW_MAX; break; - case 15300: pw_max = PW_MAX; break; case 15400: pw_max = 32; break; // Underlaying ChaCha20 fixed case 15600: pw_max = PW_MAX; break; case 15700: pw_max = PW_MAX; break; diff --git a/src/modules/module_15300.c b/src/modules/module_15300.c new file mode 100644 index 000000000..74be86bc3 --- /dev/null +++ b/src/modules/module_15300.c @@ -0,0 +1,435 @@ +/** + * Author......: See docs/credits.txt + * License.....: MIT + */ + +#include "common.h" +#include "types.h" +#include "modules.h" +#include "bitops.h" +#include "convert.h" +#include "shared.h" +#include "memory.h" + +static const u32 ATTACK_EXEC = ATTACK_EXEC_OUTSIDE_KERNEL; +static const u32 DGST_POS0 = 0; +static const u32 DGST_POS1 = 1; +static const u32 DGST_POS2 = 2; +static const u32 DGST_POS3 = 3; +static const u32 DGST_SIZE = DGST_SIZE_4_4; +static const u32 HASH_CATEGORY = HASH_CATEGORY_OS; +static const char *HASH_NAME = "DPAPI masterkey file v1"; +static const u32 HASH_TYPE = HASH_TYPE_GENERIC; +static const u64 KERN_TYPE = 15300; +static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE + | OPTI_TYPE_SLOW_HASH_SIMD_LOOP; +static const u64 OPTS_TYPE = OPTS_TYPE_STATE_BUFFER_LE + | OPTS_TYPE_PT_GENERATE_LE; +static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; +static const char *ST_PASS = "hashcat"; +static const char *ST_HASH = "$DPAPImk$1*1*S-15-21-466364039-425773974-453930460-1925*des3*sha1*24000*b038489dee5ad04e3e3cab4d957258b5*208*cb9b5b7d96a0d2a00305ca403d3fd9c47c561e35b4b2cf3aebfd1d3199a6481d56972be7ebd6c291b199e6f1c2ffaee91978706737e9b1209e6c7d3aa3d8c3c3e38ad1ccfa39400d62c2415961c17fd0bd6b0f7bbd49cc1de1a394e64b7237f56244238da8d37d78"; + +u32 module_attack_exec (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ATTACK_EXEC; } +u32 module_dgst_pos0 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS0; } +u32 module_dgst_pos1 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS1; } +u32 module_dgst_pos2 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS2; } +u32 module_dgst_pos3 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS3; } +u32 module_dgst_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_SIZE; } +u32 module_hash_category (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return HASH_CATEGORY; } +const char *module_hash_name (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return HASH_NAME; } +u32 module_hash_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return HASH_TYPE; } +u64 module_kern_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return KERN_TYPE; } +u32 module_opti_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return OPTI_TYPE; } +u64 module_opts_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return OPTS_TYPE; } +u32 module_salt_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return SALT_TYPE; } +const char *module_st_hash (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ST_HASH; } +const char *module_st_pass (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ST_PASS; } + +typedef struct dpapimk +{ + u32 context; + + u32 SID[32]; + u32 SID_len; + u32 SID_offset; + + /* here only for possible + forward compatibiliy + */ + // u8 cipher_algo[16]; + // u8 hash_algo[16]; + + u32 iv[4]; + u32 contents_len; + u32 contents[128]; + +} dpapimk_t; + +typedef struct dpapimk_tmp_v1 +{ + u32 ipad[5]; + u32 opad[5]; + u32 dgst[10]; + u32 out[10]; + + u32 userKey[5]; + +} dpapimk_tmp_v1_t; + +static const char *SIGNATURE_DPAPIMK = "$DPAPImk$"; + +u64 module_tmp_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) +{ + const u64 tmp_size = (const u64) sizeof (dpapimk_tmp_v1_t); + + return tmp_size; +} + +u64 module_esalt_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) +{ + const u64 esalt_size = (const u64) sizeof (dpapimk_t); + + return esalt_size; +} + +u32 module_pw_max (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) +{ + const u32 pw_max = PW_MAX; + + return pw_max; +} + +int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED void *digest_buf, MAYBE_UNUSED salt_t *salt, MAYBE_UNUSED void *esalt_buf, MAYBE_UNUSED hashinfo_t *hash_info, const char *line_buf, MAYBE_UNUSED const int line_len) +{ + u32 *digest = (u32 *) digest_buf; + + dpapimk_t *dpapimk = (dpapimk_t *) esalt_buf; + + token_t token; + + token.token_cnt = 10; + + token.signatures_cnt = 1; + token.signatures_buf[0] = SIGNATURE_DPAPIMK; + + // signature + token.len[0] = 9; + token.attr[0] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_SIGNATURE; + + // version + token.len_min[1] = 1; + token.len_max[1] = 1; + token.sep[1] = '*'; + token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // context + token.len_min[2] = 1; + token.len_max[2] = 1; + token.sep[2] = '*'; + token.attr[2] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // sid + token.len_min[3] = 10; + token.len_max[3] = 60; + token.sep[3] = '*'; + token.attr[3] = TOKEN_ATTR_VERIFY_LENGTH; + + // cipher + token.len_min[4] = 4; + token.len_max[4] = 6; + token.sep[4] = '*'; + token.attr[4] = TOKEN_ATTR_VERIFY_LENGTH; + + // hash + token.len_min[5] = 4; + token.len_max[5] = 6; + token.sep[5] = '*'; + token.attr[5] = TOKEN_ATTR_VERIFY_LENGTH; + + // iterations + token.len_min[6] = 1; + token.len_max[6] = 6; + token.sep[6] = '*'; + token.attr[6] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // iv + token.len_min[7] = 32; + token.len_max[7] = 32; + token.sep[7] = '*'; + token.attr[7] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_HEX; + + // content len + token.len_min[8] = 1; + token.len_max[8] = 6; + token.sep[8] = '*'; + token.attr[8] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // content + token.len_min[9] = 0; + token.len_max[9] = 1024; + token.attr[9] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_HEX; + + const int rc_tokenizer = input_tokenizer ((const u8 *) line_buf, line_len, &token); + + if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); + + const u8 *version_pos = token.buf[1]; + const u8 *context_pos = token.buf[2]; + const u8 *SID_pos = token.buf[3]; + const u8 *rounds_pos = token.buf[6]; + const u8 *iv_pos = token.buf[7]; + const u8 *contents_len_pos = token.buf[8]; + const u8 *contents_pos = token.buf[9]; + + /** + * content verification + */ + + const int version = hc_strtoul ((const char *) version_pos, NULL, 10); + const int contents_len = hc_strtoul ((const char *) contents_len_pos, NULL, 10); + + if (version == 1) + { + if (contents_len != 208) return (PARSER_SALT_LENGTH); + } + else if (version == 2) + { + if (contents_len != 288) return (PARSER_SALT_LENGTH); + } + else + { + return (PARSER_SALT_VALUE); + } + + if (contents_len != token.len[9]) return (PARSER_SALT_LENGTH); + + dpapimk->contents_len = contents_len; + + dpapimk->context = hc_strtoul ((const char *) context_pos, NULL, 10); + + // division by 4 should be fine because contents_len is either 208 or 288 + + for (u32 i = 0; i < dpapimk->contents_len / 4; i++) + { + dpapimk->contents[i] = hex_to_u32 ((const u8 *) &contents_pos[i * 8]); + + dpapimk->contents[i] = byte_swap_32 (dpapimk->contents[i]); + } + + // SID + + const int SID_len = token.len[3]; + + u8 SID_utf16le[128] = { 0 }; + + for (int i = 0; i < SID_len; i++) + { + SID_utf16le[i * 2] = SID_pos[i]; + } + + /* Specific to DPAPI: needs trailing '\0' while computing hash */ + + dpapimk->SID_len = (SID_len + 1) * 2; + + SID_utf16le[dpapimk->SID_len] = 0x80; + + memcpy ((u8 *) dpapimk->SID, SID_utf16le, sizeof (SID_utf16le)); + + for (u32 i = 0; i < 32; i++) + { + dpapimk->SID[i] = byte_swap_32 (dpapimk->SID[i]); + } + + // iv + + dpapimk->iv[0] = hex_to_u32 ((const u8 *) &iv_pos[ 0]); + dpapimk->iv[1] = hex_to_u32 ((const u8 *) &iv_pos[ 8]); + dpapimk->iv[2] = hex_to_u32 ((const u8 *) &iv_pos[16]); + dpapimk->iv[3] = hex_to_u32 ((const u8 *) &iv_pos[24]); + + dpapimk->iv[0] = byte_swap_32 (dpapimk->iv[0]); + dpapimk->iv[1] = byte_swap_32 (dpapimk->iv[1]); + dpapimk->iv[2] = byte_swap_32 (dpapimk->iv[2]); + dpapimk->iv[3] = byte_swap_32 (dpapimk->iv[3]); + + digest[0] = dpapimk->iv[0]; + digest[1] = dpapimk->iv[1]; + digest[2] = dpapimk->iv[2]; + digest[3] = dpapimk->iv[3]; + + salt->salt_buf[0] = dpapimk->iv[0]; + salt->salt_buf[1] = dpapimk->iv[1]; + salt->salt_buf[2] = dpapimk->iv[2]; + salt->salt_buf[3] = dpapimk->iv[3]; + + salt->salt_len = 16; + + // iter + + salt->salt_iter = hc_strtoul ((const char *) rounds_pos, NULL, 10) - 1; + + return (PARSER_OK); +} + +int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const void *digest_buf, MAYBE_UNUSED const salt_t *salt, MAYBE_UNUSED const void *esalt_buf, MAYBE_UNUSED const hashinfo_t *hash_info, char *line_buf, MAYBE_UNUSED const int line_size) +{ + const u32 *digest = (const u32 *) digest_buf; + + const dpapimk_t *dpapimk = (const dpapimk_t *) esalt_buf; + + u32 version = 1; + u32 context = dpapimk->context; + u32 rounds = salt->salt_iter + 1; + u32 contents_len = dpapimk->contents_len; + u32 SID_len = dpapimk->SID_len; + u32 iv_len = 32; + + u8 cipher_algorithm[8] = { 0 }; + u8 hash_algorithm[8] = { 0 }; + u8 SID[512] = { 0 }; + u8* SID_tmp; + + u32 *ptr_SID = (u32 *) dpapimk->SID; + u32 *ptr_iv = (u32 *) dpapimk->iv; + u32 *ptr_contents = (u32 *) dpapimk->contents; + + u32 u32_iv[4]; + u8 iv[32 + 1]; + + // convert back SID + + SID_tmp = (u8 *) hcmalloc ((SID_len + 1) * sizeof(u8)); + + for (u32 i = 0; i < (SID_len / 4) + 1; i++) + { + u8 hex[8] = { 0 }; + u32_to_hex (byte_swap_32 (ptr_SID[i]), hex); + + for (u32 j = 0, k = 0; j < 8; j += 2, k++) + { + SID_tmp[i * 4 + k] = hex_to_u8 (&hex[j]); + } + } + // overwrite trailing 0x80 + SID_tmp[SID_len] = 0; + + for (u32 i = 0, j = 0 ; j < SID_len ; i++, j += 2) + { + SID[i] = SID_tmp[j]; + } + + hcfree(SID_tmp); + + for (u32 i = 0; i < iv_len / 8; i++) + { + u32_iv[i] = byte_swap_32 (ptr_iv[i]); + u32_to_hex (u32_iv[i], iv + i * 8); + } + iv[32] = 0; + + u32 u32_contents[36]; + u8 contents[288 + 1]; + + for (u32 i = 0; i < contents_len / 8; i++) + { + u32_contents[i] = byte_swap_32 (ptr_contents[i]); + u32_to_hex (u32_contents[i], contents + i * 8); + } + + contents[208] = 0; + + if (contents_len == 208) + { + memcpy (cipher_algorithm, "des3", strlen("des3")); + memcpy (hash_algorithm, "sha1", strlen("sha1")); + } + + const int line_len = snprintf (line_buf, line_size, "%s%u*%u*%s*%s*%s*%u*%s*%u*%s", + SIGNATURE_DPAPIMK, + version, + context, + SID, + cipher_algorithm, + hash_algorithm, + rounds, + iv, + contents_len, + contents); + + return line_len; +} + +void module_init (module_ctx_t *module_ctx) +{ + module_ctx->module_context_size = MODULE_CONTEXT_SIZE_CURRENT; + module_ctx->module_interface_version = MODULE_INTERFACE_VERSION_CURRENT; + + module_ctx->module_attack_exec = module_attack_exec; + module_ctx->module_benchmark_esalt = MODULE_DEFAULT; + module_ctx->module_benchmark_hook_salt = MODULE_DEFAULT; + module_ctx->module_benchmark_mask = MODULE_DEFAULT; + module_ctx->module_benchmark_salt = MODULE_DEFAULT; + module_ctx->module_build_plain_postprocess = MODULE_DEFAULT; + module_ctx->module_deep_comp_kernel = MODULE_DEFAULT; + module_ctx->module_dgst_pos0 = module_dgst_pos0; + module_ctx->module_dgst_pos1 = module_dgst_pos1; + module_ctx->module_dgst_pos2 = module_dgst_pos2; + module_ctx->module_dgst_pos3 = module_dgst_pos3; + module_ctx->module_dgst_size = module_dgst_size; + module_ctx->module_dictstat_disable = MODULE_DEFAULT; + module_ctx->module_esalt_size = module_esalt_size; + module_ctx->module_extra_buffer_size = MODULE_DEFAULT; + module_ctx->module_forced_outfile_format = MODULE_DEFAULT; + module_ctx->module_hash_binary_count = MODULE_DEFAULT; + module_ctx->module_hash_binary_parse = MODULE_DEFAULT; + module_ctx->module_hash_binary_save = MODULE_DEFAULT; + module_ctx->module_hash_binary_verify = MODULE_DEFAULT; + module_ctx->module_hash_decode_outfile = MODULE_DEFAULT; + module_ctx->module_hash_decode_zero_hash = MODULE_DEFAULT; + module_ctx->module_hash_decode = module_hash_decode; + module_ctx->module_hash_encode_status = MODULE_DEFAULT; + module_ctx->module_hash_encode = module_hash_encode; + module_ctx->module_hash_init_selftest = MODULE_DEFAULT; + module_ctx->module_hash_mode = MODULE_DEFAULT; + module_ctx->module_hash_category = module_hash_category; + module_ctx->module_hash_name = module_hash_name; + module_ctx->module_hash_type = module_hash_type; + module_ctx->module_hlfmt_disable = MODULE_DEFAULT; + module_ctx->module_hook12 = MODULE_DEFAULT; + module_ctx->module_hook23 = MODULE_DEFAULT; + module_ctx->module_hook_salt_size = MODULE_DEFAULT; + module_ctx->module_hook_size = MODULE_DEFAULT; + module_ctx->module_jit_build_options = MODULE_DEFAULT; + module_ctx->module_kernel_accel_max = MODULE_DEFAULT; + module_ctx->module_kernel_accel_min = MODULE_DEFAULT; + module_ctx->module_kernel_loops_max = MODULE_DEFAULT; + module_ctx->module_kernel_loops_min = MODULE_DEFAULT; + module_ctx->module_kernel_threads_max = MODULE_DEFAULT; + module_ctx->module_kernel_threads_min = MODULE_DEFAULT; + module_ctx->module_kern_type = module_kern_type; + module_ctx->module_opti_type = module_opti_type; + module_ctx->module_opts_type = module_opts_type; + module_ctx->module_outfile_check_disable = MODULE_DEFAULT; + module_ctx->module_outfile_check_nocomp = MODULE_DEFAULT; + module_ctx->module_potfile_disable = MODULE_DEFAULT; + module_ctx->module_potfile_keep_all_hashes = MODULE_DEFAULT; + module_ctx->module_pwdump_column = MODULE_DEFAULT; + module_ctx->module_pw_max = module_pw_max; + module_ctx->module_pw_min = MODULE_DEFAULT; + module_ctx->module_salt_max = MODULE_DEFAULT; + module_ctx->module_salt_min = MODULE_DEFAULT; + module_ctx->module_salt_type = module_salt_type; + module_ctx->module_separator = MODULE_DEFAULT; + module_ctx->module_st_hash = module_st_hash; + module_ctx->module_st_pass = module_st_pass; + module_ctx->module_tmp_size = module_tmp_size; + module_ctx->module_unstable_warning = MODULE_DEFAULT; + module_ctx->module_warmup_disable = MODULE_DEFAULT; +} diff --git a/tools/test_modules/m15300.pm b/tools/test_modules/m15300.pm new file mode 100644 index 000000000..917bc12f7 --- /dev/null +++ b/tools/test_modules/m15300.pm @@ -0,0 +1,411 @@ +#!/usr/bin/env perl + +## +## Author......: See docs/credits.txt +## License.....: MIT +## + +use strict; +use warnings; + +use Encode; +use Digest::MD4 qw (md4); +use Digest::SHA qw (sha1 hmac_sha1); +use Crypt::ECB qw (encrypt); + +sub module_constraints { [[0, 255], [-1, -1], [0, 27], [-1, -1], [-1, -1]] } + +sub get_random_dpapimk_salt +{ + my $version = shift; + + my $salt_buf = ""; + + my $context = random_number (1, 2); + + my $cipher_algo = ""; + + my $hash_algo = ""; + + my $iterations; + + my $SID = sprintf ('S-15-21-%d-%d-%d-%d', + random_number (400000000,490000000), + random_number (400000000,490000000), + random_number (400000000,490000000), + random_number (1000,1999)); + + my $cipher_len = 0; + + if ($version == 1) + { + $iterations = random_number (4000, 24000); + + $cipher_algo = "des3"; + + $hash_algo = "sha1"; + + $cipher_len = 208; + } + elsif ($version == 2) + { + $iterations = random_number (8000, 17000); + + $cipher_algo = "aes256"; + + $hash_algo = "sha512"; + + $cipher_len = 288; + } + + my $iv = random_bytes (16); + $iv = unpack ("H*", $iv); + + $salt_buf = $version . '*' . + $context . '*' . + $SID . '*' . + $cipher_algo . '*' . + $hash_algo . '*' . + $iterations . '*' . + $iv . '*' . + $cipher_len . '*'; + + return $salt_buf; +} + +#Thanks to Jochen Hoenicke +# (one of the authors of Palm Keyring) +# for these next two subs. +sub dpapi_pbkdf2 +{ + my ($password, $salt, $iter, $keylen, $prf) = @_; + my ($k, $t, $u, $ui, $i); + $t = ""; + for ($k = 1; length ($t) < $keylen; $k++) + { + $u = $ui = &$prf ($salt.pack ('N', $k), $password); + for ($i = 1; $i < $iter; $i++) + { + # modification to fit Microsoft + # weird pbkdf2 implementation... + $ui = &$prf ($u, $password); + $u ^= $ui; + } + $t .= $u; + } + return substr ($t, 0, $keylen); +} + +sub module_generate_hash +{ + my $word_buf = shift; + my $salt_buf = shift; + my $dpapimk_salt = shift // get_random_dpapimk_salt (1); + my $cipher = shift; + + my @salt_arr = split ('\*', $dpapimk_salt); + + my $version = $salt_arr[0]; + my $context = $salt_arr[1]; + my $SID = $salt_arr[2]; + my $cipher_algorithm = $salt_arr[3]; + my $hash_algorithm = $salt_arr[4]; + my $iterations = $salt_arr[5]; + my $salt = pack ("H*", $salt_arr[6]); + my $cipher_len = $salt_arr[7]; + + # intermediate values + + my $user_hash; + my $user_derivationKey; + my $encKey; + my $expected_hmac; + my $cleartext; + + if ($context == 1) + { + $user_hash = sha1 (encode ("UTF-16LE", $word_buf)); + } + elsif ($context == 2) + { + $user_hash = md4 (encode ("UTF-16LE", $word_buf)); + } + + $user_derivationKey = hmac_sha1 (encode ("UTF-16LE", $SID . "\x00"), $user_hash); + + my $hmacSalt = random_bytes (16); + my $last_key = random_bytes (64); + + if ($version == 1) + { + $encKey = hmac_sha1 ($hmacSalt, $user_derivationKey); + $expected_hmac = hmac_sha1 ($last_key, $encKey); + + # need padding because keyLen is 24 and hashLen 20 + $expected_hmac = $expected_hmac . random_bytes (4); + } + elsif ($version == 2) + { + $encKey = hmac_sha512 ($hmacSalt, $user_derivationKey); + $expected_hmac = hmac_sha512 ($last_key, $encKey); + } + + $cleartext = $hmacSalt . $expected_hmac . $last_key; + + my $derived_key; + my $key; + my $iv; + + my $pbkdf2; + + if ($version == 1) + { + $derived_key = dpapi_pbkdf2 ($user_derivationKey, $salt, $iterations, 32, \&hmac_sha1); + } + elsif ($version == 2) + { + $derived_key = dpapi_pbkdf2 ($user_derivationKey, $salt, $iterations, 48, \&hmac_sha512); + } + + if (defined $cipher) + { + $cipher = pack ("H*", $cipher); + + my $computed_hmac = ""; + + if ($version == 1) + { + $key = substr ($derived_key, 0, 24); + $iv = substr ($derived_key, 24, 8); + + my $p1 = Crypt::ECB->new ({ + key => substr ($key, 0, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p2 = Crypt::ECB->new ({ + key => substr ($key, 8, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p3 = Crypt::ECB->new ({ + key => substr ($key, 16, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + # let's compute a 3DES-EDE-CBC decryption + + my $out1; + my $out2; + my $out3; + my $expected_cleartext = ""; + + # size of cipherlen is 104 bytes + for (my $k = 0; $k < 13; $k++) + { + $out1 = $p3->decrypt (substr ($cipher, $k * 8, 8)); + $out2 = $p2->encrypt ($out1); + $out3 = $p1->decrypt ($out2); + + $expected_cleartext .= substr ($out3, 0, 8) ^ $iv; + + $iv = substr ($cipher, $k * 8, 8); + } + + $last_key = substr ($expected_cleartext, length ($expected_cleartext) - 64, 64); + $hmacSalt = substr ($expected_cleartext, 0, 16); + $expected_hmac = substr ($expected_cleartext, 16, 20); + + $encKey = hmac_sha1 ($hmacSalt, $user_derivationKey); + $computed_hmac = hmac_sha1 ($last_key, $encKey); + + $cleartext = $expected_cleartext; + + if (unpack ("H*", $expected_hmac) ne unpack ("H*", $computed_hmac)) + { + $cleartext = "0" x 104; + } + } + elsif ($version == 2) + { + $key = substr ($derived_key, 0, 32); + $iv = substr ($derived_key, 32, 16); + + my $aes = Crypt::CBC->new ({ + key => $key, + cipher => "Crypt::Rijndael", + iv => $iv, + literal_key => 1, + header => "none", + keysize => 32, + padding => "null", + }); + + my $expected_cleartext = $aes->decrypt ($cipher); + + $last_key = substr ($expected_cleartext, length ($expected_cleartext) - 64, 64); + $hmacSalt = substr ($expected_cleartext, 0, 16); + $expected_hmac = substr ($expected_cleartext, 16, 64); + + $encKey = hmac_sha512 ($hmacSalt, $user_derivationKey); + $computed_hmac = hmac_sha512 ($last_key, $encKey); + + $cleartext = $expected_cleartext; + + if (unpack ("H*", $expected_hmac) ne unpack ("H*", $computed_hmac)) + { + $cleartext = "0" x 144; + } + } + } + + if ($version == 1) + { + $key = substr ($derived_key, 0, 24); + $iv = substr ($derived_key, 24, 8); + + my $p1 = Crypt::ECB->new ({ + key => substr ($key, 0, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p2 = Crypt::ECB->new ({ + key => substr ($key, 8, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p3 = Crypt::ECB->new ({ + key => substr ($key, 16, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + # let's compute a 3DES-EDE-CBC encryption + + # compute first block + my $out1 = $p1->encrypt (substr ($cleartext, 0, 8) ^ $iv); + my $out2 = $p2->decrypt ($out1); + my $out3 = $p3->encrypt ($out2); + + $cipher = substr ($out3, 0, 8); + + # size of cipherlen is 104 bytes + for (my $k = 1; $k < 13; $k++) + { + $iv = $out3; + + $out1 = $p1->encrypt (substr ($cleartext, $k * 8, 8) ^ $iv); + $out2 = $p2->decrypt ($out1); + $out3 = $p3->encrypt ($out2); + + $cipher .= substr ($out3, 0, 8); + } + } + else + { + $key = substr ($derived_key, 0, 32); + $iv = substr ($derived_key, 32, 16); + + my $aes = Crypt::CBC->new ({ + key => $key, + cipher => "Crypt::Rijndael", + iv => $iv, + literal_key => 1, + header => "none", + keysize => 32, + padding => "null", + }); + + $cipher = $aes->encrypt ($cleartext); + } + + my $tmp_hash = sprintf ('$DPAPImk$%d*%d*%s*%s*%s*%d*%s*%d*%s', + $version, + $context, + $SID, + $cipher_algorithm, + $hash_algorithm, + $iterations, + unpack ("H*", $salt), + $cipher_len, + unpack ("H*", $cipher)); + + return $tmp_hash; +} + +sub module_verify_hash +{ + my $line = shift; + + my ($hash, $word) = split (':', $line); + + return unless defined $hash; + return unless defined $word; + + my @tmp_data = split ('\$', $hash); + + my $signature = $tmp_data[1]; + + next unless ($signature eq 'DPAPImk'); + + my @data = split ('\*', $tmp_data[2]); + + next unless (scalar @data == 9); + + my $version = shift @data; + + next unless ($version == 1 || $version == 2); + + my $context = shift @data; + my $SID = shift @data; + my $cipher_algorithm = shift @data; + my $hash_algorithm = shift @data; + my $iteration = shift @data; + my $iv = shift @data; + my $cipher_len = shift @data; + my $cipher = shift @data; + + next unless (length ($cipher) == $cipher_len); + + if ($version == 1) + { + next unless ($cipher_len == 208); + } + elsif ($version == 2) + { + next unless ($cipher_len == 288); + } + + my $dpapimk_salt = substr ($hash, length ('$DPAPImk$')); + + my $word_packed = pack_if_HEX_notation ($word); + + my $new_hash = module_generate_hash ($word_packed, undef, $dpapimk_salt, $cipher); + + return ($new_hash, $word); +} + +1;