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

sp_asn1.c

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /**
00004 **  @file        sp_asn1.c
00005 **
00006 **  @author      Daniel Roelker <droelker@sourcefire.com>
00007 ** 
00008 **  @brief       Decode and detect ASN.1 types, lengths, and data.
00009 **
00010 **  Copyright (C) 2004, Daniel Roelker and Sourcefire, Inc.
00011 **
00012 **  This detection plugin adds ASN.1 detection functions on a per rule
00013 **  basis.  ASN.1 detection plugins can be added by editing this file and
00014 **  providing an interface in the configuration code.
00015 **  
00016 **  Detection Plugin Interface:
00017 **
00018 **  asn1: [detection function],[arguments],[offset type],[size]
00019 **
00020 **  Detection Functions:
00021 **
00022 **  bitstring_overflow: no arguments
00023 **  double_overflow:    no arguments
00024 **  oversize_length:    max size (if no max size, then just return value)
00025 **
00026 **  alert udp any any -> any 161 (msg:"foo"; \
00027 **      asn1: oversize_length 10000, absolute_offset 0;)
00028 **
00029 **  alert tcp any any -> any 162 (msg:"foo2"; \
00030 **      asn1: bitstring_overflow, oversize_length 500, relative_offset 7;)
00031 **
00032 **
00033 **  Note that further general information about ASN.1 can be found in
00034 **  the file doc/README.asn1.
00035 */
00036 
00037 #ifdef HAVE_CONFIG_H
00038 #include "config.h"
00039 #endif
00040 
00041 #include <sys/types.h>
00042 #include <stdlib.h>
00043 #include <ctype.h>
00044 
00045 #include "bounds.h"
00046 #include "rules.h"
00047 #include "decode.h"
00048 #include "plugbase.h"
00049 #include "parser.h"
00050 #include "debug.h"
00051 #include "util.h"
00052 #include "plugin_enum.h"
00053 #include "asn1.h"
00054 
00055 #define BITSTRING_OPT  "bitstring_overflow"
00056 #define DOUBLE_OPT     "double_overflow"
00057 #define LENGTH_OPT     "oversize_length"
00058 #define DBL_FREE_OPT   "double_free"
00059 
00060 #define ABS_OFFSET_OPT "absolute_offset"
00061 #define REL_OFFSET_OPT "relative_offset"
00062 #define PRINT_OPT      "print"
00063 
00064 #define ABS_OFFSET 1
00065 #define REL_OFFSET 2
00066 
00067 #define DELIMITERS " ,\t\n"
00068 
00069 typedef struct s_ASN1_CTXT
00070 {
00071     int bs_overflow;
00072     
00073     int double_overflow;
00074 
00075     int print;
00076 
00077     int length;
00078     unsigned int max_length;
00079 
00080     int offset;
00081     int offset_type;
00082 
00083 } ASN1_CTXT;
00084 
00085 extern u_int8_t *doe_ptr;
00086 
00087 /*
00088 **  NAME
00089 **    Asn1RuleParse::
00090 */
00091 /**
00092 **  Parse the detection option arguments.
00093 **    - bitstring_overflow
00094 **    - double_overflow
00095 **    - oversize_length
00096 **    - print
00097 **    - abs_offset
00098 **    - rel_offset
00099 **
00100 **  @return void
00101 */
00102 static void Asn1RuleParse(char *data, OptTreeNode *otn, ASN1_CTXT *asn1)
00103 {
00104     char *pcTok;
00105 
00106     if(!data)
00107     {
00108         FatalError("%s(%d) => No options to 'asn1' detection plugin.\n",
00109                    file_name, file_line);
00110     }
00111 
00112     pcTok = strtok(data, DELIMITERS);
00113     if(!pcTok)
00114     {
00115         FatalError("%s(%d) => No options to 'asn1' detection plugin.\n",
00116                    file_name, file_line);
00117     }
00118 
00119     while(pcTok)
00120     {
00121         if(!strcasecmp(pcTok, BITSTRING_OPT))
00122         {
00123             asn1->bs_overflow = 1;
00124         }
00125         else if(!strcasecmp(pcTok, DOUBLE_OPT))
00126         {
00127             asn1->double_overflow = 1;
00128         }
00129         else if(!strcasecmp(pcTok, PRINT_OPT))
00130         {
00131             asn1->print = 1;
00132         }
00133         else if(!strcasecmp(pcTok, LENGTH_OPT))
00134         {
00135             pcTok = strtok(NULL, DELIMITERS);
00136             if(!pcTok)
00137             {
00138                 FatalError("%s(%d) => No option to '%s' in 'asn1' detection "
00139                            "plugin\n", LENGTH_OPT, file_name, file_line);
00140             }
00141 
00142             asn1->length = 1;
00143             asn1->max_length = atoi(pcTok);
00144 
00145             if(asn1->max_length < 0)
00146             {
00147                 FatalError("%s(%d) => Negative size to '%s' in 'asn1' "
00148                            "detection plugin.  Must be positive or zero.\n", 
00149                            LENGTH_OPT, file_name, file_line);
00150             }
00151         }
00152         else if(!strcasecmp(pcTok, ABS_OFFSET_OPT))
00153         {
00154             pcTok = strtok(NULL, DELIMITERS);
00155             if(!pcTok)
00156             {
00157                 FatalError("%s(%d) => No option to '%s' in 'asn1' detection "
00158                            "plugin\n", ABS_OFFSET_OPT, file_name, file_line);
00159             }
00160 
00161             asn1->offset_type = ABS_OFFSET;
00162             asn1->offset = atoi(pcTok);
00163         }
00164         else if(!strcasecmp(pcTok, REL_OFFSET_OPT))
00165         {
00166             pcTok = strtok(NULL, DELIMITERS);
00167             if(!pcTok)
00168             {
00169                 FatalError("%s(%d) => No option to '%s' in 'asn1' detection "
00170                            "plugin\n", REL_OFFSET_OPT, file_name, file_line);
00171             }
00172 
00173             asn1->offset_type = REL_OFFSET;
00174             asn1->offset = atoi(pcTok);
00175         }
00176         else
00177         {
00178             FatalError("%s(%d) => Unknown ('%s') asn1 detection option.\n",
00179                        file_name, file_line, pcTok);
00180         }
00181 
00182         pcTok = strtok(NULL, DELIMITERS);
00183     }
00184 
00185     return;
00186 }
00187 
00188 /*
00189 **  NAME
00190 **    BitStringOverflow::
00191 */
00192 /**
00193 **  The neccessary info to detect possible bitstring overflows.  Thanks
00194 **  once again to microsoft for keeping us in business.
00195 **
00196 **  @return integer
00197 **
00198 **  @retval 0 failed
00199 **  @retval 1 detected
00200 */
00201 static int BitStringOverflow(ASN1_TYPE *asn1, void * user)
00202 {
00203     if(!asn1)
00204         return 0;
00205 
00206     /*
00207     **  Here's what this means:
00208     **
00209     **  If the ASN.1 type is a non-constructed bitstring (meaning that
00210     **  there is only one encoding, not multiple encodings).  And
00211     **  the number of bits to ignore (this is taken from the first byte)
00212     **  is greater than the total number of bits, then we have an
00213     **  exploit attempt.
00214     */
00215     if(asn1->ident.tag == SF_ASN1_TAG_BIT_STR && !asn1->ident.flag)
00216     {
00217         if(asn1->len.size && asn1->data && 
00218            (((asn1->len.size - 1)<<3) < (unsigned int)asn1->data[0]))
00219         {
00220             return 1;
00221         }
00222     }
00223 
00224     return 0;
00225 }
00226 
00227 /*
00228 **  NAME
00229 **    DetectBitStringOverflow::
00230 */
00231 /**
00232 **  This is just a wrapper to the traverse function.  It's important because
00233 **  this allows us to do more with individual nodes in the future.
00234 **
00235 **  @return integer
00236 **
00237 **  @retval 0 failed
00238 **  @rteval 1 detected
00239 */
00240 static int DetectBitStringOverflow(ASN1_TYPE *asn1)
00241 {
00242     return asn1_traverse(asn1, NULL, BitStringOverflow);
00243 }
00244 
00245 /*
00246 **  NAME
00247 **    DoubleOverflow::
00248 */
00249 /**
00250 **  This is the info to detect double overflows.  This may not be a
00251 **  remotely exploitable (remote services may not call the vulnerable
00252 **  microsoft function), but better safe than sorry.
00253 **
00254 **  @return integer
00255 **
00256 **  @retval 0 failed
00257 **  @retval 1 detected
00258 */
00259 static int DoubleOverflow(ASN1_TYPE *asn1, void *user)
00260 {
00261     if(!asn1)
00262         return 0;
00263 
00264     /*
00265     **  Here's what this does.
00266     **
00267     **  There is a vulnerablity in the MSASN1 library when decoding
00268     **  a double (real) type.  If the encoding is ASCII (specified by
00269     **  not setting bit 7 or 8), and the buffer is greater than 256,
00270     **  then you overflow the array in the function.
00271     */
00272     if(asn1->ident.tag == SF_ASN1_TAG_REAL && !asn1->ident.flag)
00273     {
00274         if(asn1->len.size && asn1->data &&
00275            ((asn1->data[0] & 0xc0) == 0x00) && 
00276            (asn1->len.size > 256))
00277         {
00278             return 1;
00279         }
00280     }
00281 
00282     return 0;
00283 }
00284 
00285 /*
00286 **  NAME
00287 **    DetectDoubleOverflow::
00288 */
00289 /**
00290 **  This is just a wrapper to the traverse function.  It's important because
00291 **  this allows us to do more with individual nodes in the future.
00292 **
00293 **  @return integer
00294 **
00295 **  @retval 0 failed
00296 **  @rteval 1 detected
00297 */
00298 static int DetectDoubleOverflow(ASN1_TYPE *asn1)
00299 {
00300     return asn1_traverse(asn1, NULL, DoubleOverflow);
00301 }
00302 
00303 /*
00304 **  NAME
00305 **    OversizeLength::
00306 */
00307 /**
00308 **  This is the most generic of our ASN.1 detection functionalities.  This
00309 **  will compare the ASN.1 type lengths against the user defined max
00310 **  length and alert if the length is greater than the user supplied length.
00311 **  
00312 **  @return integer
00313 **
00314 **  @retval 0 failed
00315 **  @retval 1 detected
00316 */
00317 static int OversizeLength(ASN1_TYPE *asn1, void *user)
00318 {
00319     unsigned int *max_size;
00320 
00321     if(!asn1 || !user)
00322         return 0;
00323 
00324     max_size = (unsigned int *)user;
00325 
00326     if(*max_size && *max_size <= asn1->len.size)
00327         return 1;
00328 
00329     return 0;
00330 }
00331 
00332 /*
00333 **  NAME
00334 **    DetectOversizeLength::
00335 */
00336 /**
00337 **  This is just a wrapper to the traverse function.  It's important because
00338 **  this allows us to do more with individual nodes in the future.
00339 **
00340 **  @return integer
00341 **
00342 **  @retval 0 failed
00343 **  @rteval 1 detected
00344 */
00345 static int DetectOversizeLength(ASN1_TYPE *asn1, unsigned int max_size)
00346 {
00347     return asn1_traverse(asn1, (void *)&max_size, OversizeLength);
00348 }
00349 
00350 /*
00351 **  NAME
00352 **    Asn1DetectFuncs::
00353 */
00354 /**
00355 **  The main function for adding ASN.1 detection type functionality.
00356 **
00357 **  @return integer
00358 **
00359 **  @retval 0 failed
00360 **  @retval 1 detected
00361 */
00362 static int Asn1DetectFuncs(ASN1_TYPE *asn1, ASN1_CTXT *ctxt, int dec_ret_val)
00363 {
00364     int iRet = 0;
00365 
00366     /*
00367     **  Print first, before we do other detection.  If print is the only
00368     **  option, then we want to evaluate this option as true and continue.
00369     **  Otherwise, if another option is wrong, then we 
00370     */
00371     if(ctxt->print)
00372     {
00373         asn1_traverse(asn1, NULL, asn1_print_types);
00374         iRet = 1;
00375     }
00376 
00377     /*
00378     **  Let's check the bitstring overflow.
00379     */
00380     if(ctxt->bs_overflow)
00381     {
00382         iRet = DetectBitStringOverflow(asn1);
00383         if(iRet)
00384             return 1;
00385     }
00386 
00387     if(ctxt->double_overflow)
00388     {
00389         iRet = DetectDoubleOverflow(asn1);
00390         if(iRet)
00391             return 1;
00392     }
00393 
00394     if(ctxt->length)
00395     {
00396         iRet = DetectOversizeLength(asn1, ctxt->max_length);
00397 
00398         /*
00399         **  If we didn't detect any oversize length in the decoded structs,
00400         **  that might be because we had a really overlong length that is
00401         **  bigger than our data type could hold.  In this case, it's 
00402         **  overlong too.
00403         */
00404         if(!iRet && dec_ret_val == ASN1_ERR_OVERLONG_LEN)
00405             iRet = 1;
00406 
00407         /*
00408         **  We add this return in here, so that we follow suit with the
00409         **  previous detections.  Just trying to short-circuit any future
00410         **  problems if we change the code flow here.
00411         */
00412         if(iRet)
00413             return 1;
00414     }
00415 
00416     return iRet;
00417 }
00418 
00419 /*
00420 **  NAME
00421 **    Asn1Detect::
00422 */
00423 /**
00424 **  The main snort detection function.  We grab the context ptr from the
00425 **  otn and go forth.  We check all the offsets to make sure we're in
00426 **  bounds, etc.
00427 **
00428 **  @return integer
00429 **
00430 **  @retval 0 failed
00431 **  @retval 1 detected
00432 */
00433 static int Asn1Detect(Packet *p, OptTreeNode *otn, OptFpList *fp_list)
00434 {
00435     ASN1_CTXT *ctxt;
00436     ASN1_TYPE *asn1;
00437     int iRet;
00438     unsigned int size;
00439     char *start;
00440     char *end;
00441     char *offset = NULL;
00442 
00443     /*
00444     **  Failed if there is no data to decode.
00445     */
00446     if(!p->data)
00447         return 0;
00448 
00449     ctxt = (ASN1_CTXT *)fp_list->context;
00450 
00451     start = p->data;
00452     end   = start + p->dsize;
00453 
00454     switch(ctxt->offset_type)
00455     {
00456         case REL_OFFSET:
00457             if(!doe_ptr)
00458             {
00459                 DEBUG_WRAP(DebugMessage(DEBUG_ASN1, "[*] No doe_ptr for "
00460                            "relative offset, so we are bailing.\n"););
00461                 return 0;
00462             }
00463                            
00464             /*
00465             **  Check that it is in bounds first.
00466             */
00467             if(!inBounds(start, end, doe_ptr))
00468             {
00469                 DEBUG_WRAP(DebugMessage(DEBUG_ASN1, "[*] ASN.1 bounds "
00470                            "check failed for doe_ptr.\n"););
00471                 return 0;
00472             }
00473 
00474             if(!inBounds(start, end, doe_ptr+ctxt->offset))
00475             {
00476                 DEBUG_WRAP(DebugMessage(DEBUG_ASN1, "[*] ASN.1 bounds "
00477                            "check failed doe_ptr+offset.\n"););
00478                 return 0;
00479             }
00480 
00481             offset = doe_ptr+ctxt->offset;
00482             break;
00483 
00484         case ABS_OFFSET:
00485         default:
00486             if(!inBounds(start, end, start+ctxt->offset))
00487             {
00488                 DEBUG_WRAP(DebugMessage(DEBUG_ASN1, "[*] ASN.1 bounds "
00489                            "check failed.\n"););
00490                 return 0;
00491             }
00492 
00493             offset = start+ctxt->offset;
00494             break;
00495     }
00496 
00497     /*
00498     **  Final Check.  We are good to go now.
00499     */
00500     if(!inBounds(start,end,offset))
00501     {
00502         DEBUG_WRAP(DebugMessage(DEBUG_ASN1, "[*] ASN.1 bounds "
00503                    "check failed.\n"););
00504         return 0;
00505     }
00506 
00507     /*
00508     **  Set size for asn1_decode().  This should never be -1 since
00509     **  we do the previous in bounds check.
00510     */
00511     size = end - offset;
00512 
00513     iRet = asn1_decode(offset, size, &asn1);
00514     if(iRet && !asn1)
00515     {
00516         DEBUG_WRAP(DebugMessage(DEBUG_ASN1, "[*] ASN.1 decode failed "
00517                    "miserably.\n"););
00518         return 0;
00519     }
00520 
00521     /*
00522     **  Let's do detection now.
00523     */
00524     if(Asn1DetectFuncs(asn1, ctxt, iRet))
00525         return fp_list->next->OptTestFunc(p, otn, fp_list->next);
00526 
00527     return 0;
00528 }
00529 
00530 static void Asn1Init(char *data, OptTreeNode *otn, int protocol)
00531 {
00532     ASN1_CTXT *asn1;
00533     OptFpList *ofl;
00534 
00535     /* 
00536      * allocate the data structure and attach 
00537      * it to the rule's data struct list 
00538      */
00539     asn1 = (ASN1_CTXT *)SnortAlloc(sizeof(ASN1_CTXT));
00540     memset(asn1, 0x00, sizeof(ASN1_CTXT));
00541 
00542     Asn1RuleParse(data, otn, asn1);
00543 
00544     ofl = AddOptFuncToList(Asn1Detect, otn);
00545 
00546     ofl->context = (void *)asn1;
00547 }
00548 
00549 void SetupAsn1()
00550 {
00551     /* map the keyword to an initialization/processing function */
00552     RegisterPlugin("asn1", Asn1Init);
00553 
00554     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: ASN1 Setup\n"););
00555 }
00556 

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