Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

spp_rpc_decode.c

Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017 */
00018 
00019 /* $Id$ */
00020 /* spp_rpc_decode 
00021  * 
00022  * Purpose:
00023  *
00024  * This preprocessor normalizes the RPC requests from remote machines by
00025  * converting all fragments into one continous stream.
00026  * This is very useful for doing things like defeating hostile attackers
00027  * trying to stealth themselves from IDSs by fragmenting the request so the
00028  * string 0186A0 is broken up.
00029  *
00030  * Arguments:
00031  *   
00032  * This plugin takes a list of integers representing the TCP ports that the
00033  * user is interested in having normalized
00034  *
00035  * Effect:
00036  *
00037  * Changes the data in the packet payload and changes
00038  * p->dsize to reflect the new (smaller) payload size.
00039  *
00040  * Comments:
00041  *
00042  */
00043 
00044 #ifdef HAVE_CONFIG_H     
00045 #include "config.h"      
00046 #endif
00047 
00048 #include <sys/types.h>
00049 #include <stdlib.h>
00050 #include <ctype.h>
00051 
00052 #ifdef HAVE_STRINGS_H    
00053 #include <strings.h>     
00054 #endif
00055 
00056 #include "decode.h"
00057 #include "plugbase.h"
00058 #include "parser.h"
00059 #include "log.h"
00060 #include "debug.h"
00061 #include "util.h"
00062 
00063 #include "mstring.h"
00064 #include "snort.h"
00065 #include "detect.h"
00066 #include "log.h"
00067 #include "generators.h"
00068 #include "event_queue.h"
00069 
00070 extern char *file_name;
00071 extern int file_line;
00072 extern int do_detect;
00073 
00074 #define OPT_ALERT_FRAGMENTS "alert_fragments"
00075 #define OPT_ALERT_MULTIPLE_REQUESTS "no_alert_multiple_requests"
00076 #define OPT_ALERT_LARGE_FRAGMENTS "no_alert_large_fragments"
00077 #define OPT_ALERT_INCOMPLETE "no_alert_incomplete"
00078 
00079 #define TEXT_ALERT_MULTIPLE_REQUESTS "alert_multiple_requests"
00080 #define TEXT_ALERT_LARGE_FRAGMENTS "alert_large_fragments"
00081 #define TEXT_ALERT_INCOMPLETE "alert_incomplete"
00082 
00083 #define RPC_CLASS DECODE_CLASS /* use the same classification as the other decoder alerts */
00084 
00085 typedef struct _RpcDecodeData
00086 {
00087     char alert_fragments;    /* Alert when we see ANY fragmented RPC requests */
00088     char alert_incomplete; /* Alert when we don't see all of a request in one packet */
00089     char alert_multi;        /* Alert when we see multiple requests in one packet */
00090     char alert_large;        /* Alert when we see multiple requests in one packet */
00091 } RpcDecodeData;
00092 
00093 static RpcDecodeData rpcpreprocdata; /* Configuration Set */
00094 static char RpcDecodePorts[65536/8];
00095 
00096 void RpcDecodeInit(u_char *);
00097 void RpcDecodeInitIgnore(u_char *);
00098 void PreprocRpcDecode(Packet *, void *);
00099 void SetRpcPorts(char *);
00100 int ConvertRPC(Packet *);
00101 
00102 /*
00103  * Function: SetupRpcDecode()
00104  *
00105  * Purpose: Registers the preprocessor keyword and initialization 
00106  *          function into the preprocessor list.
00107  *
00108  * Arguments: None.
00109  *
00110  * Returns: void function
00111  *
00112  */
00113 void SetupRpcDecode()
00114 {
00115     /* link the preprocessor keyword to the init function in 
00116        the preproc list */
00117     RegisterPreprocessor("rpc_decode", RpcDecodeInit);
00118 
00119     DEBUG_WRAP(DebugMessage(DEBUG_RPC,"Preprocessor: RpcDecode in setup...\n"););
00120 }
00121 
00122 
00123 /*
00124  * Function: RpcDecodeInit(u_char *)
00125  *
00126  * Purpose: Processes the args sent to the preprocessor, sets up the
00127  *          port list, links the processing function into the preproc
00128  *          function list
00129  *
00130  * Arguments: args => ptr to argument string
00131  *
00132  * Returns: void function
00133  *
00134  */
00135 void RpcDecodeInit(u_char *args)
00136 {
00137     DEBUG_WRAP(DebugMessage(DEBUG_RPC,"Preprocessor: RpcDecode Initialized\n"););
00138 
00139     bzero(&rpcpreprocdata,sizeof(RpcDecodeData));
00140 
00141     /* turn on the following alerts by default */
00142     rpcpreprocdata.alert_multi = 1;
00143     rpcpreprocdata.alert_incomplete = 1;
00144     rpcpreprocdata.alert_large = 1;
00145     
00146     /* parse the argument list into a list of ports to normalize */
00147     SetRpcPorts(args);
00148 
00149     /* Set the preprocessor function into the function list */
00150     AddFuncToPreprocList(PreprocRpcDecode);
00151 }
00152 
00153 /*
00154  * Function: SetRpcPorts(char *)
00155  *
00156  * Purpose: Reads the list of port numbers from the argument string and
00157  *          parses them into the port list data struct
00158  *
00159  * Arguments: portlist => argument list
00160  *
00161  * Returns: void function
00162  *
00163  */
00164 void SetRpcPorts(char *portlist)
00165 {
00166     char portstr[STD_BUF];
00167     char **toks;
00168     int is_reset = 0;
00169     int num_toks;
00170     int num;
00171 
00172     if(portlist == NULL || *portlist == '\0')
00173     {
00174         portlist = "111 32771";
00175     }
00176 
00177     /* tokenize the argument list */
00178     toks = mSplit(portlist, " ", 31, &num_toks, '\\');
00179 
00180     LogMessage("rpc_decode arguments:\n");
00181     
00182     /* convert the tokens and place them into the port list */
00183     for(num = 0; num < num_toks; num++)
00184     {
00185         if(isdigit((int)toks[num][0]))
00186         {
00187             char *num_p = NULL; /* used to determine last position in string */
00188             long t_num;
00189 
00190             t_num = strtol(toks[num], &num_p, 10);
00191 
00192             if(*num_p != '\0')
00193             {
00194                 FatalError("ERROR %s(%d) => Port Number invalid format: %s\n",
00195                            file_name, file_line, toks[num]);
00196             }
00197             else if(t_num < 0 || t_num > 65535)
00198             {
00199                 FatalError("ERROR %s(%d) => Port Number out of range: %ld\n",
00200                            file_name, file_line, t_num);
00201             }
00202 
00203             /* user specified a legal port number and it should override the default
00204                port list, so reset it unless already done */
00205             if(!is_reset)
00206             {
00207                 bzero(&RpcDecodePorts, sizeof(RpcDecodePorts));
00208                 portstr[0] = '\0';
00209                 is_reset = 1;
00210             }
00211 
00212             /* mark this port as being interesting using some portscan2-type voodoo,
00213                and also add it to the port list string while we're at it so we can
00214                later print out all the ports with a single LogMessage() */
00215             RpcDecodePorts[(t_num/8)] |= 1<<(t_num%8);
00216             strlcat(portstr, toks[num], STD_BUF - 1);
00217             strlcat(portstr, " ", STD_BUF - 1);
00218         }
00219         else if(!strcasecmp(OPT_ALERT_MULTIPLE_REQUESTS,toks[num]))
00220         {
00221             rpcpreprocdata.alert_multi = 0;
00222         }
00223         else if(!strcasecmp(OPT_ALERT_INCOMPLETE,toks[num]))
00224         {
00225             rpcpreprocdata.alert_incomplete = 0;
00226         }
00227         else if(!strcasecmp(OPT_ALERT_LARGE_FRAGMENTS,toks[num]))
00228         {
00229             rpcpreprocdata.alert_large = 0;
00230         }
00231         else if(!strcasecmp(OPT_ALERT_FRAGMENTS,toks[num]))
00232         {
00233             rpcpreprocdata.alert_fragments = 1;
00234         }
00235         else
00236         {
00237             FatalError("ERROR %s(%d) => Unknown argument to rpc_decode "
00238                        "preprocessor: \"%s\"\n",
00239                        file_name, file_line, toks[num]);
00240         }
00241     }
00242 
00243     mSplitFree(&toks, num_toks);
00244 
00245     /* print out final port list */
00246     LogMessage("    Ports to decode RPC on: %s\n", portstr);
00247     LogMessage("    %s: %s\n", OPT_ALERT_FRAGMENTS, rpcpreprocdata.alert_fragments ? "ACTIVE": "INACTIVE");
00248     LogMessage("    %s: %s\n", TEXT_ALERT_LARGE_FRAGMENTS, rpcpreprocdata.alert_large ? "ACTIVE": "INACTIVE");
00249     LogMessage("    %s: %s\n", TEXT_ALERT_INCOMPLETE, rpcpreprocdata.alert_incomplete ? "ACTIVE": "INACTIVE");
00250     LogMessage("    %s: %s\n", TEXT_ALERT_MULTIPLE_REQUESTS, rpcpreprocdata.alert_multi ? "ACTIVE": "INACTIVE");
00251 }                                                                               
00252    
00253 
00254 /*
00255  * Function: PreprocRpcDecode(Packet *)
00256  *
00257  * Purpose: Inspects the packet's payload for fragment records and 
00258  *          converts them into one infragmented record.
00259  *
00260  * Arguments: p => pointer to the current packet data struct 
00261  *
00262  * Returns: void function
00263  *
00264  */
00265 void PreprocRpcDecode(Packet *p, void *context)
00266 {
00267     int ret = 0; /* return code for ConvertRPC */
00268     
00269     DEBUG_WRAP(DebugMessage(DEBUG_RPC,"rpc decoder init on %d bytes\n", p->dsize););
00270 
00271     /* check to make sure we're talking TCP and that the TWH has already
00272        completed before processing anything */
00273     if(!PacketIsTCP(p))
00274     {
00275         DEBUG_WRAP(DebugMessage(DEBUG_RPC,"It isn't TCP session traffic\n"););
00276         return;
00277     }
00278 
00279     if (!(p->preprocessors & PP_RPCDECODE))
00280     {
00281         DEBUG_WRAP(DebugMessage(DEBUG_RPC,"Packet isn't taged for RPC inspection\n"););
00282         return;
00283     }
00284 
00285     if((snort_runtime.capabilities.stateful_inspection == 1) &&
00286        (p->packet_flags & PKT_FROM_SERVER))
00287     {
00288         DEBUG_WRAP(DebugMessage(DEBUG_RPC,"This is from a server\n"););
00289         return;
00290     }
00291 
00292 
00293     /* check the port list */
00294     if(!(RpcDecodePorts[(p->dp/8)] & (1<<(p->dp%8))))
00295     {
00296         return;
00297     }
00298 
00299     ret = ConvertRPC(p);
00300     DEBUG_WRAP(DebugMessage(DEBUG_RPC,"Got ret: %d from ConvertRPC\n", ret););
00301     
00302     if(ret != 0)
00303     {
00304         switch(ret)
00305         {
00306         case RPC_FRAG_TRAFFIC:
00307             if(rpcpreprocdata.alert_fragments)
00308             {
00309                 SnortEventqAdd(GENERATOR_SPP_RPC_DECODE, RPC_FRAG_TRAFFIC, 
00310                         1, RPC_CLASS, 3, RPC_FRAG_TRAFFIC_STR, 0);
00311             }
00312             break;
00313         case RPC_MULTIPLE_RECORD:
00314             if(rpcpreprocdata.alert_multi)
00315             {
00316                 SnortEventqAdd(GENERATOR_SPP_RPC_DECODE, RPC_MULTIPLE_RECORD, 
00317                         1, RPC_CLASS, 3, RPC_MULTIPLE_RECORD_STR, 0);
00318             }
00319             break;
00320         case RPC_LARGE_FRAGSIZE:
00321             if(rpcpreprocdata.alert_large)
00322             {
00323                 SnortEventqAdd(GENERATOR_SPP_RPC_DECODE, RPC_LARGE_FRAGSIZE, 
00324                         1, RPC_CLASS, 3, RPC_LARGE_FRAGSIZE_STR, 0);
00325             }
00326             break;
00327         case RPC_INCOMPLETE_SEGMENT:
00328             if(rpcpreprocdata.alert_incomplete)
00329             {
00330                 SnortEventqAdd(GENERATOR_SPP_RPC_DECODE, RPC_INCOMPLETE_SEGMENT, 
00331                         1, RPC_CLASS, 3, RPC_INCOMPLETE_SEGMENT_STR, 0);
00332             }
00333             break;
00334         case RPC_ZERO_LENGTH_FRAGMENT:
00335             if(rpcpreprocdata.alert_multi)
00336             {
00337                 SnortEventqAdd(GENERATOR_SPP_RPC_DECODE, RPC_ZERO_LENGTH_FRAGMENT, 
00338                         1, RPC_CLASS, 3, RPC_ZERO_LENGTH_FRAGMENT_STR, 0);
00339             }
00340             break;
00341         }
00342     }
00343     
00344     return;    
00345 }
00346 
00347 /* most significant bit */
00348 #define MSB 0x80000000
00349 
00350 /*
00351  * For proto ref, see rfc1831 section 10 and page 445 UNP vol2
00352  *  
00353  * check to make sure we've got enough data to process a record
00354  *
00355  * Where did the original 16 come from?  It seems that it could be
00356  * a last frag of 0 length according to spec.
00357  *
00358  * The minimum "valid" packet for us is 8 fields * 4 bytes
00359  *
00360  * This decoder is ignorant of TCP state so we'll have to assume
00361  * that reassembled TCP stuff is reinjected to the preprocessor
00362  * chain
00363  *
00364  * This decoder is also ignorant of multiple RPC requests in a
00365  * single stream.  To compensate, we can configure alerts
00366  *
00367  * Additionally, we don't do anything to verify that this is
00368  * really an RPC service port so we don't decode anything that
00369  * happens as a result
00370  *
00371  * From rfc1831:
00372  *
00373  *  Fragment Header ( 1 flag bit, 31 bit uint )
00374  *     RPC Body
00375  *  
00376  *        unsigned int xid 
00377  *        struct call_body {
00378  *             unsigned int rpcvers;  // must be equal to two (2) 
00379  *             unsigned int prog;
00380  *             unsigned int vers;
00381  *             unsigned int proc;
00382  *             opaque_auth  cred;
00383  *             opaque_auth  verf;
00384  *        }
00385  */
00386 
00387 int ConvertRPC(Packet *p)
00388 {
00389     u_int8_t *data = p->data;   /* packet data */
00390     u_int16_t *size = &(p->dsize); /* size of packet data */
00391     u_int8_t *rpc;       /* this is where the converted data will be written */
00392     u_int8_t *index;     /* this is the index pointer to walk thru the data */
00393     u_int8_t *end;       /* points to the end of the payload for loop control */
00394     u_int16_t psize = *size;     /* payload size */
00395     int i = 0;           /* loop counter */
00396     int length;          /* length of current fragment */
00397     int last_fragment = 0; /* have we seen the last fragment sign? */
00398     int decoded_len = 4; /* our decoded length is always atleast a 0 byte header */
00399     u_int32_t fraghdr;   /* Used to store the RPC fragment header data */
00400     int fragcount = 0;   /* How many fragment counters have we seen? */
00401     
00402     if(psize < 32)
00403     {
00404         DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Returning due to"
00405                                 " small packet size: %d\n ", psize););
00406         return 0;
00407     }
00408 
00409     /* on match, normalize the data */
00410     DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Got RPC traffic (%d bytes)!\n", psize););
00411 
00412     /* cheesy alignment safe fraghdr = *(uint32_t *) data*/
00413     *((u_int8_t *) &fraghdr)       = data[0];
00414     *(((u_int8_t *) &fraghdr) + 1) = data[1];
00415     *(((u_int8_t *) &fraghdr) + 2) = data[2];
00416     *(((u_int8_t *) &fraghdr) + 3) = data[3];
00417     
00418 
00419     /* The fragment header is 4 bytes in network byte order */
00420     fraghdr = ntohl(fraghdr);
00421     length = fraghdr & 0x7FFFFFFF;
00422     
00423     /* Check to see if we are on the last fragment */
00424     if(fraghdr & MSB)
00425     {
00426         /* on match, normalize the data */
00427         DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Found Last Fragment: %u!\n",fraghdr););
00428 
00429         if((length + 4 != psize) && !(p->packet_flags & PKT_REBUILT_STREAM))
00430         {
00431             DEBUG_WRAP(DebugMessage(DEBUG_RPC, "It's not the only thing in this buffer!"
00432                                     " length: %d psize: %d!\n", length, psize););            
00433             return RPC_MULTIPLE_RECORD;
00434         }
00435         else if ( length == 0 )
00436         {
00437             DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Zero-length RPC fragment detected."
00438                                     " length: %d psize: %d.\n", length, psize););            
00439             return RPC_ZERO_LENGTH_FRAGMENT;
00440         }
00441         return 0;
00442     }
00443     else if(rpcpreprocdata.alert_fragments)
00444     {
00445         return RPC_FRAG_TRAFFIC;
00446     }
00447 
00448     rpc =   (u_int8_t *) data;
00449     index = (u_int8_t *) data;
00450     end =   (u_int8_t *) data + psize;
00451 
00452 
00453     /* now we know it's in fragmented records, 4 bytes of 
00454      * header(of which the most sig bit fragment (0=yes 1=no). 
00455      * The header is followed by the value move pointer up 4 
00456      * bytes, we need to stuff header in first 4 bytes.  
00457      * But the header has the total length...we don't know 
00458      * until the end 
00459      */
00460     
00461     /* This is where decoded data will be written */
00462     rpc += 4;
00463 
00464     /* always make sure that we have enough data to process atleast
00465      * the header and that we only process at most, one fragment
00466      */
00467     
00468     while(((end - index) >= 4) && (last_fragment == 0))
00469     {
00470         /* get the fragment length (31 bits) and move the pointer to
00471            the start of the actual data */
00472         
00473         *((u_int8_t *) &fraghdr)       = index[0];
00474         *(((u_int8_t *) &fraghdr) + 1) = index[1];
00475         *(((u_int8_t *) &fraghdr) + 2) = index[2];
00476         *(((u_int8_t *) &fraghdr) + 3) = index[3];
00477 
00478         fraghdr = ntohl(fraghdr);
00479         length = fraghdr & 0x7FFFFFFF;
00480         
00481         /* move the current index into the packet past the
00482            fragment header */
00483         index += 4; 
00484         
00485         if(fraghdr & MSB)
00486         {
00487             DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Last Fragment detected\n"););
00488             last_fragment = 1;
00489         }
00490 
00491         if((length + decoded_len) < decoded_len)
00492         {
00493             /* don't allow integer overflow to confuse us.  Should be
00494              * caught by length > psize but who knows when weird
00495              * psize's might be allowed */
00496             
00497             DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Integer Overflow"
00498                                     " field(%d) exceeds packet size(%d)\n",
00499                                     length, psize););
00500             return RPC_LARGE_FRAGSIZE;
00501         }
00502 
00503         decoded_len += length;
00504 
00505         if(length > psize)
00506         {
00507             DEBUG_WRAP(DebugMessage(DEBUG_RPC, "Length of"
00508                                     " field(%d) exceeds packet size(%d)\n",
00509                                     length, psize););
00510 
00511             
00512             return RPC_INCOMPLETE_SEGMENT;
00513         }
00514         else if(decoded_len > psize)
00515         {
00516             /* The entire request is larger than our current packet
00517              *  size
00518              */
00519             DEBUG_WRAP(DebugMessage(DEBUG_RPC, " Decoded Length (%d)"
00520                                     "exceeds packet size(%d)\n",
00521                                     decoded_len, psize););
00522             return RPC_LARGE_FRAGSIZE;
00523         }
00524         else if((index + length) > end)
00525         {
00526             DEBUG_WRAP(DebugMessage(DEBUG_RPC,
00527                                     "returning LARGE_FRAGSIZE"
00528                                     "since we'd read past our end\n"););
00529             return RPC_LARGE_FRAGSIZE;
00530         }
00531         else
00532         {
00533             fragcount++;
00534             
00535             DEBUG_WRAP(DebugMessage(DEBUG_RPC,
00536                                     "length: %d size: %d decoded_len: %d\n",
00537                                     length, psize, decoded_len););                        
00538 
00539             if(fragcount == 1)
00540             {
00541                 /* adjust the indexes because the records are already
00542                  * in the right spot */
00543                 rpc += length;
00544                 index += length; /* index is checked against the end above */
00545             }
00546             else
00547             {                
00548                 for (i=0; i < length; i++, rpc++, index++)
00549                 {
00550                     *rpc = *index;
00551                 }
00552             }
00553         }
00554     }
00555 
00556     /* rewrite the header on the request packet */
00557     /* move the fragment header back onto the data */
00558 
00559     
00560     fraghdr = ntohl(decoded_len); /* size */
00561 
00562     data[0] = *((u_int8_t *) &fraghdr);
00563     data[1] = *(((u_int8_t *) &fraghdr) + 1);
00564     data[2] = *(((u_int8_t *) &fraghdr) + 2);
00565     data[3] = *(((u_int8_t *) &fraghdr) + 3);
00566     
00567     data[0] |=  0x80;             /* Mark as unfragmented */
00568     
00569     /* is there another request encoded that is trying to evade us by doing
00570      *
00571      * frag last frag [ more data ]?
00572      */
00573     if(decoded_len + ((fragcount - 1) * 4) != psize)
00574     {
00575         DEBUG_WRAP(DebugMessage(DEBUG_RPC, "decoded len does not compute: %d\n",
00576                                 decoded_len););
00577         return RPC_MULTIPLE_RECORD;
00578     }
00579 
00580     
00581     /* set the payload size to reflect the new size
00582      *
00583      * sizeof(Header) + total payload size of a single message
00584      */
00585     *size = decoded_len; /* this potentially throws away data... */
00586 
00587     DEBUG_WRAP(DebugMessage(DEBUG_RPC, "New size: %d\n", decoded_len);
00588                DebugMessage(DEBUG_RPC, "converted data:\n");
00589                //PrintNetData(stdout, data, decoded_len);
00590                );
00591     return 0;
00592 }
00593 

Generated on Sun May 14 14:51:17 2006 by  doxygen 1.4.2