hashcat/OpenCL/m25400-pure.cl

440 lines
11 KiB
Common Lisp
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Author......: See docs/credits.txt
* License.....: MIT
*/
// https://web.archive.org/web/20220306152229/https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
#ifdef KERNEL_STATIC
#include M2S(INCLUDE_PATH/inc_vendor.h)
#include M2S(INCLUDE_PATH/inc_types.h)
#include M2S(INCLUDE_PATH/inc_platform.cl)
#include M2S(INCLUDE_PATH/inc_common.cl)
#include M2S(INCLUDE_PATH/inc_hash_md5.cl)
#include M2S(INCLUDE_PATH/inc_cipher_rc4.cl)
#endif
#define COMPARE_S M2S(INCLUDE_PATH/inc_comp_single.cl)
#define COMPARE_M M2S(INCLUDE_PATH/inc_comp_multi.cl)
typedef struct pdf
{
int V;
int R;
int P;
int enc_md;
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 u_pass_len;
u32 rc4key[2];
u32 rc4data[2];
int P_minus;
} pdf_t;
typedef struct pdf14_tmp
{
u32 digest[4];
u32 out[8];
} pdf14_tmp_t;
KERNEL_FQ void m25400_init (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
{
/**
* base
*/
const u64 gid = get_global_id (0);
//const u64 lid = get_local_id (0);
if (gid >= GID_CNT) return;
u32 w0[4];
w0[0] = pws[gid].i[ 0];
w0[1] = pws[gid].i[ 1];
w0[2] = pws[gid].i[ 2];
w0[3] = pws[gid].i[ 3];
u32 w1[4];
w1[0] = pws[gid].i[ 4];
w1[1] = pws[gid].i[ 5];
w1[2] = pws[gid].i[ 6];
w1[3] = pws[gid].i[ 7];
const u32 pw_len = pws[gid].pw_len;
const u32 padding[8] =
{
0x5e4ebf28,
0x418a754e,
0x564e0064,
0x0801faff,
0xb6002e2e,
0x803e68d0,
0xfea90c2f,
0x7a695364
};
/**
* shared
*/
u32 P = esalt_bufs[DIGESTS_OFFSET_HOST].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]; // 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_HOST].id_buf[0];
id_buf[ 1] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[1];
id_buf[ 2] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[2];
id_buf[ 3] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[3];
id_buf[ 4] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[4];
id_buf[ 5] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[5];
id_buf[ 6] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[6];
id_buf[ 7] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[7];
id_buf[ 8] = 0;
id_buf[ 9] = 0;
id_buf[10] = 0;
id_buf[11] = 0;
u32 rc4data[2];
rc4data[0] = padding[0];
rc4data[1] = padding[1];
/**
* main init
*/
u32 w0_t[4];
u32 w1_t[4];
u32 w2_t[4];
u32 w3_t[4];
// max length supported by pdf11 is 32
w0_t[0] = padding[0];
w0_t[1] = padding[1];
w0_t[2] = padding[2];
w0_t[3] = padding[3];
w1_t[0] = padding[4];
w1_t[1] = padding[5];
w1_t[2] = padding[6];
w1_t[3] = padding[7];
w2_t[0] = 0;
w2_t[1] = 0;
w2_t[2] = 0;
w2_t[3] = 0;
w3_t[0] = 0;
w3_t[1] = 0;
w3_t[2] = 0;
w3_t[3] = 0;
switch_buffer_by_offset_le (w0_t, w1_t, w2_t, w3_t, pw_len);
// add password
// truncate at 32 is wanted, not a bug!
// add padding
w0_t[0] |= w0[0];
w0_t[1] |= w0[1];
w0_t[2] |= w0[2];
w0_t[3] |= w0[3];
w1_t[0] |= w1[0];
w1_t[1] |= w1[1];
w1_t[2] |= w1[2];
w1_t[3] |= w1[3];
w2_t[0] = 0x80;
w2_t[1] = 0;
w2_t[2] = 0;
w2_t[3] = 0;
w3_t[0] = 0;
w3_t[1] = 0;
w3_t[2] = 32 * 8;
w3_t[3] = 0;
u32 digest[4];
digest[0] = MD5M_A;
digest[1] = MD5M_B;
digest[2] = MD5M_C;
digest[3] = MD5M_D;
md5_transform (w0_t, w1_t, w2_t, w3_t, digest);
tmps[gid].digest[0] = digest[0];
tmps[gid].digest[1] = digest[1];
tmps[gid].digest[2] = digest[2];
tmps[gid].digest[3] = digest[3];
tmps[gid].out[0] = rc4data[0];
tmps[gid].out[1] = rc4data[1];
tmps[gid].out[2] = 0;
tmps[gid].out[3] = 0;
tmps[gid].out[4] = 0; // we only need the size of out for the plaintext check
tmps[gid].out[5] = 0; // we only need the size of out for the plaintext check
tmps[gid].out[6] = 0; // we only need the size of out for the plaintext check
tmps[gid].out[7] = 0; // we only need the size of out for the plaintext check
}
KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
{
/**
* base
*/
const u64 gid = get_global_id (0);
const u64 lid = get_local_id (0);
if (gid >= GID_CNT) return;
/**
* shared
*/
LOCAL_VK u32 S[64 * FIXED_LOCAL_SIZE];
/**
* loop
*/
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];
for (u32 i = 0, j = LOOP_POS; i < LOOP_CNT; i++, j++)
{
if (j < 50)
{
// the owner-key is generated by iterating a md5 hash 50 times
// see: "Algorithm 3.3 Computing the encryption dictionarys O (owner password) value"
u32 w0_t[4];
u32 w1_t[4];
u32 w2_t[4];
u32 w3_t[4];
w0_t[0] = digest[0];
w0_t[1] = digest[1];
w0_t[2] = digest[2];
w0_t[3] = digest[3];
w1_t[0] = 0x80;
w1_t[1] = 0;
w1_t[2] = 0;
w1_t[3] = 0;
w2_t[0] = 0;
w2_t[1] = 0;
w2_t[2] = 0;
w2_t[3] = 0;
w3_t[0] = 0;
w3_t[1] = 0;
w3_t[2] = 16 * 8;
w3_t[3] = 0;
digest[0] = MD5M_A;
digest[1] = MD5M_B;
digest[2] = MD5M_C;
digest[3] = MD5M_D;
md5_transform (w0_t, w1_t, w2_t, w3_t, digest);
}
}
u32 out[4];
out[0] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[0]; // store original o-value in out (scratchpad)
out[1] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[1];
out[2] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[2];
out[3] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[3];
u32 out2[4];
out2[0] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[4]; // store original o-value in out (scratchpad)
out2[1] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[5];
out2[2] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[6];
out2[3] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[7];
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 dictionarys O (owner password) value": "If there is no owner password, use the user password instead".
u32 tmp[4];
u8 j;
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, lid);
j = rc4_next_16 (S, 0, 0, out, out, lid);
rc4_next_16 (S, 16, j, out2, out2, lid);
}
rc4_init_128 (S, o_rc4_decryption_key, lid);
j = rc4_next_16 (S, 0, 0, out, out, lid); // output of the rc4 decrypt of the o-value should be the padded user-password
tmps[gid].out[0] = out[0];
tmps[gid].out[1] = out[1];
tmps[gid].out[2] = out[2];
tmps[gid].out[3] = out[3];
rc4_next_16 (S, 16, j, out2, out2, lid); // decrypt a second block of rc4 to improve plaintext check and limit false positives
tmps[gid].out[4] = out2[0];
tmps[gid].out[5] = out2[1];
tmps[gid].out[6] = out2[2];
tmps[gid].out[7] = out2[3];
tmps[gid].digest[0] = digest[0];
tmps[gid].digest[1] = digest[1];
tmps[gid].digest[2] = digest[2];
tmps[gid].digest[3] = digest[3];
}
KERNEL_FQ void m25400_comp (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
{
const u32 digest[4] =
{
esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[0],
esalt_bufs[DIGESTS_OFFSET_HOST].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_CNT) return;
const u64 lid = get_local_id (0);
#define il_pos 0
const u32 out[8] =
{
tmps[gid].out[0],
tmps[gid].out[1],
tmps[gid].out[2],
tmps[gid].out[3],
tmps[gid].out[4],
tmps[gid].out[5],
tmps[gid].out[6],
tmps[gid].out[7]
};
// 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 < 32; i++) // check all 32 bytes of the decrypted o-value, this including the padding.
{
// cast out buffer to byte such that we can do a byte per byte comparison
PRIVATE_AS const u32 *u32OutBufPtr = (PRIVATE_AS u32 *) out;
PRIVATE_AS const u8 *u8OutBufPtr = (PRIVATE_AS u8 *) u32OutBufPtr;
// cast padding buffer to byte such that we can do a byte per byte comparison
PRIVATE_AS const u32 *u32OutPadPtr = (PRIVATE_AS u32 *) padding;
PRIVATE_AS const u8 *u8OutPadPtr = (PRIVATE_AS 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 padding and ASCII
if (is_valid_printable_8(u8OutBufPtr[i]) ||
(u8OutBufPtr[i] == u8OutPadPtr[i_padding]))
{
if (u8OutBufPtr[i] == u8OutPadPtr[i_padding])
{
//if ((gid == 0) && (lid == 0)) printf("correct padding byte[%d]=0x%02x==0x%02x\n", i, u8OutBufPtr[i], u8OutPadPtr[i_padding]);
i_padding = i_padding + 1;
}
else
{
if (is_valid_printable_8(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("wrong byte[%d]=0x%02x\n", i, u8OutBufPtr[i]);
//
// printf("u8OutBufPtr=0x");
// for(int j=0;j<32;j++) {
// printf("%02x", u8OutBufPtr[j]);
// }
// printf("\n");
//
// printf("u8OutPadPtr=0x");
// for(int j=0;j<32;j++) {
// printf("%02x", u8OutPadPtr[j]);
// }
// printf("\n");
//}
correct = false;
break;
}
}
if (correct)
{
int digest_pos = find_hash (digest, DIGESTS_CNT, &digests_buf[DIGESTS_OFFSET_HOST]);
if (digest_pos != -1)
{
const u32 final_hash_pos = DIGESTS_OFFSET_HOST + digest_pos;
if (hc_atomic_inc (&hashes_shown[final_hash_pos]) == 0)
{
mark_hash (plains_buf, d_return_buf, SALT_POS_HOST, DIGESTS_CNT, digest_pos, final_hash_pos, gid, il_pos, 0, 0);
}
}
}
}