From db2e7d1391f816307b388de56b59f20fd0fd3ede Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 20 Aug 2021 10:16:14 +0200 Subject: [PATCH] m25400 can now recover both a owner-password and user-password (when set) based on a pdf's o-value - added option to add the user-password to the pdf hash as an extra colum - added pdf test files, both with and without a owner- and user-password The main todo is to add a recovered user-password to back the hash that's written to the potfile. Currently I'm printing a recovered password as "(user password=...) after the recovered owner-password. Similair as the VC PIM is printed. However, this isn't most elegant. A secondary todo is to verify a recovered user-password based on the u-value, this could possibly simplify the check whether the recoverd password is a user-password or owner-password. --- OpenCL/m25400-pure.cl | 197 +++++++++++++----- src/modules/module_22000.c | 1 + src/modules/module_25400.c | 137 ++++++++---- tools/pdf_tests/_README.txt | 14 ++ tools/pdf_tests/owner.hash | 1 + tools/pdf_tests/owner.in | 1 + tools/pdf_tests/owner.pdf | Bin 0 -> 9081 bytes tools/pdf_tests/user-owner.hash | 1 + tools/pdf_tests/user-owner.pdf | Bin 0 -> 10041 bytes .../pdf_tests/user-owner_userpw-in-hash.hash | 1 + tools/pdf_tests/user-owner_userpw-in-hash.in | 1 + tools/pdf_tests/user.hash | 1 + tools/pdf_tests/user.pdf | Bin 0 -> 8617 bytes tools/pdf_tests/user_userpw-in-hash.hash | 1 + tools/pdf_tests/user_userpw-in-hash.in | 1 + tools/test_modules/m25400.pm | 65 +++--- 16 files changed, 300 insertions(+), 122 deletions(-) create mode 100644 tools/pdf_tests/_README.txt create mode 100644 tools/pdf_tests/owner.hash create mode 100644 tools/pdf_tests/owner.in create mode 100644 tools/pdf_tests/owner.pdf create mode 100644 tools/pdf_tests/user-owner.hash create mode 100644 tools/pdf_tests/user-owner.pdf create mode 100644 tools/pdf_tests/user-owner_userpw-in-hash.hash create mode 100644 tools/pdf_tests/user-owner_userpw-in-hash.in create mode 100644 tools/pdf_tests/user.hash create mode 100644 tools/pdf_tests/user.pdf create mode 100644 tools/pdf_tests/user_userpw-in-hash.hash create mode 100644 tools/pdf_tests/user_userpw-in-hash.in diff --git a/OpenCL/m25400-pure.cl b/OpenCL/m25400-pure.cl index d6c15f17a..d782e4eba 100644 --- a/OpenCL/m25400-pure.cl +++ b/OpenCL/m25400-pure.cl @@ -3,8 +3,7 @@ * License.....: MIT */ -// TODO use user password as input for md5 of o_digest if no owner password is set -// TODO dynamically add user password including padding to the RC4 input for the computation of the pdf o-value +// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf #ifdef KERNEL_STATIC #include "inc_vendor.h" @@ -20,22 +19,24 @@ typedef struct pdf { - int V; - int R; - int P; + int V; + int R; + int P; - int enc_md; + int enc_md; - u32 id_buf[8]; - u32 u_buf[32]; - u32 o_buf[32]; + u32 id_buf[8]; + u32 u_buf[32]; + u32 o_buf[32]; + u32 u_pass_buf[8]; - int id_len; - int o_len; - int u_len; + int id_len; + int o_len; + int u_len; + int u_pass_len; - u32 rc4key[2]; - u32 rc4data[2]; + u32 rc4key[2]; + u32 rc4data[2]; } pdf_t; @@ -89,9 +90,9 @@ KERNEL_FQ void m25400_init (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t)) * shared */ - u32 P = esalt_bufs[DIGESTS_OFFSET].P; + u32 P = esalt_bufs[DIGESTS_OFFSET].P; // TODO this is never used, but should be according according to "Algorithm 3.2 Computing an encryption key" line 4. - u32 id_buf[12]; + u32 id_buf[12]; // TODO this is never used, but should be according according to "Algorithm 3.2 Computing an encryption key" line 5. id_buf[ 0] = esalt_bufs[DIGESTS_OFFSET].id_buf[0]; id_buf[ 1] = esalt_bufs[DIGESTS_OFFSET].id_buf[1]; @@ -206,14 +207,12 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t)) */ u32 digest[4]; - digest[0] = tmps[gid].digest[0]; digest[1] = tmps[gid].digest[1]; digest[2] = tmps[gid].digest[2]; digest[3] = tmps[gid].digest[3]; u32 out[4]; - out[0] = tmps[gid].out[0]; out[1] = tmps[gid].out[1]; out[2] = tmps[gid].out[2]; @@ -223,6 +222,8 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t)) { if (j < 50) { + // the owner-key is generated by iterating a md5 hash 50 times + // see: "Algorithm 3.3 Computing the encryption dictionary’s O (owner password) value" u32 w0_t[4]; u32 w1_t[4]; u32 w2_t[4]; @@ -252,28 +253,41 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t)) md5_transform (w0_t, w1_t, w2_t, w3_t, digest); } - else - { - const u32 x = j - 50; - - const u32 xv = x << 0 - | x << 8 - | x << 16 - | x << 24; - - u32 tmp[4]; - - tmp[0] = digest[0] ^ xv; - tmp[1] = digest[1] ^ xv; - tmp[2] = digest[2] ^ xv; - tmp[3] = digest[3] ^ xv; - - rc4_init_128 (S, tmp); + } - rc4_next_16 (S, 0, 0, out, out); - } + out[0] = esalt_bufs[DIGESTS_OFFSET].o_buf[0]; // store original o-value in out (scratchpad) + out[1] = esalt_bufs[DIGESTS_OFFSET].o_buf[1]; + out[2] = esalt_bufs[DIGESTS_OFFSET].o_buf[2]; + out[3] = esalt_bufs[DIGESTS_OFFSET].o_buf[3]; + u32 o_rc4_decryption_key[4]; + o_rc4_decryption_key[0] = digest[0]; // store the owner-key + o_rc4_decryption_key[1] = digest[1]; + o_rc4_decryption_key[2] = digest[2]; + o_rc4_decryption_key[3] = digest[3]; + + // we decrypt the o-value to obtain either the owner-password (or user-password if no owner-password is set) + // see: "Algorithm 3.3 Computing the encryption dictionary’s O (owner password) value": "If there is no owner password, use the user password instead". + u32 tmp[4]; + for (u32 i = 19; i>0; i--) + { + // xor the iterator into the rc4 key + const u32 xv = i << 0 + | i << 8 + | i << 16 + | i << 24; + + tmp[0] = o_rc4_decryption_key[0] ^ xv; + tmp[1] = o_rc4_decryption_key[1] ^ xv; + tmp[2] = o_rc4_decryption_key[2] ^ xv; + tmp[3] = o_rc4_decryption_key[3] ^ xv; + + rc4_init_128 (S, tmp); + rc4_next_16 (S, 0, 0, out, out); } + rc4_init_128 (S, o_rc4_decryption_key); + rc4_next_16 (S, 0, 0, out, out); // output of the rc4 decrypt of the o-value should be the padded user-password + tmps[gid].digest[0] = digest[0]; tmps[gid].digest[1] = digest[1]; tmps[gid].digest[2] = digest[2]; @@ -287,28 +301,113 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t)) KERNEL_FQ void m25400_comp (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t)) { + const u32 digest[4] = + { + esalt_bufs[DIGESTS_OFFSET].o_buf[0], + esalt_bufs[DIGESTS_OFFSET].o_buf[1], + 0x0,// apparently only the first 16 bytes of the digest are used to look it up? + 0x0 // apparently only the first 16 bytes of the digest are used to look it up? + }; + + const u32 padding[8] = + { + 0x5e4ebf28, + 0x418a754e, + 0x564e0064, + 0x0801faff, + 0xb6002e2e, + 0x803e68d0, + 0xfea90c2f, + 0x7a695364 + }; + /** * modifier */ - const u64 gid = get_global_id (0); if (gid >= gid_max) return; const u64 lid = get_local_id (0); - /** - * digest - */ - - const u32 r0 = tmps[gid].out[0]; - const u32 r1 = tmps[gid].out[1]; - const u32 r2 = 0; - const u32 r3 = 0; #define il_pos 0 - #ifdef KERNEL_STATIC - #include COMPARE_M - #endif + + const u32 out[4] = + { + tmps[gid].out[0], + tmps[gid].out[1], + tmps[gid].out[2], + tmps[gid].out[3] + }; + + + // the best comparison I can think of is checking each byte + // whether it's a padding byte or ASCII, if so we're good, + // if not, decryption was not successful + bool correct = true; + int i_padding=0; + for(int i=0;i<16;i++) + { + // cast out buffer to byte such that we can do a byte per byte comparison + const u32 *u32OutBufPtr = out; + const u8 *u8OutBufPtr; + u8OutBufPtr = (u8*) u32OutBufPtr; + + // cast padding buffer to byte such that we can do a byte per byte comparison + const u32 *u32OutPadPtr = padding; + const u8 *u8OutPadPtr; + u8OutPadPtr = (u8*) u32OutPadPtr; + + // we don't use the user-password in the attack now (as we don't need it), + // however we could use it in the comparison of the decrypted o-value, + // yet it may make this attack a bit more fragile, as now we just check for ASCII + if((u8OutBufPtr[i] >=20 && u8OutBufPtr[i] <= 0x7e) || u8OutBufPtr[i]==u8OutPadPtr[i_padding]) + { + if(u8OutBufPtr[i]==u8OutPadPtr[i_padding]) { + //printf("correct padding byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); + i_padding=i_padding+1; + } + else + { + if(u8OutBufPtr[i] >=20 && u8OutBufPtr[i] <= 0x7e) { + //printf("correct ASCII byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); + } + } + } + else + { + //printf("wrong byte[%d]=0x%02x\n", i, u8OutBufPtr[i]); + // + //printf("u8OutBufPtr=0x"); + //for(int j=0;j<16;j++) { + // printf("%02x", u8OutBufPtr[j]); + //} + //printf("\n"); + // + //printf("u8OutPadPtr=0x"); + //for(int j=0;j<16;j++) { + // printf("%02x", u8OutPadPtr[j]); + //} + //printf("\n"); + + correct = false; + break; + } + } + + if (correct) + { + int digest_pos = find_hash (digest, digests_cnt, &digests_buf[DIGESTS_OFFSET]); + if (digest_pos != -1) + { + const u32 final_hash_pos = DIGESTS_OFFSET + digest_pos; + + if (hc_atomic_inc (&hashes_shown[final_hash_pos]) == 0) + { + mark_hash (plains_buf, d_return_buf, SALT_POS, digests_cnt, digest_pos, final_hash_pos, gid, il_pos, 0, 0); + } + } + } } diff --git a/src/modules/module_22000.c b/src/modules/module_22000.c index 2b5f60cfb..b5543f365 100644 --- a/src/modules/module_22000.c +++ b/src/modules/module_22000.c @@ -734,6 +734,7 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE const int rc_tokenizer = input_tokenizer ((const u8 *) line_buf, line_len, &token); + // if the tokenizer reports PARSER_OK, then modify the input line artificially to match the new input line format if (rc_tokenizer == PARSER_OK) { tmp_len = snprintf (tmp_buf, sizeof (tmp_buf), "WPA*01*%s***", line_buf); diff --git a/src/modules/module_25400.c b/src/modules/module_25400.c index 39ef9e50f..ec7675053 100644 --- a/src/modules/module_25400.c +++ b/src/modules/module_25400.c @@ -3,8 +3,7 @@ * License.....: MIT */ -// TODO use user password as input for md5 of o_digest if no owner password is set -// TODO dynamically add user password including padding to the RC4 input for the computation of the pdf o-value +// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf #include "common.h" #include "types.h" @@ -21,11 +20,11 @@ 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_DOCUMENTS; -static const char *HASH_NAME = "PDF 1.4 - 1.6 (Acrobat 5 - 8) - edit password"; +static const char *HASH_NAME = "PDF 1.4 - 1.6 (Acrobat 5 - 8) - user and owner password (o-value)"; static const u64 KERN_TYPE = 25400; static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE | OPTI_TYPE_NOT_ITERATED; -static const u64 OPTS_TYPE = OPTS_TYPE_PT_GENERATE_LE; +static const u64 OPTS_TYPE = OPTS_TYPE_PT_GENERATE_LE | OPTS_TYPE_COPY_TMPS | OPTS_TYPE_PT_ALWAYS_ASCII; static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; static const char *ST_PASS = "hashcat"; static const char *ST_HASH = "$pdf$2*3*128*-3904*1*16*631ed33746e50fba5caf56bcc39e09c6*32*5f9d0e4f0b39835dace0d306c40cd6b700000000000000000000000000000000*32*842103b0a0dc886db9223b94afe2d7cd63389079b61986a4fcf70095ad630c24"; @@ -47,23 +46,24 @@ const char *module_st_pass (MAYBE_UNUSED const hashconfig_t *hashconfig, typedef struct pdf { - int V; - int R; - int P; + int V; + int R; + int P; - int enc_md; + int enc_md; - u32 id_buf[8]; - u32 u_buf[32]; - u32 o_buf[32]; - u8 u_pass_buf[64]; + u32 id_buf[8]; + u32 u_buf[32]; + u32 o_buf[32]; + u32 u_pass_buf[8]; - int id_len; - int o_len; - int u_len; + int id_len; + int o_len; + int u_len; + int u_pass_len; - u32 rc4key[2]; - u32 rc4data[2]; + u32 rc4key[2]; + u32 rc4data[2]; } pdf_t; @@ -146,8 +146,7 @@ u64 module_tmp_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED c 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 = 32; // https://www.pdflib.com/knowledge-base/pdf-password-security/encryption/ - + const u32 pw_max = 32; // "truncate the password string to exactly 32 bytes." see "Algorithm 3.2 computing an encryption key" return pw_max; } @@ -251,9 +250,6 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE input_len = tmp_len; } - - //token_t token; should we first destroy token? - token.token_cnt = 13; token.signatures_cnt = 1; @@ -328,9 +324,8 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE token.attr[11] = TOKEN_ATTR_VERIFY_LENGTH | TOKEN_ATTR_VERIFY_HEX; - token.len_min[12] = 0; - token.len_max[12] = 64; // arbitrarily limit user-password length to 64 now + token.len_max[12] = 32; // "truncate the password string to exactly 32 bytes." see "Algorithm 3.2 computing an encryption key" token.sep[12] = '*'; token.attr[12] = TOKEN_ATTR_VERIFY_LENGTH; @@ -351,6 +346,10 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE const u8 *o_len_pos = token.buf[10]; const u8 *o_buf_pos = token.buf[11]; // owner hash const u8 *u_pass_buf_pos = token.buf[12]; // user password (optional) + // we don't use the user-password in the attack now (as we don't need it), + // however we could use it in the comparison of the decrypted o-value, + // yet it may make this attack a bit more fragile, as now we just check for ASCII + // validate data @@ -386,13 +385,12 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE } // copy data to esalt - pdf->V = V; pdf->R = R; pdf->P = P; - //printf("\n\n u_pass_buf_pos: %s\n\n", u_pass_buf_pos); - memcpy ( pdf->u_pass_buf , u_pass_buf_pos, 64); + memcpy ( pdf->u_pass_buf, u_pass_buf_pos, 32); + pdf->u_pass_len = strlen((char *) pdf->u_pass_buf); pdf->enc_md = enc_md; @@ -432,7 +430,6 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE pdf->o_len = o_len; // precompute rc4 data for later use - u32 padding[8] = { 0x5e4ebf28, @@ -446,7 +443,6 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE }; // md5 - u32 salt_pc_block[32] = { 0 }; u8 *salt_pc_ptr = (u8 *) salt_pc_block; @@ -462,7 +458,6 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE pdf->rc4data[1] = salt_pc_digest[1]; // we use ID for salt, maybe needs to change, we will see... - salt->salt_buf[0] = pdf->id_buf[0]; salt->salt_buf[1] = pdf->id_buf[1]; salt->salt_buf[2] = pdf->id_buf[2]; @@ -483,15 +478,81 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE 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 void *hook_salt_buf, MAYBE_UNUSED const hashinfo_t *hash_info, char *line_buf, MAYBE_UNUSED const int line_size) +int module_build_plain_postprocess (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const hashes_t *hashes, MAYBE_UNUSED const void *tmps, const u32 *src_buf, MAYBE_UNUSED const size_t src_sz, MAYBE_UNUSED const int src_len, u32 *dst_buf, MAYBE_UNUSED const size_t dst_sz) { - const pdf_t *pdf = (const pdf_t *) esalt_buf; + const u32 padding[8] = + { + 0x5e4ebf28, + 0x418a754e, + 0x564e0064, + 0x0801faff, + 0xb6002e2e, + 0x803e68d0, + 0xfea90c2f, + 0x7a695364 + }; + + pdf14_tmp_t *pdf_tmp = (pdf14_tmp_t *) tmps; + pdf_t *pdf = (pdf_t *) hashes->esalts_buf; + // if the password in tmp->out is equal to the padding, then we recovered just the owner-password + if(pdf_tmp->out[0]==padding[0] && pdf_tmp->out[1]==padding[1] && pdf_tmp->out[2]==padding[2] && pdf_tmp->out[3]==padding[3]) + { + return snprintf ((char *) dst_buf, dst_sz, "%s (user password not set)", (char *) src_buf); + } + + // cast out buffer to byte such that we can do a byte per byte comparison + u32 *u32OutBufPtr = pdf_tmp->out; + u8 *u8OutBufPtr; + u8OutBufPtr = (u8*) u32OutBufPtr; + + // cast padding buffer to byte such that we can do a byte per byte comparison + const u32 *u32OutPadPtr = padding; + const u8 *u8OutPadPtr; + u8OutPadPtr = (u8*) u32OutPadPtr; + + bool remove_padding=false; + int i_padding=0; + for(int i=0;i<16;i++) + { + if(u8OutBufPtr[i]==u8OutPadPtr[i_padding] || remove_padding) + { + u8OutBufPtr[i]=0x0; + remove_padding=true; + } + } + + // if the password in tmp->out is equal to the password tried, then we recovered just the owner-password or just the user-password + // we check whether we already have a user-password in the hash + // TODO would be better to actually also verify the u-value whether we've retrieved the correct user-password, + // however, we'd need to include a lot of code/complexity here to do so (or call into 10500 kernel). + // this seems relevant: run_kernel (hashcat_ctx, device_param, KERN_RUN_3, 0, 1, false, 0) + if(pdf_tmp->out[0]==src_buf[0] && pdf_tmp->out[1]==src_buf[1] && pdf_tmp->out[2]==src_buf[2] && pdf_tmp->out[3]==src_buf[3]) + { + if(pdf->u_pass_len==0) + { + // we seem to only have recovered the user-password as we don't have one yet + return snprintf ((char *) dst_buf, dst_sz, "(user password=%s)", (char *) src_buf); + } + } + // we recovered both the user-password and the owner-password + return snprintf ((char *) dst_buf, dst_sz, "%s (user password=%s)", (char *) src_buf, (char *) pdf_tmp->out); +} + + +// TODO how to add the recovered user-password to the hash? +// module_hash_encode() is called before module_build_plain_postprocess() is +// module_hash_encode() doesn't know the recovered password src_buf or the decrypted o-value pdf_tmp->out +// it seems a bit excessive to add these both to module_hash_encode()'s parameters +// module_build_plain_postprocess() cannot alter the hash nor hash access to the pdf/esalt object +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 void *hook_salt_buf, MAYBE_UNUSED const hashinfo_t *hash_info, char *line_buf, MAYBE_UNUSED const int line_size) +{ int line_len = 0; + pdf_t *pdf = (pdf_t *) esalt_buf; if (pdf->id_len == 32) { - line_len = snprintf (line_buf, line_size, "$pdf$%d*%d*%d*%d*%d*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x", + line_len = snprintf (line_buf, line_size, "$pdf$%d*%d*%d*%d*%d*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%s", pdf->V, pdf->R, 128, @@ -523,12 +584,13 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE byte_swap_32 (pdf->o_buf[4]), byte_swap_32 (pdf->o_buf[5]), byte_swap_32 (pdf->o_buf[6]), - byte_swap_32 (pdf->o_buf[7]) + byte_swap_32 (pdf->o_buf[7]), + (char *) pdf->u_pass_buf // TODO just prints the old hash now, we don't edit the hash to add a recovered user-password to it (yet) ); } else { - line_len = snprintf (line_buf, line_size, "$pdf$%d*%d*%d*%d*%d*%d*%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x", + line_len = snprintf (line_buf, line_size, "$pdf$%d*%d*%d*%d*%d*%d*%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%d*%08x%08x%08x%08x%08x%08x%08x%08x*%s", pdf->V, pdf->R, 128, @@ -556,7 +618,8 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE byte_swap_32 (pdf->o_buf[4]), byte_swap_32 (pdf->o_buf[5]), byte_swap_32 (pdf->o_buf[6]), - byte_swap_32 (pdf->o_buf[7]) + byte_swap_32 (pdf->o_buf[7]), + (char *) pdf->u_pass_buf // TODO just prints the old hash now, we don't edit the hash to add a recovered user-password to it (yet) ); } @@ -573,7 +636,7 @@ void module_init (module_ctx_t *module_ctx) 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_build_plain_postprocess = module_build_plain_postprocess; module_ctx->module_deep_comp_kernel = MODULE_DEFAULT; module_ctx->module_dgst_pos0 = module_dgst_pos0; module_ctx->module_dgst_pos1 = module_dgst_pos1; diff --git a/tools/pdf_tests/_README.txt b/tools/pdf_tests/_README.txt new file mode 100644 index 000000000..bfc17b7ff --- /dev/null +++ b/tools/pdf_tests/_README.txt @@ -0,0 +1,14 @@ +Test files for pdf kernels + +Didn't use hashcat as password as pdf files/a single pdf hash can +have two passwords, based on the u-value and o-value. +https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf + +The user-password (to open the file is): user +The owner-password (to e.g. restrict printing is): owner + +We have files with and without the "_userpw-in-hash" prefix, +this is to accommodate tests for 25400, which needs the user-password +to calculate the o-value. The user-password is used when no owner-password is set. + +The pdf files have been made with Adobe Acrobat Pro DC version 2015.007.20003 \ No newline at end of file diff --git a/tools/pdf_tests/owner.hash b/tools/pdf_tests/owner.hash new file mode 100644 index 000000000..8a54c9a91 --- /dev/null +++ b/tools/pdf_tests/owner.hash @@ -0,0 +1 @@ +$pdf$4*4*128*-3392*1*16*b438e5ce7c355548a28bf1408a36bbdf*32*63a1a7a95566adcf61a8c36002b6bfa900000000000000000000000000000000*32*566fa873ee33c797cd3b904fdadf814afa34df9a38f6ed41b984e2c6da2aa6f5 diff --git a/tools/pdf_tests/owner.in b/tools/pdf_tests/owner.in new file mode 100644 index 000000000..b0b50286d --- /dev/null +++ b/tools/pdf_tests/owner.in @@ -0,0 +1 @@ +$pdf$4*4*128*-3392*1*16*b438e5ce7c355548a28bf1408a36bbdf*32*63a1a7a95566adcf61a8c36002b6bfa900000000000000000000000000000000*32*566fa873ee33c797cd3b904fdadf814afa34df9a38f6ed41b984e2c6da2aa6f5:owner diff --git a/tools/pdf_tests/owner.pdf b/tools/pdf_tests/owner.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fe337fd660a3ce6a62776d69a95b6afac92a82db GIT binary patch literal 9081 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 bNXR`vH=0xjHu)cD`LFH`5D5IA{*C_w^94_B literal 0 HcmV?d00001 diff --git a/tools/pdf_tests/user-owner.hash b/tools/pdf_tests/user-owner.hash new file mode 100644 index 000000000..4aafd4110 --- /dev/null +++ b/tools/pdf_tests/user-owner.hash @@ -0,0 +1 @@ +$pdf$4*4*128*-3392*1*16*f72f279e0db2ee4ca76b0db6884e85cd*32*665d9c5a3c2443ac2aa3a9b855843bf200000000000000000000000000000000*32*0ba3835f88f90388e74e54584125ce142be0de24c6b0d37746e075b891756671 diff --git a/tools/pdf_tests/user-owner.pdf b/tools/pdf_tests/user-owner.pdf new file mode 100644 index 0000000000000000000000000000000000000000..20d6a94af4f30373bb5bab1fd26a9bc776139a80 GIT binary patch literal 10041 zcmd6NWl&tr)-Dhr1PQJ|26tu#861MUI|OHNXYk+-!QCOa1PBDz1b2r(@ZcJpV7cTy zIp;lfzqju9_wK6MyKDD!uV+2IR#)xS53RDeBrBK=f zt?Z%3&Q@MfGXNMU4*-KeoM52R(-SBGfPgr-fr?M>)d9TR96Ug2fF6L83j_dj@#+f+ zVLXAj!=x$xv0^%H!yb|2pl3)=IFfR`$NR%7GEy^Xr%gF%&^Fl27@?Y(7`1)r=`wm&cn!ZtMHecZ8el8z5BCg? zXB=@B1NDi#KV$yT=mIl#hIu$c%`s3x7^t+g5=xR7sDG$^Vw3Z4Hvfqj00aX31D^*3 zR0D88{>J3>d9j4#nP%iG%PjMiV@0=|*HduyNQ%n&O1IT^LZ{fFg)8elG%#UKeBqmv z(b7HDH~w6ViZxkob6@<9drMa{y-9V;JfkQw*FGg$%jPuy1Nh%7_9vjkB%hX84QBS& zQ^XZ!Dd7gShXKVMOqCp=_CPU71)zw8nkM_d;ewx#KCS&9r_^A^_GZS;X8-AB=K(4) zpcMoehW~sPzN4tFEkZkn$Go;e(^oU?F1hB~6z^*8#0XRduySzlvIA8CoKME>B>#or z>F)gRrUrw#ewMBPjaVg9L0OYVF#j3c|8+3}H36J|$Mx?;|KpPa6lQE@3^N9R{|W?U zV+*JYfa|aJU%?>u)Mx8p@%LCT=#9%}2UllP=rc6KmTgTnNHPst9Hv3tTO zrVrFsGO>o5!k)Ti?4A<$DOdrjfAOvA0DHA_knZ93)~rm|CKm|k3L?btzkRGksBWIIO(1x3<` zn<*aF!X^R10FK5##Lr*A{#X>Xg1IO|oy8pN93AX`yUO!>$8c~~b2K)E0>zyNir%>zW3S$mE{L(i;sqgfzc?8#r>f z_fxf`*CKai?=j%{;-(IC;9pmHt6KT(3w#MY^kVA#L3RfCF^`d?|4vRLc%$>yir@7O zdQ=tafQ*u!rm|c`j9D}SHCZ?H)kWN|6}mx`EHq3$#x#xYw+-vhTl?s{^cj_$u%bec z$zL^vt+p5u@WrH?G?nyJD{0GP>Aw>+=}urZtX#qNG|JP6(c(R~tI{K)98$ooHKqa|$w_|G-|Mm(XY%u5pKszTgjOxs zOy-^9%Qe}bKVOciiyFge6t{Yzr!o#+Q_s0BW}gLiFzO>to$t1lV08y}+P1!gR<&^I zwI}uycV(qF_l%T{hdvbAOK5-0| z@OsJjY}Svpq=q+>GtBv8cPv%z)O?1mZ*`oO(fhL)qpV*(fWkze*HxU0RjS3Wb$&lT zq2`!u1kQptwU`B0h18}}*Kb!zF+>M+S9;I)+A@|ukHF5qr3QZOz>11j&4-%H$ifTK zAgxpEEWt@JpcIs)C?>-@M19?ccVWm^Mj4+cc6djh*?y2!)nuYa32Z; z9f$Yx*kU@3H{9Pll?9qGIn{|HnW%E)SB(+C1!$HbSho||vo4$)Of;jb?a8=CjjF9q zUN)oIg}O4&V=~4$`xIYW4m?)5f9_gaTVcwvPBds$EFcKSx=cSIPmVo2zzE%P(3xe@ zeQu3FmK*wF$GHt>8$lE$d#uZ#wC)|s{P7$)k>`!*flcqaR_eNNv*={R971R+l>DrN zB7Y9M**7BZqB%L=Dw3*0^Vf!s_i-K6PLb+HoF%-nT3^5?HnwOSO`ecGgs!l&Kb7v| zZiOYkc6pZyK%FHjPI`0sc0>lAS~K$(}*fnr&u3vV;Z?4D*ZAlfl~v8n@tNFDFKr zY5IZD)^MN#xggA9?ln{!&3Qi*Fsz^67=VEG(q+<0q085vRN6uzHX3U5ZZfU6UWnAW zH8IitEdPpLaHZaN)2RY(Yr1}WcZq7jj>r_D$&H1JE}KPc;8S2J^VqZryYTU{Q9Iut zYazSFc+u+ynVcahn)#|2I8182Qv3X_hQ5W~M~4`Q#J(nO0u*sKNG;OlWQ%l}b#bXE z#!im1qsJU)ZDz;%m66Hl1Hq16^JKD5M!3H`(b`*1Kg8GFq0BJAVpXuZ)z^?S{hqIx z-C94T_|_14ydqp3lQ?snp6&0PhhN@47+bhtm8qDonF67rw{o(&b0)`E1$roFOQCs2 z7t`pDt)w5F6yEuU#(EpJ>;$ELraUZq*c%e1szq`fPW@5*?L_<;NdLMm*#gKPVx3Kk z4Rvadq;(qufVKOG7*6dw?Tx-_YMZS^mJOeOq_AeKk#dk&*rNNX*XvZ4w2|mQR_c-G zCxdTOGg(nz`}!Vu;GzB8o-TL5UHXu!JA1O@7oiY(W5_+b$_P1QAG}(klg_tRYwr5G z3=RAa#9wi4_`+0N2I@XWNiS>FyQe^N_Ju0e^pw`kP+yXpB^RHXk@htNyNc?!67v#A)GQ^1BBMrA_(k+PxLFv= zRiPvcF};l1i)RNc4I z-BVo3wi389QdMBgGJzj1j$`lWwUA8mI#_(;knU6ycF!(iAC>~2-zGz?GajyL3=&)v zG8)B;#iBDJ4HROjZO&sdCU&P`!GZCt{$vD z9Gef6T-<^j`@;gP>}HU*oc^lp%;BisZ*2Lr%#;0y`L0zR!>oMUEK$?mWhEE1fsJ~6 zCv>9eeduB)DG^sUYo0gcd3X*QT#w=s2c0QjWDpgTr4l5ar7tWiKkNI= z⪻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 literal 0 HcmV?d00001 diff --git a/tools/pdf_tests/user_userpw-in-hash.hash b/tools/pdf_tests/user_userpw-in-hash.hash new file mode 100644 index 000000000..57240c61e --- /dev/null +++ b/tools/pdf_tests/user_userpw-in-hash.hash @@ -0,0 +1 @@ +$pdf$4*4*128*-1028*1*16*e77b55355f74d54895c93063ddbad299*32*61536f41f6bf35bce77a96eb45cebec500000000000000000000000000000000*32*ce69e44fd4717582076b810914829e577152c3110ee581d65e53c64a4e8f578d*user diff --git a/tools/pdf_tests/user_userpw-in-hash.in b/tools/pdf_tests/user_userpw-in-hash.in new file mode 100644 index 000000000..660c2ade4 --- /dev/null +++ b/tools/pdf_tests/user_userpw-in-hash.in @@ -0,0 +1 @@ +$pdf$4*4*128*-1028*1*16*e77b55355f74d54895c93063ddbad299*32*61536f41f6bf35bce77a96eb45cebec500000000000000000000000000000000*32*ce69e44fd4717582076b810914829e577152c3110ee581d65e53c64a4e8f578d*user:user diff --git a/tools/test_modules/m25400.pm b/tools/test_modules/m25400.pm index 7b4b03363..a2d6206da 100644 --- a/tools/test_modules/m25400.pm +++ b/tools/test_modules/m25400.pm @@ -6,12 +6,9 @@ ## # based off m10500 but added the owner password part ($o) to be able to test the edit password -# two TODOs still (now only works if no user password is set): -# 1. TODO use user password as input for md5 of o_digest if no owner password is set -# 2. TODO dynamically add user password including padding to the RC4 input for the computation of the pdf o-value # easy test shortcut for debugging -# a=$(echo 1 | tools/test.pl passthrough 10500 | tail -n1); echo $a; echo 1 | ./hashcat --potfile-disable --runtime 400 --hwmon-disable -O -D 2 --backend-vector-width 4 -a 0 -m 10500 $a +# a=$(echo 1 | tools/test.pl passthrough 25400 | tail -n1); echo $a; echo 1 | ./hashcat --potfile-disable --runtime 400 --hwmon-disable -O -D 2 --backend-vector-width 4 -a 0 -m 25400 $a use strict; use warnings; @@ -99,9 +96,6 @@ sub pdf_compute_encryption_key_owner } } - #printf("\$o_digest = %s\n", unpack ("H*", $o_digest)); - - my $o_key; if ($R == 2) { @@ -111,7 +105,6 @@ sub pdf_compute_encryption_key_owner { $o_key = substr($o_digest, 0, 16); #length is always 128 bits or 16 bytes } - #printf("\$o_key = %s\n", unpack ("H*", $o_key)); return $o_key; } @@ -173,28 +166,31 @@ sub module_generate_hash ################ USER PASSWORD ################# # do not change $u if it exists, keep this the same, as we don't know the user password, # we cannot calculate this part of the hash again - my $res; - if("".$u_pass eq "") + + if ($u eq "0000000000000000000000000000000000000000000000000000000000000000") { + my $res; + if($u_pass eq "") + { + # we don't know the user-password so calculate $u based on the owner-password $res = pdf_compute_encryption_key_user($word, $padding, $id, $u, $o, $P, $V, $R, $enc); - } - else - { - #$u = pack("H*", $u) - #now that we know the user-password we can generate it + } + else + { + #we do know the user-password, so we can generate $u $res = pdf_compute_encryption_key_user($u_pass, $padding, $id, $u, $o, $P, $V, $R, $enc); - } + } - my $digest = md5 ($padding . pack ("H*", $id)); + my $digest = md5 ($padding . pack ("H*", $id)); - my $m = Crypt::RC4->new ($res); - $u = $m->RC4 ($digest); + my $m = Crypt::RC4->new ($res); + $u = $m->RC4 ($digest); - my @ress = split "", $res; + my @ress = split "", $res; - #do xor of rc4 19 times - for (my $x = 1; $x <= 19; $x++) - { + #do xor of rc4 19 times + for (my $x = 1; $x <= 19; $x++) + { my @xor; for (my $i = 0; $i < 16; $i++) @@ -207,13 +203,19 @@ sub module_generate_hash my $m2 = Crypt::RC4->new ($s); $u = $m2->RC4 ($u); + } + $u .= substr (pack ("H*", $u_save), 16, 16); + } + else + { + $u = pack("H*", $u) } - $u .= substr (pack ("H*", $u_save), 16, 16); ################ OWNER PASSWORD ################# my $o_key = pdf_compute_encryption_key_owner($word, $padding, $id, $u, $o, $P, $V, $R, $enc); + my $n = Crypt::RC4->new ($o_key); - if("".$u_pass eq "") + if($u_pass eq "") { $o = $n->RC4(substr ($padding, 0, 32 - length "")); } @@ -222,8 +224,6 @@ sub module_generate_hash #dynamically add user password including padding to the RC4 input for the computation of the pdf o-value $o = $n->RC4($u_pass.substr ($padding, 0, 32 - length $u_pass)); } - - #printf("padding_empty_str = %s\n", unpack ("H*", substr ($padding, 0, 32 - length ""))); my @ress2 = split "", $o_key; @@ -240,19 +240,14 @@ sub module_generate_hash } my $s = join ("", @xor); - - my $n2 = Crypt::RC4->new ($s); + my $n2 = Crypt::RC4->new ($s); $o = $n2->RC4 ($o); } } - #printf("\$u = %s\n", unpack ("H*", $u)); - #printf("\$o = %s\n", unpack ("H*", $o)); - #printf("\$u = %s\n", unpack ("H*", $u)); - my $hash; - if("".$u_pass eq "") + if($u_pass eq "") { $hash = sprintf ('$pdf$%d*%d*128*%d*%d*16*%s*32*%s*32*%s', $V, $R, $P, $enc, $id, unpack ("H*", $u), unpack ("H*", $o)); } @@ -260,7 +255,6 @@ sub module_generate_hash { $hash = sprintf ('$pdf$%d*%d*128*%d*%d*16*%s*32*%s*32*%s*%s', $V, $R, $P, $enc, $id, unpack ("H*", $u), unpack ("H*", $o), $u_pass); } - #print("hash\n".$hash."\n"); return $hash; } @@ -293,7 +287,6 @@ sub module_verify_hash my $u_pass = ""; if($i_data == 12) { $u_pass = shift @data; - #printf("u_pass = %s\n", $u_pass); } return unless defined $id;