NT domain member to domain controller authentication protocol

Linus Nordberg linus at incolumitas.se
Tue Sep 9 06:32:01 GMT 1997

Paul Ashton wrote:
>Paul Ashton <paul at argo.demon.co.uk> and
>Luke Leighton <lkcl at switchboard.net> present the NT domain
>authentication protocol. Comments and corrections are welcome.
>Add(A1,A2): Intel byte ordered addition of corresponding 4 byte
>            words in arrays A1 and A2
>E(K,D): DES ECB encryption of 8 byte data D using 7 byte key K
>lmowf(): Lan man hash
>ntowf(): NT hash
>PW: md4(machine_password) == md4(lsadump $machine.acc)
>                          == pwdump(machine$)
>              (initially) == md4(lmowf(unicode(machine)))
>RC4(K,Lk,D,Ld): RC4 encryption of data D of length Ld with key K
>                of length Lk
>v[m..n(,l)]: subset of v from bytes m to n, optionally padded
>             with zeroes to length l
>Cred(K,D): E(K[7..7,7],E(K[0..6],D)) computes a credential
>Time(): 4 byte current time
>Cc,Cs: 8 byte client and server challenges
>Rc,Rs: 8 byte client and server credentials

Hi all,

So far, the only thing that I can find that doesn't seem to be correct
is the initial password. Looks more like md4(unicode(machine)) to
me. No lmowf(), that is. Please correct me if I've got something wrong
here. Password change not yet verified though.

I have appended to this posting my implementation of the functions I
have used, or at least the interesting parts.

In fact, it's not much more than wrappers around a few functions from
the Samba package. The hard part is to find your way inside the SMB
packets where things float around depending on variable length fields
such as machine, domain and user names. That, I have been doing "by
hand". Once that is solved in code, one could for example build the
"NT-password equivalents sniffer".

For such a sniffer to have a chance, it has to be in place the moment
each domain member it is supposed to sniff enters the domain.
Furthermore, it must successfully identify and deal with all password
changes the client is doing. This makes the malicious usage of this
thought sniffer a bit less malicious than your average 'tcpdump |
egrep telnet' but it does make a point in the discussion of the
untouchable network security of Windows NT, especially when people are
starting to talk of the "secure channels" as some kind of voodoo.

The sniffer scenario relies on the predictable initial password. Does
anybody know of any way to change this behavior? 


/* nta.c */
   A few functions to calculate (1) NT password equivalents and (2) NT DC and
   Domain Member (DM) credentials, given some data that can be collected by
   capturing packets from the wire.

   To calculate the hashed passwords you will need:

     A) A network packet dump of last time either of DC or DM booted (to get
	the challenges)
	  The client and server challenges can be found in ...
	  (cli: SMBCmd=0x25, RPCop=4, last 8 bytes; srv: SMBCmd=0x25, first
	  8 bytes of last 12)
     B1) Password for DM domain account (to calculate session key (Ks) from the
           Use lsadump from Paul Ashton:
	   Usage: lsadump $machine.acc [machine]
	   You need admin rights on the machine to do this, so the following
	   may be the only option:
     B2) Packet dump of
         (1) when the DM joined the domain (to get the initial session key)
           The challenges can be found in ...
         (2) every DM pw change up to now (to follow the password changes)
           This usually occurs "some time" after joining the domain and then 
	   every 7th day, unless either the DM has been configured not to do 
	   so (Netlogon/Parameters/DisablePasswordChange:1) or the DC refuses
	   (Netlogon/Parameters/RefusePasswordChange:1). See MSKB Q154501.
     C) Packet dump of the user network logon (to get the session key encrypted
          This can be found somewhere in ... (SMBCmd=0x25, RPCop=2)

   Ok, that's quite some input. But there *are* other things that these
   functions could be used for. Like a Samba Domain Controller for example...

   See the Paul Ashton / Luke Leighton spec for terminology and let
     Al = the encrypted LanMan hash in the first 16 byte data block
          collected in C).
     An = the encrypted NT hash in the second 16 byte data block
          collected in C).

   When all the data is collected, feed it to the proper functions:
   - ntowf = owf(Ks(mpw, Cc, Cs), An)
   - lmowf = owf(Ks(mpw, Cc, Cs), Al)

   This code depends on:
   - smbencrypt from Samba package (1.9.17), which needs libdes (4.01)
   - arcfour (from SSH package (1.2.20))

   Please note that this code is *not* independant of processor
   architecture and has only been tested under linux on intel.

   Built from spec by Paul Ashton and Luke Leighton, posted to NTBugTraq:

   Any credit to the Samba team / Ashton / Leighton. 

   --Linus Nordberg (linus at incolumitas.se)

 #include <arcfour.h>

