Penetration Testing mailing list archives

Re: pwdump 2 & 3


From: "Nicolas RUFF (lists)" <ruff.lists () edelweb fr>
Date: Sun, 30 Jan 2005 23:33:00 +0100

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";


Current thread: