Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env perl
- # Author: philsmd (for hashcat)
- # License: public domain
- # Date: January 2017
- use strict;
- use warnings;
- use Crypt::PBKDF2;
- use Crypt::Mode::ECB;
- #
- # Constants
- #
- my $CHECK_SIG = 12008468691120727718; # 0xa6a6a6a6a6a6a6a6
- my $MAX_PLIST_SEARCH_DISTANCE = 256;
- #
- # Helper functions
- #
- sub to_64bit_num
- {
- my $str = shift;
- my $res = 0;
- for (my $i = 0; $i < 8; $i++)
- {
- $res += ord (substr ($str, $i, 1)) << (8 * (7 - $i));
- }
- return $res ;
- }
- sub from_64bit_num
- {
- my $num = shift;
- my $res = "";
- for (my $i = 0; $i < 8; $i++)
- {
- $res = chr ($num & 0xff) . $res;
- $num >>= 8;
- }
- return $res ;
- }
- sub aes_unwrap
- {
- my $key = shift;
- my $WPKY = shift;
- # init
- my @C;
- for (my $i = 0; $i < length ($WPKY) / 8; $i++)
- {
- $C[$i] = unpack ("Q>", substr ($WPKY, $i * 8, 8)); # $C[$i] = to_64bit_num (substr ($WPKY, $i * 8, 8));
- }
- my $n = scalar (@C) - 1;
- my @R = (0) x ($n + 1);
- my $A = $C[0];
- for (my $i = 1; $i < $n + 1; $i++)
- {
- $R[$i] = $C[$i];
- }
- # AES mode ECB
- my $m = Crypt::Mode::ECB->new ('AES', 0);
- # main unwrap loop
- for (my $j = 5; $j >= 0; $j--)
- {
- for (my $i = $n; $i > 0; $i--)
- {
- my $todec;
- $todec = pack ("Q>", $A ^ ($n * $j + $i)); # $todec = from_64bit_num ($A ^ ($n * $j + $i));
- $todec .= pack ("Q>", $R[$i]);
- my $B = $m->decrypt ($todec, $key);
- $A = unpack ("Q>", substr ($B, 0, 8));
- $R[$i] = unpack ("Q>", substr ($B, 8, 8));
- }
- }
- return $A;
- }
- sub parse_manifest_file
- {
- my $fp = shift;
- my $wpky = undef;
- my $salt = undef;
- my $iter = undef;
- my $manifest_buffer = do
- {
- local $/ = undef;
- <$fp>;
- };
- my $manifest_buffer_len = length ($manifest_buffer);
- return (undef, undef, undef) if (length ($manifest_buffer) < 4 + 4 + 4 + 4 + 4 + 4);
- my @salt_matches = ($manifest_buffer =~ /SALT..../g);
- # okay, I admit this is some strange parsing, but it seems to work all the times (for me)
- # for instance, it assumes that the order is always like this: 1. salt, 2. iter, 3. wpky
- my $idx_glob = 0;
- for (my $i = 0; $i < scalar (@salt_matches); $i++)
- {
- my $idx_salt = index ($manifest_buffer, "SALT", $idx_glob + 0);
- last if ($idx_salt == -1);
- my $idx_iter = index ($manifest_buffer, "ITER", $idx_salt + 1);
- last if ($idx_iter == -1);
- my $idx_wpky = index ($manifest_buffer, "WPKY", $idx_iter + 1);
- last if ($idx_wpky == -1);
- # special case:
- last if ($manifest_buffer_len - $idx_wpky < 8); # too close to the EOF
- if ($idx_wpky - $idx_salt < $MAX_PLIST_SEARCH_DISTANCE) # some sane distance between the items
- {
- my $salt_len = substr ($manifest_buffer, $idx_salt + 4, 4);
- my $iter_len = substr ($manifest_buffer, $idx_iter + 4, 4);
- my $wpky_len = substr ($manifest_buffer, $idx_wpky + 4, 4);
- $idx_salt += 8;
- $idx_iter += 8;
- $idx_wpky += 8;
- $salt = substr ($manifest_buffer, $idx_salt, unpack ("L>", $salt_len));
- $iter = substr ($manifest_buffer, $idx_iter, unpack ("L>", $iter_len));
- $wpky = substr ($manifest_buffer, $idx_wpky, unpack ("L>", $wpky_len));
- # iter is a special case, needs to be converted to a number
- $iter = unpack ("L>", $iter);
- last;
- }
- $idx_glob = $idx_wpky + 1;
- }
- # optional also search for DPIC and DPSL (iOS 10.2+ ?)
- my $dpic = undef; # iteration count
- my $dpsl = undef; # salt
- my @dpsl_matches = ($manifest_buffer =~ /DPSL..../g);
- $idx_glob = 0;
- for (my $i = 0; $i < scalar (@dpsl_matches); $i++)
- {
- my $idx_dpic = index ($manifest_buffer, "DPIC", $idx_glob + 0);
- last if ($idx_dpic == -1);
- my $idx_dpsl = index ($manifest_buffer, "DPSL", $idx_dpic + 1);
- last if ($idx_dpsl == -1);
- if ($idx_dpsl - $idx_dpic < $MAX_PLIST_SEARCH_DISTANCE)
- {
- my $dpic_len = substr ($manifest_buffer, $idx_dpic + 4, 4);
- my $dpsl_len = substr ($manifest_buffer, $idx_dpsl + 4, 4);
- $idx_dpic += 8;
- $idx_dpsl += 8;
- $dpic = substr ($manifest_buffer, $idx_dpic, unpack ("L>", $dpic_len));
- $dpsl = substr ($manifest_buffer, $idx_dpsl, unpack ("L>", $dpsl_len));
- $dpic = unpack ("L>", $dpic);
- last;
- }
- $idx_glob = $idx_dpsl + 1;
- }
- return ($wpky, $salt, $iter, $dpic, $dpsl);
- }
- #
- # MAIN
- #
- # init:
- if (scalar (@ARGV) != 1)
- {
- print "Usage: $0 <Manifest.plist file>\n";
- exit (1);
- }
- my $manifest_file = $ARGV[0];
- my $FH_MANIFEST;
- if (! open ($FH_MANIFEST, "<$manifest_file"))
- {
- print "ERROR: Could not open '$manifest_file'\n";
- exit (1);
- }
- binmode ($FH_MANIFEST);
- my ($WPKY, $SALT, $ITER, $DPIC, $DPSL) = parse_manifest_file ($FH_MANIFEST);
- close ($FH_MANIFEST);
- if (! defined ($WPKY))
- {
- print "ERROR: WPKY could not be found in '$manifest_file'\n";
- exit (1);
- }
- if (! defined ($SALT))
- {
- print "ERROR: SALT could not be found in '$manifest_file'\n";
- exit (1);
- }
- if (! defined ($ITER))
- {
- print "ERROR: ITER could not be found in '$manifest_file'\n";
- exit (1);
- }
- # either none or both should be set!
- $DPSL = undef if (! defined ($DPIC));
- $DPIC = undef if (! defined ($DPSL));
- # crypto init
- #
- # Actual START
- #
- my $pbkdf2 = Crypt::PBKDF2->new
- (
- hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA1'),
- iterations => $ITER,
- output_len => 32
- );
- my $pbkdf2x;
- if (defined ($DPSL))
- {
- $pbkdf2x = Crypt::PBKDF2->new
- (
- hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2'),
- iterations => $DPIC,
- output_len => 32
- );
- }
- while (my $pass = <STDIN>)
- {
- chomp ($pass);
- my $key;
- if (defined ($DPSL))
- {
- my $key_dpsl = $pbkdf2x->PBKDF2 ($DPSL, $pass);
- $key = $pbkdf2->PBKDF2 ($SALT, $key_dpsl);
- }
- else
- {
- $key = $pbkdf2->PBKDF2 ($SALT, $pass);
- }
- if (aes_unwrap ($key, $WPKY) == $CHECK_SIG)
- {
- print "Password found: $pass\n";
- exit (0);
- }
- }
- exit (1);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement