Luke Kenneth Casson Leighton
1997-Aug-29 11:21 UTC
NT domain member to domain controller authentication protocol
Paul Ashton <paul@argo.demon.co.uk> and Luke Leighton <lkcl@switchboard.net> 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 Protocol -------- C->S ReqChal,Cc S->C Cs C & S compute session key Ks = E(PW[9..15],E(PW[0..6],Add(Cc,Cs))) C: Rc = Cred(Ks,Cc) C->S Authenticate,Rc S: Rs = Cred(Ks,Cs), assert(Rc == Cred(Ks,Cc)) S->C Rs C: assert(Rs == Cred(Ks,Cs)) On joining the domain the client will optionally attempt to change its password and the domain controller may refuse to update it depending on registry settings. This will also occur weekly afterwards. C: Tc = Time(), Rc' = Cred(Ks,Rc+Tc) C->S ServerPasswordSet,Rc',Tc, rc4(Ks[0..7,16],lmowf(randompassword()) C: Rc = Cred(Ks,Rc+Tc+1) S: assert(Rc' == Cred(Ks,Rc+Tc)), Ts = Time() S: Rs' = Cred(Ks,Rs+Tc+1) S->C Rs',Ts C: assert(Rs' == Cred(Ks,Rs+Tc+1)) S: Rs = Rs' User: U with password P wishes to login to the domain (incidental data such as workstation and domain omitted) C: Tc = Time(), Rc' = Cred(Ks,Rc+Tc) C->S NetLogonSamLogon,Rc',Tc,U, rc4(Ks[0..7,16],16,ntowf(P),16), rc4(Ks[0..7,16],16,lmowf(P),16) S: assert(Rc' == Cred(Ks,Rc+Tc)) assert(passwords match those in SAM) S: Ts = Time() S->C Cred(Ks,Cred(Ks,Rc+Tc+1)),userinfo(logon script,UID,SIDs,etc) C: assert(Rs == Cred(Ks,Cred(Rc+Tc+1)) C: Rc = Cred(Ks,Rc+Tc+1) Comments -------- On first joining the domain the session key could be computed by anyone listening in on the network as the machine password has a well known value. Until the machine is rebooted it will use this session key to encrypt NT and LM one way functions of passwords which are password equivalents. Any user who logs in before the machine has been rebooted a second time will have their password equivalent exposed. Of course the new machine password is exposed at this time anyway. None of the returned user info such as logon script, profile path and SIDs *appear* to be protected by anything other than the TCP checksum. The server time stamps appear to be ignored. The client sends a ReturnAuthenticator in the SamLogon request which I can't find a use for. However its time is used as the timestamp returned by the server. The password OWFs should NOT be sent over the network reversibly encrypted. They should be sent using RC4(Ks,md4(owf)) with the server computing the same function using the owf values in the SAM.
Linus Nordberg
1997-Sep-09 06:32 UTC
NT domain member to domain controller authentication protocol
Paul Ashton wrote:>Paul Ashton <paul@argo.demon.co.uk> and >Luke Leighton <lkcl@switchboard.net> 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 credentialsHi 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? 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 <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 + 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 Nordberg | Email: linus@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@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 ------------------------------------------------------------------