mirror of
https://github.com/hashcat/hashcat.git
synced 2024-11-22 08:08:10 +00:00
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.
This commit is contained in:
parent
012c5b16cd
commit
db2e7d1391
@ -3,8 +3,7 @@
|
|||||||
* License.....: MIT
|
* License.....: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO use user password as input for md5 of o_digest if no owner password is set
|
// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
|
||||||
// TODO dynamically add user password including padding to the RC4 input for the computation of the pdf o-value
|
|
||||||
|
|
||||||
#ifdef KERNEL_STATIC
|
#ifdef KERNEL_STATIC
|
||||||
#include "inc_vendor.h"
|
#include "inc_vendor.h"
|
||||||
@ -29,10 +28,12 @@ typedef struct pdf
|
|||||||
u32 id_buf[8];
|
u32 id_buf[8];
|
||||||
u32 u_buf[32];
|
u32 u_buf[32];
|
||||||
u32 o_buf[32];
|
u32 o_buf[32];
|
||||||
|
u32 u_pass_buf[8];
|
||||||
|
|
||||||
int id_len;
|
int id_len;
|
||||||
int o_len;
|
int o_len;
|
||||||
int u_len;
|
int u_len;
|
||||||
|
int u_pass_len;
|
||||||
|
|
||||||
u32 rc4key[2];
|
u32 rc4key[2];
|
||||||
u32 rc4data[2];
|
u32 rc4data[2];
|
||||||
@ -89,9 +90,9 @@ KERNEL_FQ void m25400_init (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
|
|||||||
* shared
|
* 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[ 0] = esalt_bufs[DIGESTS_OFFSET].id_buf[0];
|
||||||
id_buf[ 1] = esalt_bufs[DIGESTS_OFFSET].id_buf[1];
|
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];
|
u32 digest[4];
|
||||||
|
|
||||||
digest[0] = tmps[gid].digest[0];
|
digest[0] = tmps[gid].digest[0];
|
||||||
digest[1] = tmps[gid].digest[1];
|
digest[1] = tmps[gid].digest[1];
|
||||||
digest[2] = tmps[gid].digest[2];
|
digest[2] = tmps[gid].digest[2];
|
||||||
digest[3] = tmps[gid].digest[3];
|
digest[3] = tmps[gid].digest[3];
|
||||||
|
|
||||||
u32 out[4];
|
u32 out[4];
|
||||||
|
|
||||||
out[0] = tmps[gid].out[0];
|
out[0] = tmps[gid].out[0];
|
||||||
out[1] = tmps[gid].out[1];
|
out[1] = tmps[gid].out[1];
|
||||||
out[2] = tmps[gid].out[2];
|
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)
|
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 w0_t[4];
|
||||||
u32 w1_t[4];
|
u32 w1_t[4];
|
||||||
u32 w2_t[4];
|
u32 w2_t[4];
|
||||||
@ -252,27 +253,40 @@ 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);
|
md5_transform (w0_t, w1_t, w2_t, w3_t, digest);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
const u32 x = j - 50;
|
|
||||||
|
|
||||||
const u32 xv = x << 0
|
out[0] = esalt_bufs[DIGESTS_OFFSET].o_buf[0]; // store original o-value in out (scratchpad)
|
||||||
| x << 8
|
out[1] = esalt_bufs[DIGESTS_OFFSET].o_buf[1];
|
||||||
| x << 16
|
out[2] = esalt_bufs[DIGESTS_OFFSET].o_buf[2];
|
||||||
| x << 24;
|
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];
|
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] = digest[0] ^ xv;
|
tmp[0] = o_rc4_decryption_key[0] ^ xv;
|
||||||
tmp[1] = digest[1] ^ xv;
|
tmp[1] = o_rc4_decryption_key[1] ^ xv;
|
||||||
tmp[2] = digest[2] ^ xv;
|
tmp[2] = o_rc4_decryption_key[2] ^ xv;
|
||||||
tmp[3] = digest[3] ^ xv;
|
tmp[3] = o_rc4_decryption_key[3] ^ xv;
|
||||||
|
|
||||||
rc4_init_128 (S, tmp);
|
rc4_init_128 (S, tmp);
|
||||||
|
|
||||||
rc4_next_16 (S, 0, 0, out, out);
|
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[0] = digest[0];
|
||||||
tmps[gid].digest[1] = digest[1];
|
tmps[gid].digest[1] = digest[1];
|
||||||
@ -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))
|
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
|
* modifier
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const u64 gid = get_global_id (0);
|
const u64 gid = get_global_id (0);
|
||||||
|
|
||||||
if (gid >= gid_max) return;
|
if (gid >= gid_max) return;
|
||||||
|
|
||||||
const u64 lid = get_local_id (0);
|
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
|
#define il_pos 0
|
||||||
|
|
||||||
#ifdef KERNEL_STATIC
|
|
||||||
#include COMPARE_M
|
const u32 out[4] =
|
||||||
#endif
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
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)
|
if (rc_tokenizer == PARSER_OK)
|
||||||
{
|
{
|
||||||
tmp_len = snprintf (tmp_buf, sizeof (tmp_buf), "WPA*01*%s***", line_buf);
|
tmp_len = snprintf (tmp_buf, sizeof (tmp_buf), "WPA*01*%s***", line_buf);
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
* License.....: MIT
|
* License.....: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO use user password as input for md5 of o_digest if no owner password is set
|
// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
|
||||||
// TODO dynamically add user password including padding to the RC4 input for the computation of the pdf o-value
|
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
@ -21,11 +20,11 @@ static const u32 DGST_POS2 = 2;
|
|||||||
static const u32 DGST_POS3 = 3;
|
static const u32 DGST_POS3 = 3;
|
||||||
static const u32 DGST_SIZE = DGST_SIZE_4_4;
|
static const u32 DGST_SIZE = DGST_SIZE_4_4;
|
||||||
static const u32 HASH_CATEGORY = HASH_CATEGORY_DOCUMENTS;
|
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 u64 KERN_TYPE = 25400;
|
||||||
static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE
|
static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE
|
||||||
| OPTI_TYPE_NOT_ITERATED;
|
| 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 u32 SALT_TYPE = SALT_TYPE_EMBEDDED;
|
||||||
static const char *ST_PASS = "hashcat";
|
static const char *ST_PASS = "hashcat";
|
||||||
static const char *ST_HASH = "$pdf$2*3*128*-3904*1*16*631ed33746e50fba5caf56bcc39e09c6*32*5f9d0e4f0b39835dace0d306c40cd6b700000000000000000000000000000000*32*842103b0a0dc886db9223b94afe2d7cd63389079b61986a4fcf70095ad630c24";
|
static const char *ST_HASH = "$pdf$2*3*128*-3904*1*16*631ed33746e50fba5caf56bcc39e09c6*32*5f9d0e4f0b39835dace0d306c40cd6b700000000000000000000000000000000*32*842103b0a0dc886db9223b94afe2d7cd63389079b61986a4fcf70095ad630c24";
|
||||||
@ -56,11 +55,12 @@ typedef struct pdf
|
|||||||
u32 id_buf[8];
|
u32 id_buf[8];
|
||||||
u32 u_buf[32];
|
u32 u_buf[32];
|
||||||
u32 o_buf[32];
|
u32 o_buf[32];
|
||||||
u8 u_pass_buf[64];
|
u32 u_pass_buf[8];
|
||||||
|
|
||||||
int id_len;
|
int id_len;
|
||||||
int o_len;
|
int o_len;
|
||||||
int u_len;
|
int u_len;
|
||||||
|
int u_pass_len;
|
||||||
|
|
||||||
u32 rc4key[2];
|
u32 rc4key[2];
|
||||||
u32 rc4data[2];
|
u32 rc4data[2];
|
||||||
@ -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)
|
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;
|
return pw_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,9 +250,6 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE
|
|||||||
input_len = tmp_len;
|
input_len = tmp_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//token_t token; should we first destroy token?
|
|
||||||
|
|
||||||
token.token_cnt = 13;
|
token.token_cnt = 13;
|
||||||
|
|
||||||
token.signatures_cnt = 1;
|
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[11] = TOKEN_ATTR_VERIFY_LENGTH
|
||||||
| TOKEN_ATTR_VERIFY_HEX;
|
| TOKEN_ATTR_VERIFY_HEX;
|
||||||
|
|
||||||
|
|
||||||
token.len_min[12] = 0;
|
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.sep[12] = '*';
|
||||||
token.attr[12] = TOKEN_ATTR_VERIFY_LENGTH;
|
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_len_pos = token.buf[10];
|
||||||
const u8 *o_buf_pos = token.buf[11]; // owner hash
|
const u8 *o_buf_pos = token.buf[11]; // owner hash
|
||||||
const u8 *u_pass_buf_pos = token.buf[12]; // user password (optional)
|
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
|
// validate data
|
||||||
|
|
||||||
@ -386,13 +385,12 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy data to esalt
|
// copy data to esalt
|
||||||
|
|
||||||
pdf->V = V;
|
pdf->V = V;
|
||||||
pdf->R = R;
|
pdf->R = R;
|
||||||
pdf->P = P;
|
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, 32);
|
||||||
memcpy ( pdf->u_pass_buf , u_pass_buf_pos, 64);
|
pdf->u_pass_len = strlen((char *) pdf->u_pass_buf);
|
||||||
|
|
||||||
pdf->enc_md = enc_md;
|
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;
|
pdf->o_len = o_len;
|
||||||
|
|
||||||
// precompute rc4 data for later use
|
// precompute rc4 data for later use
|
||||||
|
|
||||||
u32 padding[8] =
|
u32 padding[8] =
|
||||||
{
|
{
|
||||||
0x5e4ebf28,
|
0x5e4ebf28,
|
||||||
@ -446,7 +443,6 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE
|
|||||||
};
|
};
|
||||||
|
|
||||||
// md5
|
// md5
|
||||||
|
|
||||||
u32 salt_pc_block[32] = { 0 };
|
u32 salt_pc_block[32] = { 0 };
|
||||||
|
|
||||||
u8 *salt_pc_ptr = (u8 *) salt_pc_block;
|
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];
|
pdf->rc4data[1] = salt_pc_digest[1];
|
||||||
|
|
||||||
// we use ID for salt, maybe needs to change, we will see...
|
// we use ID for salt, maybe needs to change, we will see...
|
||||||
|
|
||||||
salt->salt_buf[0] = pdf->id_buf[0];
|
salt->salt_buf[0] = pdf->id_buf[0];
|
||||||
salt->salt_buf[1] = pdf->id_buf[1];
|
salt->salt_buf[1] = pdf->id_buf[1];
|
||||||
salt->salt_buf[2] = pdf->id_buf[2];
|
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);
|
return (PARSER_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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 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)
|
||||||
{
|
{
|
||||||
const pdf_t *pdf = (const pdf_t *) esalt_buf;
|
|
||||||
|
|
||||||
int line_len = 0;
|
int line_len = 0;
|
||||||
|
|
||||||
|
pdf_t *pdf = (pdf_t *) esalt_buf;
|
||||||
if (pdf->id_len == 32)
|
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->V,
|
||||||
pdf->R,
|
pdf->R,
|
||||||
128,
|
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[4]),
|
||||||
byte_swap_32 (pdf->o_buf[5]),
|
byte_swap_32 (pdf->o_buf[5]),
|
||||||
byte_swap_32 (pdf->o_buf[6]),
|
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
|
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->V,
|
||||||
pdf->R,
|
pdf->R,
|
||||||
128,
|
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[4]),
|
||||||
byte_swap_32 (pdf->o_buf[5]),
|
byte_swap_32 (pdf->o_buf[5]),
|
||||||
byte_swap_32 (pdf->o_buf[6]),
|
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_hook_salt = MODULE_DEFAULT;
|
||||||
module_ctx->module_benchmark_mask = MODULE_DEFAULT;
|
module_ctx->module_benchmark_mask = MODULE_DEFAULT;
|
||||||
module_ctx->module_benchmark_salt = 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_deep_comp_kernel = MODULE_DEFAULT;
|
||||||
module_ctx->module_dgst_pos0 = module_dgst_pos0;
|
module_ctx->module_dgst_pos0 = module_dgst_pos0;
|
||||||
module_ctx->module_dgst_pos1 = module_dgst_pos1;
|
module_ctx->module_dgst_pos1 = module_dgst_pos1;
|
||||||
|
14
tools/pdf_tests/_README.txt
Normal file
14
tools/pdf_tests/_README.txt
Normal file
@ -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
|
1
tools/pdf_tests/owner.hash
Normal file
1
tools/pdf_tests/owner.hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-3392*1*16*b438e5ce7c355548a28bf1408a36bbdf*32*63a1a7a95566adcf61a8c36002b6bfa900000000000000000000000000000000*32*566fa873ee33c797cd3b904fdadf814afa34df9a38f6ed41b984e2c6da2aa6f5
|
1
tools/pdf_tests/owner.in
Normal file
1
tools/pdf_tests/owner.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-3392*1*16*b438e5ce7c355548a28bf1408a36bbdf*32*63a1a7a95566adcf61a8c36002b6bfa900000000000000000000000000000000*32*566fa873ee33c797cd3b904fdadf814afa34df9a38f6ed41b984e2c6da2aa6f5:owner
|
BIN
tools/pdf_tests/owner.pdf
Normal file
BIN
tools/pdf_tests/owner.pdf
Normal file
Binary file not shown.
1
tools/pdf_tests/user-owner.hash
Normal file
1
tools/pdf_tests/user-owner.hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-3392*1*16*f72f279e0db2ee4ca76b0db6884e85cd*32*665d9c5a3c2443ac2aa3a9b855843bf200000000000000000000000000000000*32*0ba3835f88f90388e74e54584125ce142be0de24c6b0d37746e075b891756671
|
BIN
tools/pdf_tests/user-owner.pdf
Normal file
BIN
tools/pdf_tests/user-owner.pdf
Normal file
Binary file not shown.
1
tools/pdf_tests/user-owner_userpw-in-hash.hash
Normal file
1
tools/pdf_tests/user-owner_userpw-in-hash.hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-3392*1*16*f72f279e0db2ee4ca76b0db6884e85cd*32*665d9c5a3c2443ac2aa3a9b855843bf200000000000000000000000000000000*32*0ba3835f88f90388e74e54584125ce142be0de24c6b0d37746e075b891756671*user
|
1
tools/pdf_tests/user-owner_userpw-in-hash.in
Normal file
1
tools/pdf_tests/user-owner_userpw-in-hash.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-3392*1*16*f72f279e0db2ee4ca76b0db6884e85cd*32*665d9c5a3c2443ac2aa3a9b855843bf200000000000000000000000000000000*32*0ba3835f88f90388e74e54584125ce142be0de24c6b0d37746e075b891756671*user:owner
|
1
tools/pdf_tests/user.hash
Normal file
1
tools/pdf_tests/user.hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-1028*1*16*e77b55355f74d54895c93063ddbad299*32*61536f41f6bf35bce77a96eb45cebec500000000000000000000000000000000*32*ce69e44fd4717582076b810914829e577152c3110ee581d65e53c64a4e8f578d
|
BIN
tools/pdf_tests/user.pdf
Normal file
BIN
tools/pdf_tests/user.pdf
Normal file
Binary file not shown.
1
tools/pdf_tests/user_userpw-in-hash.hash
Normal file
1
tools/pdf_tests/user_userpw-in-hash.hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-1028*1*16*e77b55355f74d54895c93063ddbad299*32*61536f41f6bf35bce77a96eb45cebec500000000000000000000000000000000*32*ce69e44fd4717582076b810914829e577152c3110ee581d65e53c64a4e8f578d*user
|
1
tools/pdf_tests/user_userpw-in-hash.in
Normal file
1
tools/pdf_tests/user_userpw-in-hash.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
$pdf$4*4*128*-1028*1*16*e77b55355f74d54895c93063ddbad299*32*61536f41f6bf35bce77a96eb45cebec500000000000000000000000000000000*32*ce69e44fd4717582076b810914829e577152c3110ee581d65e53c64a4e8f578d*user:user
|
@ -6,12 +6,9 @@
|
|||||||
##
|
##
|
||||||
|
|
||||||
# based off m10500 but added the owner password part ($o) to be able to test the edit password
|
# 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
|
# 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 strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
@ -99,9 +96,6 @@ sub pdf_compute_encryption_key_owner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#printf("\$o_digest = %s\n", unpack ("H*", $o_digest));
|
|
||||||
|
|
||||||
|
|
||||||
my $o_key;
|
my $o_key;
|
||||||
if ($R == 2)
|
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
|
$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;
|
return $o_key;
|
||||||
}
|
}
|
||||||
@ -173,15 +166,18 @@ sub module_generate_hash
|
|||||||
################ USER PASSWORD #################
|
################ USER PASSWORD #################
|
||||||
# do not change $u if it exists, keep this the same, as we don't know the 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
|
# 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);
|
$res = pdf_compute_encryption_key_user($word, $padding, $id, $u, $o, $P, $V, $R, $enc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#$u = pack("H*", $u)
|
#we do know the user-password, so we can generate $u
|
||||||
#now that we know the user-password we can generate it
|
|
||||||
$res = pdf_compute_encryption_key_user($u_pass, $padding, $id, $u, $o, $P, $V, $R, $enc);
|
$res = pdf_compute_encryption_key_user($u_pass, $padding, $id, $u, $o, $P, $V, $R, $enc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,11 +205,17 @@ sub module_generate_hash
|
|||||||
$u = $m2->RC4 ($u);
|
$u = $m2->RC4 ($u);
|
||||||
}
|
}
|
||||||
$u .= substr (pack ("H*", $u_save), 16, 16);
|
$u .= substr (pack ("H*", $u_save), 16, 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$u = pack("H*", $u)
|
||||||
|
}
|
||||||
|
|
||||||
################ OWNER PASSWORD #################
|
################ OWNER PASSWORD #################
|
||||||
my $o_key = pdf_compute_encryption_key_owner($word, $padding, $id, $u, $o, $P, $V, $R, $enc);
|
my $o_key = pdf_compute_encryption_key_owner($word, $padding, $id, $u, $o, $P, $V, $R, $enc);
|
||||||
|
|
||||||
my $n = Crypt::RC4->new ($o_key);
|
my $n = Crypt::RC4->new ($o_key);
|
||||||
if("".$u_pass eq "")
|
if($u_pass eq "")
|
||||||
{
|
{
|
||||||
$o = $n->RC4(substr ($padding, 0, 32 - length ""));
|
$o = $n->RC4(substr ($padding, 0, 32 - length ""));
|
||||||
}
|
}
|
||||||
@ -223,8 +225,6 @@ sub module_generate_hash
|
|||||||
$o = $n->RC4($u_pass.substr ($padding, 0, 32 - length $u_pass));
|
$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;
|
my @ress2 = split "", $o_key;
|
||||||
|
|
||||||
if ($R >= 3)
|
if ($R >= 3)
|
||||||
@ -240,19 +240,14 @@ sub module_generate_hash
|
|||||||
}
|
}
|
||||||
|
|
||||||
my $s = join ("", @xor);
|
my $s = join ("", @xor);
|
||||||
|
|
||||||
my $n2 = Crypt::RC4->new ($s);
|
my $n2 = Crypt::RC4->new ($s);
|
||||||
|
|
||||||
$o = $n2->RC4 ($o);
|
$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;
|
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));
|
$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);
|
$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;
|
return $hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +287,6 @@ sub module_verify_hash
|
|||||||
my $u_pass = "";
|
my $u_pass = "";
|
||||||
if($i_data == 12) {
|
if($i_data == 12) {
|
||||||
$u_pass = shift @data;
|
$u_pass = shift @data;
|
||||||
#printf("u_pass = %s\n", $u_pass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return unless defined $id;
|
return unless defined $id;
|
||||||
|
Loading…
Reference in New Issue
Block a user