Re: pwdump 2 & 3

From: Nicolas RUFF (lists) (ruff.lists@edelweb.fr)
Date: Sun Jan 30 2005 - 17:33:00 EST


> Your explanation is quite interesting, but I see a conflict with the
> information mentioned in:
> "Windows Passwords: Everything You Need To Know"
> http://202.181.238.2/hk/teched2004/ppt/Day_2_Rm402/WIN495(1500-1615).ppt
>
> According to the above mentioned presentation, the information in the
> cache is:
> MD5(NTLM(password)+userID+Domain)
>
> Can you provide any feedback on that?

Well, I am afraid that Microsoft is wrong :-)

To demonstrate this, I wrote the following utility in PERL
(CACHE-COMPUTE.PL).

In short :
- Go onto a Windows NT4 Workstation (it will not work against Windows
2000+, since a different password storage scheme is used).
- Use LSADUMP to dump NL$x entries.
- Fill the "username" and "password" in CACHE-COMPUTE with data from a
user that is in cache (for example, the currently logged in user).
- You should match one line of the LSADUMP output with CACHE-COMPUTE output.

On my computer :
- username = testuser
- password = thisisatest
- CACHE-COMPUTE output = 7a0420497cf92869352c51578c1945fc
- LSADUMP =
NL$8
  72 F1 8D 6A 2A 4F E1 6C 7B 99 A7 02 2A 0E CD 40 r..j*O.l{...*..@
  7A 04 20 49 7C F9 28 69 35 2C 51 57 8C 19 45 FC z. I|.(i5,QW..E.
  01 01 00 ...

Regards,
- Nicolas RUFF
-----------------------------------
Security Consultant
EdelWeb (http://www.edelweb.fr/)
Mail: nicolas.ruff (at) edelweb.fr
-----------------------------------

#!/usr/bin/perl

# CACHE-COMPUTE.PL
# A little utility to compute cache values for Windows NT4
#
# I really don't like PERL ...
# ... but it is hard to find a MD4 module for Python
# (c) Nicolas RUFF / EdelWeb / released on Jan 2005

use Crypt::DES;
use Digest::MD4;

@odd_parity = (
   1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
  16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
  32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
  49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
  64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
  81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
  97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
);

# Shamelessly copied from Authen::NTLM-1.02
# Added des_set_odd_parity() from SSLEAY
sub str_to_key
{
   my ($str) = @_;
   my $i;
   my @key;
   my $out;
   my @str = map {ord($_)} split(//, $str);
   $key[0] = $str[0]>>1;
   $key[1] = (($str[0]&0x01)<<6) | ($str[1]>>2);
   $key[2] = (($str[1]&0x03)<<5) | ($str[2]>>3);
   $key[3] = (($str[2]&0x07)<<4) | ($str[3]>>4);
   $key[4] = (($str[3]&0x0f)<<3) | ($str[4]>>5);
   $key[5] = (($str[4]&0x1f)<<2) | ($str[5]>>6);
   $key[6] = (($str[5]&0x3f)<<1) | ($str[6]>>7);
   $key[7] = $str[6]&0x7f;
   foreach $i (0..7)
   {
     $key[$i] = 0xff&($key[$i]<<1);
     # des_set_odd_parity()
     $key[$i] = $odd_parity[ $key[$i] ];
   }

   return \@key;
}

# --------------------------------------------------
# Input data
# --------------------------------------------------
# username = "testuser" (Unicode)
$username = "t\x00e\x00s\x00t\x00u\x00s\x00e\x00r\x00";
$password = "thisisatest";

# --------------------------------------------------
# Compute LM hash
# In this sample : "8A6D8380CAC58F22 01FC5A6BE7BC6929"
# --------------------------------------------------

$password = uc($password);

$password_part1 = pack( "a7", substr($password, 0, 7) );
$key1 = str_to_key( $password_part1 );
$deskey1 = pack( "C8", @$key1 );

$password_part2 = pack( "a7", substr($password, 7, 7) );
$key2 = str_to_key( $password_part2 );
$deskey2 = pack( "C8", @$key2 );

# Magic string (see LM specs)
$lmplaintext = pack( "H16", "4B47532140232425" );

$des1 = new Crypt::DES $deskey1;
$lmhash1 = $des1->encrypt($lmplaintext);
print "LM part1 : ", unpack("H16", $lmhash1), "\n";

$des2 = new Crypt::DES $deskey2;
$lmhash2 = $des2->encrypt($lmplaintext);
print "LM part2 : ", unpack("H16", $lmhash2), "\n";

# --------------------------------------------------
# Compute cache value
# In this sample : NL$8 =
# 72 F1 8D 6A 2A 4F E1 6C 7B 99 A7 02 2A 0E CD 40
# 7A 04 20 49 7C F9 28 69 35 2C 51 57 8C 19 45 FC [*]
# 01 01 00
# --------------------------------------------------

$string = $lmhash1 . $lmhash2 . $username;

$ctx = new Digest::MD4;
$ctx->reset();
print "Output : ", Digest::MD4->hexhash($string), "\n";



This archive was generated by hypermail 2.1.7 : Sat Apr 12 2008 - 10:54:15 EDT