Itunes Backup
#16
Well, it is pretty easy to see from the link posted by keen (http://stackoverflow.com/questions/14983...one-backup) that after the $key = pbkdf2_hmac_sha1 ($pass, $salt, $iter) call the WPKY key needs to be "unwrapped" (see AESUnwrap ($key, $wpky) in the code snippet from the stackoverflow post) to verify if the first 8 bytes are 0xa6a6a6a6a6a6a6a6.
If this is successfull the $pass (and therefore the $key + $wpky combination) are correct.

Therefore this algorithm is not just -m 12000 = PBKDF2-HMAC-SHA1 and is also not yet supported by hashcat.



Update:

Here you have a POC (proof of concept) to show that this is exactly how you possibly can attack this:

Code:
#!/usr/bin/env perl

# Author: philsmd
# 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;
  }

  return ($wpky, $salt, $iter);
}

#
# 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) = 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);
}

# crypto init

my $pbkdf2 = Crypt::PBKDF2->new
(
  hasher     => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA1'),
  iterations => $ITER,
  output_len => 32
);

#
# Actual START
#

while (my $pass = <STDIN>)
{
  chomp ($pass);

  my $key = $pbkdf2->PBKDF2 ($SALT, $pass);

  if (aes_unwrap ($key, $WPKY) == $CHECK_SIG)
  {
    print "Password found: $pass\n";

    exit (0);
  }
}

exit (1);

Note: I put this code together pretty quickly so please don't blame me for it. Feel free to use it (pubic domain license).

This is an example on how you use it (dictionary/password candidates come from stdin, Manifest file as first command line argument):


Code:
./hashcat --stdout -a 3 ?d?d?d?d?d?d | ./crack_plist_file.pl Manifest.plist

Password found: 123456


Messages In This Thread
Itunes Backup - by jwarren - 11-15-2016, 09:50 PM
RE: Itunes Backup - by IncognitoEntity - 12-10-2016, 12:06 AM
RE: Itunes Backup - by keen - 01-15-2017, 07:55 PM
RE: Itunes Backup - by goclukee - 12-21-2016, 11:52 AM
RE: Itunes Backup - by Tuxel - 01-16-2017, 07:21 PM
RE: Itunes Backup - by epixoip - 01-16-2017, 09:36 PM
RE: Itunes Backup - by Tuxel - 01-16-2017, 10:40 PM
RE: Itunes Backup - by epixoip - 01-16-2017, 11:01 PM
RE: Itunes Backup - by rk - 01-17-2017, 05:18 PM
RE: Itunes Backup - by epixoip - 01-17-2017, 09:33 PM
RE: Itunes Backup - by Si2006 - 01-18-2017, 12:02 PM
RE: Itunes Backup - by Tuxel - 01-18-2017, 12:41 PM
RE: Itunes Backup - by trebor_hc - 01-18-2017, 04:53 PM
RE: Itunes Backup - by rk - 01-18-2017, 08:30 PM
RE: Itunes Backup - by boonkerz - 01-19-2017, 12:14 AM
RE: Itunes Backup - by philsmd - 01-19-2017, 12:51 AM
RE: Itunes Backup - by rk - 01-19-2017, 09:05 PM
RE: Itunes Backup - by Tuxel - 01-19-2017, 10:02 PM
RE: Itunes Backup - by philsmd - 01-19-2017, 11:43 PM
RE: Itunes Backup - by Tuxel - 01-20-2017, 12:05 AM
RE: Itunes Backup - by IncognitoEntity - 01-20-2017, 11:57 AM
RE: Itunes Backup - by philsmd - 01-20-2017, 01:13 PM
RE: Itunes Backup - by Tuxel - 01-20-2017, 01:32 PM
RE: Itunes Backup - by philsmd - 01-20-2017, 04:09 PM
RE: Itunes Backup - by IncognitoEntity - 01-20-2017, 06:00 PM
RE: Itunes Backup - by Tuxel - 01-20-2017, 09:49 PM
RE: Itunes Backup - by keen - 01-25-2017, 10:58 PM
RE: Itunes Backup - by philsmd - 01-26-2017, 10:25 PM
RE: Itunes Backup - by kiara - 01-27-2017, 12:58 PM
RE: Itunes Backup - by joswr1ght - 01-27-2017, 05:06 PM
RE: Itunes Backup - by epixoip - 01-27-2017, 10:19 PM
RE: Itunes Backup - by joswr1ght - 01-27-2017, 11:27 PM
RE: Itunes Backup - by atom - 01-27-2017, 11:38 PM
RE: Itunes Backup - by Tuxel - 01-31-2017, 09:33 AM
RE: Itunes Backup - by salvonet80 - 03-03-2017, 12:41 PM
RE: Itunes Backup - by ensign.fodder - 03-03-2017, 06:15 PM
RE: Itunes Backup - by rico - 03-03-2017, 03:53 PM
RE: Itunes Backup - by rico - 03-03-2017, 06:31 PM
RE: Itunes Backup - by nccpdsantry - 03-21-2017, 05:22 PM
RE: Itunes Backup - by atom - 03-22-2017, 01:25 PM
RE: Itunes Backup - by Copyright - 03-23-2017, 08:22 AM
RE: Itunes Backup - by squishymango - 03-23-2017, 09:54 AM
RE: Itunes Backup - by philsmd - 03-23-2017, 09:40 PM