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:
Your Name
2021-08-20 10:16:14 +02:00
parent 012c5b16cd
commit db2e7d1391
16 changed files with 301 additions and 123 deletions
+29 -36
View File
@@ -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;