diff --git a/OpenCL/m26610-pure.cl b/OpenCL/m26610-pure.cl index b6743bc72..b8258ec52 100644 --- a/OpenCL/m26610-pure.cl +++ b/OpenCL/m26610-pure.cl @@ -335,7 +335,6 @@ KERNEL_FQ void m26610_comp (KERN_ATTR_TMPS_ESALT (pbkdf2_sha256_tmp_t, pbkdf2_sh AES_GCM_Init (ukey, key_len, key, subKey, s_te0, s_te1, s_te2, s_te3, s_te4); // iv - const u32 iv[4] = { esalt_bufs[DIGESTS_OFFSET_HOST].iv_buf[0], esalt_bufs[DIGESTS_OFFSET_HOST].iv_buf[1], @@ -349,33 +348,21 @@ KERNEL_FQ void m26610_comp (KERN_ATTR_TMPS_ESALT (pbkdf2_sha256_tmp_t, pbkdf2_sh AES_GCM_Prepare_J0 (iv, iv_len, subKey, J0); - // ct - - //u32 T[4] = { 0 }; + //ct u32 ct[4] = { esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[0], esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[1], esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[2], esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[3] - }; + }; u32 pt[4] = { 0 }; - //u32 S_len = 16; - //u32 aad_buf[4] = { 0 }; - //u32 aad_len = 0; - - //AES_GCM_GHASH_GLOBAL (subKey, aad_buf, aad_len, esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf, esalt_bufs[DIGESTS_OFFSET_HOST].ct_len, S); - - //AES_GCM_GCTR (key, J0, S, S_len, T, s_te0, s_te1, s_te2, s_te3, s_te4); - // we try to decrypt the ciphertext AES_GCM_inc32(J0); // the first ctr is used to compute the tag, only the second is used for decryption: https://en.wikipedia.org/wiki/Galois/Counter_Mode#/media/File:GCM-Galois_Counter_Mode_with_IV.svg AES_GCM_GCTR (key, J0, ct, 16, pt, s_te0, s_te1, s_te2, s_te3, s_te4); // decrypt the ciphertext - - if ((gid == 0) && (lid == 0)) printf ("pt[0]=%08x\n", pt[0]); // should be 5b7b2274 or [{"type" - + // if ((gid == 0) && (lid == 0)) printf ("pt[0]=%08x\n", pt[0]); // should be 5b7b2274 or [{"type" // cast plaintext buffer to byte such that we can do a byte per byte comparison PRIVATE_AS const u32 *u32OutBufPtr = (PRIVATE_AS u32 *) pt; @@ -389,11 +376,12 @@ KERNEL_FQ void m26610_comp (KERN_ATTR_TMPS_ESALT (pbkdf2_sha256_tmp_t, pbkdf2_sh for(int i=0;i<16;i++) { if(u8OutBufPtr[i] >=20 && u8OutBufPtr[i] <= 0x7e) { - if ((gid == 0) && (lid == 0)) printf("correct ASCII byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); + //if ((gid == 0) && (lid == 0)) printf("correct ASCII byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); } else { - if ((gid == 0) && (lid == 0)) printf("NOT correct! byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); + //if ((gid == 0) && (lid == 0)) printf("NOT correct! byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); correct = false; + break; } } @@ -405,23 +393,17 @@ KERNEL_FQ void m26610_comp (KERN_ATTR_TMPS_ESALT (pbkdf2_sha256_tmp_t, pbkdf2_sh esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[3], }; - - if ((gid == 0) && (lid == 0)) printf ("ct[0]=%08x\n", ct[0]); - if ((gid == 0) && (lid == 0)) printf ("ct[1]=%08x\n", ct[1]); - if ((gid == 0) && (lid == 0)) printf ("ct[2]=%08x\n", ct[2]); - if ((gid == 0) && (lid == 0)) printf ("ct[3]=%08x\n", ct[3]); - if ((gid == 0) && (lid == 0)) printf("here0\n"); - + //if ((gid == 0) && (lid == 0)) printf ("ct[0]=%08x\n", ct[0]); + //if ((gid == 0) && (lid == 0)) printf ("ct[1]=%08x\n", ct[1]); + //if ((gid == 0) && (lid == 0)) printf ("ct[2]=%08x\n", ct[2]); + //if ((gid == 0) && (lid == 0)) printf ("ct[3]=%08x\n", ct[3]); + if (correct) { - - if ((gid == 0) && (lid == 0)) printf("here1\n"); int digest_pos = find_hash (digest, DIGESTS_CNT, &digests_buf[DIGESTS_OFFSET_HOST]); if (digest_pos != -1) { - - if ((gid == 0) && (lid == 0)) printf("here2\n"); const u32 final_hash_pos = DIGESTS_OFFSET_HOST + digest_pos; if (hc_atomic_inc (&hashes_shown[final_hash_pos]) == 0) diff --git a/docs/changes.txt b/docs/changes.txt index d8eeb2ee6..c515097e1 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -22,6 +22,7 @@ - Added hash-mode: HMAC-RIPEMD160 (key = $salt) - Added hash-mode: bcrypt(sha256($pass)) - Added hash-mode: md5(md5($salt).md5(md5($pass))) +- Added hash-mode: metamask (26610) with plaintext check instead of checking tag (26600). No longer needs all data in hash. ## ## Performance diff --git a/src/modules/module_26610.c b/src/modules/module_26610.c index abc88309d..017cf3a21 100644 --- a/src/modules/module_26610.c +++ b/src/modules/module_26610.c @@ -18,7 +18,8 @@ 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_CRYPTOCURRENCY_WALLET; -static const char *HASH_NAME = "MetaMask Wallet short data"; +// 22610 generates a decryption key based on a password-guess and uses that to AES-GCM decrypt the data decrypts +static const char *HASH_NAME = "MetaMask Wallet (short hash, plaintext check)"; static const u64 KERN_TYPE = 26610; static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE | OPTI_TYPE_SLOW_HASH_SIMD_LOOP; @@ -26,6 +27,7 @@ static const u64 OPTS_TYPE = OPTS_TYPE_STOCK_MODULE | OPTS_TYPE_PT_GENERATE_LE; static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; static const char *ST_PASS = "hashcat1"; +// hash generated using with python3 tools/metamask2hashcat.py --vault tools/2hashcat_tests/metamask2hashcat.json --shortdata static const char *ST_HASH = "$metamask-short$jfGI3TXguhb8GPnKSXFrMzRk2NCEc131Gt5G3kZr5+s=$h+BoIf2CQ5BEjaIOShFE7g==$R95fzGt4UQ0uwrcrVYnIi4UcSlWn9wlmer+//526ZDwYAp50K82F1u1oacYcdjjhuEvbZnWk/uBG00UkgLLlOw=="; 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; } @@ -237,15 +239,7 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE digest[1] = (metamask->ct_buf[1]); digest[2] = (metamask->ct_buf[2]); digest[3] = (metamask->ct_buf[3]); - -printf ("ct_buf[0]=%08x\n", metamask->ct_buf[0]); -printf ("ct_buf[1]=%08x\n", metamask->ct_buf[1]); -printf ("ct_buf[2]=%08x\n", metamask->ct_buf[2]); -printf ("ct_buf[3]=%08x\n", metamask->ct_buf[3]); -printf ("digest[0]=%08x\n", digest[0]); -printf ("digest[1]=%08x\n", digest[1]); -printf ("digest[2]=%08x\n", digest[2]); -printf ("digest[3]=%08x\n", digest[3]); + return (PARSER_OK); } diff --git a/tools/2hashcat_tests/metamask2hashcat-test.py b/tools/2hashcat_tests/metamask2hashcat-test.py new file mode 100644 index 000000000..3ee031f05 --- /dev/null +++ b/tools/2hashcat_tests/metamask2hashcat-test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + +from base64 import b64decode +from hashlib import pbkdf2_hmac + +from Crypto.Cipher import AES + +#TODO perhaps load the vault from tools/2hashcat_tests/metamask2hashcat.json similar as in tools/metamask2hashcat.py +vault = {"data":"R95fzGt4UQ0uwrcrVYnIi4UcSlWn9wlmer+//526ZDwYAp50K82F1u1oacYcdjjhuEvbZnWk/uBG00UkgLLlO3WbINljqmu2QWdDEwjTgo/qWR6MU9d/82rxNiONHQE8UrZ8SV+htVr6XIB0ze3aCV0E+fwI93EeP79ZeDxuOEhuHoiYT0bHWMv5nA48AdluG4DbOo7SrDAWBVCBsEdXsOfYsS3/TIh0a/iFCMX4uhxY2824JwcWp4H36SFWyBYMZCJ3/U4DYFbbjWZtGRthoJlIik5BJq4FLu3Y1jEgza0AWlAvu4MKTEqrYSpUIghfxf1a1f+kPvxsHNq0as0kRwCXu09DObbdsiggbmeoBkxMZiFq0d9ar/3Gon0r3hfc3c124Wlivzbzu1JcZ3wURhLSsUS7b5cfG86aXHJkxmQDA5urBz6lw3bsIvlEUB2ErkQy/zD+cPwCG1Rs/WKt7KNh45lppCUkHccbf+xlpdc8OfUwj01Xp7BdH8LMR7Vx1C4hZCvSdtURVl0VaAMxHDX0MjRkwmqS","iv":"h+BoIf2CQ5BEjaIOShFE7g==","salt":"jfGI3TXguhb8GPnKSXFrMzRk2NCEc131Gt5G3kZr5+s="} +password = "hashcat1" + +salt = b64decode(vault["salt"]) +key = pbkdf2_hmac("sha256", password.encode(), salt, 10_000) + +iv = b64decode(vault["iv"]) +payload = b64decode(vault["data"]) +ciphertext = payload[:-16] +print(f"ciphertext.hex()={ciphertext.hex()[0:128]}") +tag = payload[-16:] + +cipher = AES.new(key, AES.MODE_GCM, nonce=iv) +plaintext = cipher.decrypt(ciphertext) + +print(plaintext.hex()[0:128]) +print(str(plaintext[0:128])) + +print() +try: + cipher.verify(tag) + print("The message is authentic:", plaintext.decode()) +except ValueError: + print("Key incorrect or message corrupted") +print() + +cipher = AES.new(key, AES.MODE_GCM, nonce=iv) +plaintext = cipher.decrypt(ciphertext[:32]) +print("Partially encrypted message (32 bytes):", plaintext.decode()) + +cipher = AES.new(key, AES.MODE_GCM, nonce=iv) +plaintext = cipher.decrypt(ciphertext[:450]) +print("Partially encrypted message (450 bytes):", plaintext.decode()) \ No newline at end of file diff --git a/tools/2hashcat_tests/metamask2hashcat.json b/tools/2hashcat_tests/metamask2hashcat.json new file mode 100644 index 000000000..08274a2c4 --- /dev/null +++ b/tools/2hashcat_tests/metamask2hashcat.json @@ -0,0 +1 @@ +{"data":"R95fzGt4UQ0uwrcrVYnIi4UcSlWn9wlmer+//526ZDwYAp50K82F1u1oacYcdjjhuEvbZnWk/uBG00UkgLLlO3WbINljqmu2QWdDEwjTgo/qWR6MU9d/82rxNiONHQE8UrZ8SV+htVr6XIB0ze3aCV0E+fwI93EeP79ZeDxuOEhuHoiYT0bHWMv5nA48AdluG4DbOo7SrDAWBVCBsEdXsOfYsS3/TIh0a/iFCMX4uhxY2824JwcWp4H36SFWyBYMZCJ3/U4DYFbbjWZtGRthoJlIik5BJq4FLu3Y1jEgza0AWlAvu4MKTEqrYSpUIghfxf1a1f+kPvxsHNq0as0kRwCXu09DObbdsiggbmeoBkxMZiFq0d9ar/3Gon0r3hfc3c124Wlivzbzu1JcZ3wURhLSsUS7b5cfG86aXHJkxmQDA5urBz6lw3bsIvlEUB2ErkQy/zD+cPwCG1Rs/WKt7KNh45lppCUkHccbf+xlpdc8OfUwj01Xp7BdH8LMR7Vx1C4hZCvSdtURVl0VaAMxHDX0MjRkwmqS","iv":"h+BoIf2CQ5BEjaIOShFE7g==","salt":"jfGI3TXguhb8GPnKSXFrMzRk2NCEc131Gt5G3kZr5+s="} \ No newline at end of file diff --git a/tools/metamask2hashcat.py b/tools/metamask2hashcat.py index 5d286ffd2..df1ca0a41 100755 --- a/tools/metamask2hashcat.py +++ b/tools/metamask2hashcat.py @@ -11,8 +11,9 @@ import json import argparse +import base64 -def metamask_parser(file): +def metamask_parser(file, shortdata): try: f = open(file) @@ -23,7 +24,13 @@ def metamask_parser(file): parser.print_help() exit(1) - print('$metamask$' + j['salt'] + '$' + j['iv'] + '$' + j['data']) + if((len(j['data']) > 3000) or shortdata): + print("! Data too long, we limit it to 64 bytes, this hash can only be used with m26610!") + data_bin = base64.b64decode(j['data']) + j['data'] = base64.b64encode(data_bin[0:64]).decode("ascii") + print('$metamask-short$' + j['salt'] + '$' + j['iv'] + '$' + j['data']) + else: + print('$metamask$' + j['salt'] + '$' + j['iv'] + '$' + j['data']) except ValueError as e: parser.print_help() exit(1) @@ -33,10 +40,12 @@ def metamask_parser(file): if __name__ == "__main__": parser = argparse.ArgumentParser(description="metamask2hashcat.py extraction tool") parser.add_argument('--vault', required=True, help='set metamask vault (json) file from path', type=str) + parser.add_argument('--shortdata', help='force short data, can only be used with m26610, ', action='store_true') args = parser.parse_args() + if args.vault: - metamask_parser(args.vault) + metamask_parser(args.vault, args.shortdata) else: parser.print_help() exit(1)