Advertisement
Guest User

Untitled

a guest
Jan 20th, 2017
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.76 KB | None | 0 0
  1. #!/usr/bin/env perl
  2.  
  3. # Author: philsmd (for hashcat)
  4. # License: public domain
  5. # Date: January 2017
  6.  
  7. use strict;
  8. use warnings;
  9.  
  10. use Crypt::PBKDF2;
  11. use Crypt::Mode::ECB;
  12.  
  13. #
  14. # Constants
  15. #
  16.  
  17. my $CHECK_SIG = 12008468691120727718; # 0xa6a6a6a6a6a6a6a6
  18. my $MAX_PLIST_SEARCH_DISTANCE = 256;
  19.  
  20. #
  21. # Helper functions
  22. #
  23.  
  24. sub to_64bit_num
  25. {
  26. my $str = shift;
  27.  
  28. my $res = 0;
  29.  
  30. for (my $i = 0; $i < 8; $i++)
  31. {
  32. $res += ord (substr ($str, $i, 1)) << (8 * (7 - $i));
  33. }
  34.  
  35. return $res ;
  36. }
  37.  
  38. sub from_64bit_num
  39. {
  40. my $num = shift;
  41.  
  42. my $res = "";
  43.  
  44. for (my $i = 0; $i < 8; $i++)
  45. {
  46. $res = chr ($num & 0xff) . $res;
  47. $num >>= 8;
  48. }
  49.  
  50. return $res ;
  51. }
  52.  
  53. sub aes_unwrap
  54. {
  55. my $key = shift;
  56. my $WPKY = shift;
  57.  
  58. # init
  59.  
  60. my @C;
  61.  
  62. for (my $i = 0; $i < length ($WPKY) / 8; $i++)
  63. {
  64. $C[$i] = unpack ("Q>", substr ($WPKY, $i * 8, 8)); # $C[$i] = to_64bit_num (substr ($WPKY, $i * 8, 8));
  65. }
  66.  
  67. my $n = scalar (@C) - 1;
  68. my @R = (0) x ($n + 1);
  69. my $A = $C[0];
  70.  
  71. for (my $i = 1; $i < $n + 1; $i++)
  72. {
  73. $R[$i] = $C[$i];
  74. }
  75.  
  76. # AES mode ECB
  77.  
  78. my $m = Crypt::Mode::ECB->new ('AES', 0);
  79.  
  80. # main unwrap loop
  81.  
  82. for (my $j = 5; $j >= 0; $j--)
  83. {
  84. for (my $i = $n; $i > 0; $i--)
  85. {
  86. my $todec;
  87.  
  88. $todec = pack ("Q>", $A ^ ($n * $j + $i)); # $todec = from_64bit_num ($A ^ ($n * $j + $i));
  89. $todec .= pack ("Q>", $R[$i]);
  90.  
  91. my $B = $m->decrypt ($todec, $key);
  92.  
  93. $A = unpack ("Q>", substr ($B, 0, 8));
  94. $R[$i] = unpack ("Q>", substr ($B, 8, 8));
  95. }
  96. }
  97.  
  98. return $A;
  99. }
  100.  
  101. sub parse_manifest_file
  102. {
  103. my $fp = shift;
  104.  
  105. my $wpky = undef;
  106. my $salt = undef;
  107. my $iter = undef;
  108.  
  109. my $manifest_buffer = do
  110. {
  111. local $/ = undef;
  112. <$fp>;
  113. };
  114.  
  115. my $manifest_buffer_len = length ($manifest_buffer);
  116.  
  117. return (undef, undef, undef) if (length ($manifest_buffer) < 4 + 4 + 4 + 4 + 4 + 4);
  118.  
  119. my @salt_matches = ($manifest_buffer =~ /SALT..../g);
  120.  
  121. # okay, I admit this is some strange parsing, but it seems to work all the times (for me)
  122. # for instance, it assumes that the order is always like this: 1. salt, 2. iter, 3. wpky
  123.  
  124. my $idx_glob = 0;
  125.  
  126. for (my $i = 0; $i < scalar (@salt_matches); $i++)
  127. {
  128. my $idx_salt = index ($manifest_buffer, "SALT", $idx_glob + 0);
  129.  
  130. last if ($idx_salt == -1);
  131.  
  132. my $idx_iter = index ($manifest_buffer, "ITER", $idx_salt + 1);
  133.  
  134. last if ($idx_iter == -1);
  135.  
  136. my $idx_wpky = index ($manifest_buffer, "WPKY", $idx_iter + 1);
  137.  
  138. last if ($idx_wpky == -1);
  139.  
  140. # special case:
  141.  
  142. last if ($manifest_buffer_len - $idx_wpky < 8); # too close to the EOF
  143.  
  144. if ($idx_wpky - $idx_salt < $MAX_PLIST_SEARCH_DISTANCE) # some sane distance between the items
  145. {
  146. my $salt_len = substr ($manifest_buffer, $idx_salt + 4, 4);
  147. my $iter_len = substr ($manifest_buffer, $idx_iter + 4, 4);
  148. my $wpky_len = substr ($manifest_buffer, $idx_wpky + 4, 4);
  149.  
  150. $idx_salt += 8;
  151. $idx_iter += 8;
  152. $idx_wpky += 8;
  153.  
  154. $salt = substr ($manifest_buffer, $idx_salt, unpack ("L>", $salt_len));
  155. $iter = substr ($manifest_buffer, $idx_iter, unpack ("L>", $iter_len));
  156. $wpky = substr ($manifest_buffer, $idx_wpky, unpack ("L>", $wpky_len));
  157.  
  158. # iter is a special case, needs to be converted to a number
  159. $iter = unpack ("L>", $iter);
  160.  
  161. last;
  162. }
  163.  
  164. $idx_glob = $idx_wpky + 1;
  165. }
  166.  
  167. # optional also search for DPIC and DPSL (iOS 10.2+ ?)
  168.  
  169. my $dpic = undef; # iteration count
  170. my $dpsl = undef; # salt
  171.  
  172. my @dpsl_matches = ($manifest_buffer =~ /DPSL..../g);
  173.  
  174. $idx_glob = 0;
  175.  
  176. for (my $i = 0; $i < scalar (@dpsl_matches); $i++)
  177. {
  178. my $idx_dpic = index ($manifest_buffer, "DPIC", $idx_glob + 0);
  179.  
  180. last if ($idx_dpic == -1);
  181.  
  182. my $idx_dpsl = index ($manifest_buffer, "DPSL", $idx_dpic + 1);
  183.  
  184. last if ($idx_dpsl == -1);
  185.  
  186. if ($idx_dpsl - $idx_dpic < $MAX_PLIST_SEARCH_DISTANCE)
  187. {
  188. my $dpic_len = substr ($manifest_buffer, $idx_dpic + 4, 4);
  189. my $dpsl_len = substr ($manifest_buffer, $idx_dpsl + 4, 4);
  190.  
  191. $idx_dpic += 8;
  192. $idx_dpsl += 8;
  193.  
  194. $dpic = substr ($manifest_buffer, $idx_dpic, unpack ("L>", $dpic_len));
  195. $dpsl = substr ($manifest_buffer, $idx_dpsl, unpack ("L>", $dpsl_len));
  196.  
  197. $dpic = unpack ("L>", $dpic);
  198.  
  199. last;
  200. }
  201.  
  202. $idx_glob = $idx_dpsl + 1;
  203. }
  204.  
  205. return ($wpky, $salt, $iter, $dpic, $dpsl);
  206. }
  207.  
  208. #
  209. # MAIN
  210. #
  211.  
  212. # init:
  213.  
  214. if (scalar (@ARGV) != 1)
  215. {
  216. print "Usage: $0 <Manifest.plist file>\n";
  217.  
  218. exit (1);
  219. }
  220.  
  221. my $manifest_file = $ARGV[0];
  222.  
  223. my $FH_MANIFEST;
  224.  
  225. if (! open ($FH_MANIFEST, "<$manifest_file"))
  226. {
  227. print "ERROR: Could not open '$manifest_file'\n";
  228.  
  229. exit (1);
  230. }
  231.  
  232. binmode ($FH_MANIFEST);
  233.  
  234. my ($WPKY, $SALT, $ITER, $DPIC, $DPSL) = parse_manifest_file ($FH_MANIFEST);
  235.  
  236. close ($FH_MANIFEST);
  237.  
  238. if (! defined ($WPKY))
  239. {
  240. print "ERROR: WPKY could not be found in '$manifest_file'\n";
  241.  
  242. exit (1);
  243. }
  244.  
  245. if (! defined ($SALT))
  246. {
  247. print "ERROR: SALT could not be found in '$manifest_file'\n";
  248.  
  249. exit (1);
  250. }
  251.  
  252. if (! defined ($ITER))
  253. {
  254. print "ERROR: ITER could not be found in '$manifest_file'\n";
  255.  
  256. exit (1);
  257. }
  258.  
  259. # either none or both should be set!
  260.  
  261. $DPSL = undef if (! defined ($DPIC));
  262. $DPIC = undef if (! defined ($DPSL));
  263.  
  264. # crypto init
  265.  
  266. #
  267. # Actual START
  268. #
  269.  
  270. my $pbkdf2 = Crypt::PBKDF2->new
  271. (
  272. hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA1'),
  273. iterations => $ITER,
  274. output_len => 32
  275. );
  276.  
  277. my $pbkdf2x;
  278.  
  279. if (defined ($DPSL))
  280. {
  281. $pbkdf2x = Crypt::PBKDF2->new
  282. (
  283. hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2'),
  284. iterations => $DPIC,
  285. output_len => 32
  286. );
  287. }
  288.  
  289. while (my $pass = <STDIN>)
  290. {
  291. chomp ($pass);
  292.  
  293. my $key;
  294.  
  295. if (defined ($DPSL))
  296. {
  297. my $key_dpsl = $pbkdf2x->PBKDF2 ($DPSL, $pass);
  298.  
  299. $key = $pbkdf2->PBKDF2 ($SALT, $key_dpsl);
  300. }
  301. else
  302. {
  303. $key = $pbkdf2->PBKDF2 ($SALT, $pass);
  304. }
  305.  
  306. if (aes_unwrap ($key, $WPKY) == $CHECK_SIG)
  307. {
  308. print "Password found: $pass\n";
  309.  
  310. exit (0);
  311. }
  312. }
  313.  
  314. exit (1);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement