Merge pull request #3637 from thatux/correct-m25400-m26610-plaintextcheck

Correct m25400 m26610 plaintextcheck
pull/3640/head
Jens Steube 1 year ago committed by GitHub
commit b403f622ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -303,3 +303,9 @@ DECLSPEC void AES_GCM_GHASH_GLOBAL (PRIVATE_AS const u32 *subkey, PRIVATE_AS con
AES_GCM_ghash (subkey, len_buf, 16, out);
}
void AES_GCM_decrypt (PRIVATE_AS u32 *key, PRIVATE_AS u32 *J0, PRIVATE_AS const u32 *in, int in_len, PRIVATE_AS u32 *out, SHM_TYPE u32 *s_te0, SHM_TYPE u32 *s_te1, SHM_TYPE u32 *s_te2, SHM_TYPE u32 *s_te3, SHM_TYPE u32 *s_te4)
{
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, in, in_len, out, s_te0, s_te1, s_te2, s_te3, s_te4); // decrypt the first block of ciphertext
}

@ -17,5 +17,6 @@ DECLSPEC void AES_GCM_gctr (PRIVATE_AS const u32 *key, PRIVATE_AS const u32 *iv,
DECLSPEC void AES_GCM_GCTR (PRIVATE_AS u32 *key, PRIVATE_AS u32 *J0, PRIVATE_AS const u32 *in, int in_len, PRIVATE_AS u32 *out, SHM_TYPE u32 *s_te0, SHM_TYPE u32 *s_te1, SHM_TYPE u32 *s_te2, SHM_TYPE u32 *s_te3, SHM_TYPE u32 *s_te4);
DECLSPEC void AES_GCM_GHASH (PRIVATE_AS const u32 *subkey, PRIVATE_AS const u32 *aad_buf, int aad_len, PRIVATE_AS const u32 *enc_buf, int enc_len, PRIVATE_AS u32 *out);
DECLSPEC void AES_GCM_GHASH_GLOBAL (PRIVATE_AS const u32 *subkey, PRIVATE_AS const u32 *aad_buf, int aad_len, GLOBAL_AS const u32 *enc_buf, int enc_len, PRIVATE_AS u32 *out);
DECLSPEC void AES_GCM_decrypt (PRIVATE_AS u32 *key, PRIVATE_AS u32 *J0, PRIVATE_AS const u32 *in, int in_len, PRIVATE_AS u32 *out, SHM_TYPE u32 *s_te0, SHM_TYPE u32 *s_te1, SHM_TYPE u32 *s_te2, SHM_TYPE u32 *s_te3, SHM_TYPE u32 *s_te4);
#endif // INC_CIPHER_AES_GCM_H

@ -3,7 +3,7 @@
* License.....: MIT
*/
// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
// 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)
@ -43,7 +43,7 @@ typedef struct pdf
typedef struct pdf14_tmp
{
u32 digest[4];
u32 out[4];
u32 out[8];
} pdf14_tmp_t;
@ -183,6 +183,10 @@ KERNEL_FQ void m25400_init (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
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))
@ -212,12 +216,6 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
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];
out[3] = tmps[gid].out[3];
for (u32 i = 0, j = LOOP_POS; i < LOOP_CNT; i++, j++)
{
if (j < 50)
@ -255,10 +253,19 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
}
}
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];
@ -268,6 +275,7 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
// 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
@ -282,21 +290,27 @@ KERNEL_FQ void m25400_loop (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
tmp[3] = o_rc4_decryption_key[3] ^ xv;
rc4_init_128 (S, tmp, lid);
rc4_next_16 (S, 0, 0, out, out, 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);
rc4_next_16 (S, 0, 0, out, out, lid); // output of the rc4 decrypt of the o-value should be the padded user-password
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];
tmps[gid].out[0] = out[0];
tmps[gid].out[1] = out[1];
tmps[gid].out[2] = out[2];
tmps[gid].out[3] = out[3];
}
KERNEL_FQ void m25400_comp (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
@ -334,12 +348,16 @@ KERNEL_FQ void m25400_comp (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
#define il_pos 0
const u32 out[4] =
const u32 out[8] =
{
tmps[gid].out[0],
tmps[gid].out[1],
tmps[gid].out[2],
tmps[gid].out[3]
tmps[gid].out[3],
tmps[gid].out[4],
tmps[gid].out[5],
tmps[gid].out[6],
tmps[gid].out[7]
};
@ -351,7 +369,7 @@ KERNEL_FQ void m25400_comp (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
int i_padding=0;
for (int i = 0; i < 16; i++)
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;
@ -363,39 +381,40 @@ KERNEL_FQ void m25400_comp (KERN_ATTR_TMPS_ESALT (pdf14_tmp_t, pdf_t))
// 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) ||
// 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])
{
//printf("correct padding byte[%d]=0x%02x\n", i, u8OutBufPtr[i]);
//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 (u8OutBufPtr[i] >= 20 && u8OutBufPtr[i] <= 0x7e)
if (is_valid_printable_8(u8OutBufPtr[i]))
{
//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
{
//printf("wrong byte[%d]=0x%02x\n", i, u8OutBufPtr[i]);
//if ((gid == 0) && (lid == 0)) {
// 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("u8OutBufPtr=0x");
// for(int j=0;j<32;j++) {
// printf("%02x", u8OutBufPtr[j]);
// }
// printf("\n");
//
//printf("u8OutPadPtr=0x");
//for(int j=0;j<16;j++) {
// printf("%02x", u8OutPadPtr[j]);
// printf("u8OutPadPtr=0x");
// for(int j=0;j<32;j++) {
// printf("%02x", u8OutPadPtr[j]);
// }
// printf("\n");
//}
//printf("\n");
correct = false;
break;
}

@ -349,23 +349,23 @@ 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 ct[4];
ct[0] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[0];
u32 ct[8];
ct[0] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[0]; // first block of ciphertext
ct[1] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[1];
ct[2] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[2];
ct[3] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[3];
ct[4] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[4]; // second block of ciphertext
ct[5] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[5];
ct[6] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[6];
ct[7] = esalt_bufs[DIGESTS_OFFSET_HOST].ct_buf[7];
u32 pt[4] = { 0 };
u32 pt[8] = { 0 };
// we try to decrypt the ciphertext
// TODO this can be moved to a separate decryption function in inc_cipher_aes-gcm.cl
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
AES_GCM_decrypt (key, J0, ct, 32, pt, s_te0, s_te1, s_te2, s_te3, s_te4);
// 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%08x\n", pt[4], pt[5]); // should be 2054726565222c22 or Tree","
u32 digest[4];
@ -382,9 +382,15 @@ KERNEL_FQ void m26610_comp (KERN_ATTR_TMPS_ESALT (pbkdf2_sha256_tmp_t, pbkdf2_sh
const int correct = is_valid_printable_32 (pt[0])
+ is_valid_printable_32 (pt[1])
+ is_valid_printable_32 (pt[2])
+ is_valid_printable_32 (pt[3]);
+ is_valid_printable_32 (pt[3])
+ is_valid_printable_32 (pt[4])
+ is_valid_printable_32 (pt[5])
+ is_valid_printable_32 (pt[6])
+ is_valid_printable_32 (pt[7]);
// if ((gid == 0) && (lid == 0)) printf("correct=%d\n", correct);
if (correct == 4)
if (correct == 8)
{
int digest_pos = find_hash (digest, DIGESTS_CNT, &digests_buf[DIGESTS_OFFSET_HOST]);

@ -40,6 +40,7 @@
- Prevent Hashcat from hanging by checking during startup that the output file is a named pipe
- Fixed debug mode 5 by adding the missing colon between original-word and finding-rule
- Skip generated rule that was the result of chaining rule operation and caused this generated rule to exceed the maximum number of function calls
- Fixed incorrect plaintext check for 25400 and 26610. Increased plaintext check to 32 bytes to prevent false positives.
##
## Technical

@ -74,7 +74,7 @@ typedef struct pdf
typedef struct pdf14_tmp
{
u32 digest[4];
u32 out[4];
u32 out[8];
} pdf14_tmp_t;
@ -501,7 +501,11 @@ int module_build_plain_postprocess (MAYBE_UNUSED const hashconfig_t *hashconfig,
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])
pdf_tmp->out[3] == padding[3] &&
pdf_tmp->out[4] == padding[4] &&
pdf_tmp->out[5] == padding[5] &&
pdf_tmp->out[6] == padding[6] &&
pdf_tmp->out[7] == padding[7])
{
return snprintf ((char *) dst_buf, dst_sz, "%s (user password not set)", (char *) src_buf);
}
@ -534,10 +538,14 @@ int module_build_plain_postprocess (MAYBE_UNUSED const hashconfig_t *hashconfig,
// 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_tmp->out[0] == padding[0] &&
pdf_tmp->out[1] == padding[1] &&
pdf_tmp->out[2] == padding[2] &&
pdf_tmp->out[3] == padding[3] &&
pdf_tmp->out[4] == padding[4] &&
pdf_tmp->out[5] == padding[5] &&
pdf_tmp->out[6] == padding[6] &&
pdf_tmp->out[7] == padding[7])
{
if (pdf->u_pass_len == 0)
{

Loading…
Cancel
Save