/* Calculate session key (Ks) from machine pw and client and server challenges 
void Ks(unsigned char *mpw, unsigned char *Cc, unsigned char *Cs, unsigned 
char *out)
  unsigned char Csum[8];
  unsigned char desout[8];

  /* Ks = E(PW[9..15],E(PW[0..6],Add(Cc,Cs))) */

  /* data in first E() is chalsum = "intel byte ordered addition of 
corresponding 4 byte words" in Cc and Cs */
  *((unsigned *) (Csum)) = *((unsigned *) (Cc)) + *((unsigned *) (Cs));
  *((unsigned *) (Csum + 4)) = *((unsigned *) (Cc + 4)) + *((unsigned *) (Cs + 

  /* first: key is pw[0..6], data is Csum */
  E1(mpw, Csum, desout);
  /* second: key is pw[9..15], data is output of first des encr */
  E1(mpw + 9, desout, out);


/* Calculate credentials (R) out of session key (Ks) and challenge (C) */
void R(unsigned char *Ks, unsigned char *C, unsigned char *out)
  unsigned char desout[8];
  unsigned char K[8];

  /* R = E(Ks[7..7,7],E(Ks[0..6],C)) */

  E1(Ks, C, desout);
  memset(K, 0, sizeof (K));
  K[0] = Ks[7];
  E1(K, desout, out);


/* Calculate owf out of session key (Ks) and authentication data (A) */
void owf(unsigned char *Ks, unsigned char *A, unsigned char *out)
  ArcfourContext ctx;
  unsigned char K[16];
  /* owf = rc4(Ks[0..7,16],16,A,16) */

  memset(K, 0, sizeof (K));
  memcpy(K, Ks, 8);

  memset(&ctx, 0, sizeof (ctx));
  arcfour_init(&ctx, K, 16);
  arcfour_decrypt(&ctx, out, A, 16);


/* Calculate new machine password, given Ks and 16 byte mess from the wire */
/* NOTE: This has not yet (1997-09-09) been verified. --linus */
void new_mpw(unsigned char *Ks, unsigned char *mess, unsigned char *out)
  /* newpw = rc4(Ks[0..7,16],16,mess,16) */
  /* in fact, it's the same as owf() */

  owf(Ks, mess, out);
/* Calculate initial WS account password, given the WS machine name */
void init_mpw(char *nm, unsigned char *out)
    Paul Ashton says:
    PW: md4(machine_password) == md4(lsadump $machine.acc)
                              == pwdump(machine$)
                  (initially) == md4(lmowf(unicode(machine)))

    Testing shows that the initial password is nothing but 
    md4(unicode(machine)). --linus
  E_md4hash(nm, out);

/* eof nta.c */

Linus Nordberg                | Email: linus at incolumitas.se
Incolumitas AB                | Phone: +46-8-656 24 40
Warfvinges väg 16             | GSM:   +46-706-75 74 72
S-112 51 STOCKHOLM, Sweden    | PGP Bits/KeyID: 1024/F854DAB9
PGP fingerprint: 6A CC 4A 59 7A 49 D5 02  38 35 43 09 5F C0 84 F7

Linus Nordberg                | Email: linus at incolumitas.se
Incolumitas AB                | Phone: +46-8-656 24 40
Warfvinges väg 16             | GSM:   +46-706-75 74 72
S-112 51 STOCKHOLM, Sweden    | PGP Bits/KeyID: 1024/F854DAB9
PGP fingerprint: 6A CC 4A 59 7A 49 D5 02  38 35 43 09 5F C0 84 F7

More information about the samba mailing list