--- To:NTBUGTRAQ@NTADVICE.COM cc:samba@samba.anu.edu.au Subject: Re: NT domain member to domain controller authentication protocol -------- Paul Ashton wrote: >Paul Ashton and >Luke Leighton present the NT domain >authentication protocol. Comments and corrections are welcome. > >Definitions >----------- > >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 not so clever way of setting the initial password. Does anybody know of any way to change this behavior? Enjoy, --linus ------------ /* 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) and B1) Password for DM domain account (to calculate session key (Ks) from the challenges) Use lsadump from Paul Ashton: http://ntbugtraq.rc.on.ca/SCRIPTS/WA.EXE?A2=ind9708&L=ntbugtraq&O=T&P=1230 Usage: lsadump $machine.acc [machine] You need admin rights on the machine to do this, so the following may be the only option: or B2) Packet dump of (1) when the DM joined the domain (to get the initial session key) The challenges can be found in ... and (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. and C) Packet dump of the user network logon (to get the session key encrypted hashes) 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: http://ntbugtraq.rc.on.ca/SCRIPTS/WA.EXE?A2=ind9708&L=ntbugtraq&O=A&P=2935 Any credit to the Samba team / Ashton / Leighton. --Linus Nordberg (linus@incolumitas.se) */ #include /* 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 + 4)); /* 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); return; } /* 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); return; } /* 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); return; } /* 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); return; } /* 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); return; } /* eof nta.c */ --- --linus PGP Bits/KeyID: 1024/F854DAB9 Fingerprint: 6A CC 4A 59 7A 49 D5 02 38 35 43 09 5F C0 84 F7