/* Shellcode Detector (nop sled) This has proven to be substantially better than the snort experimental NOP signatures with fewer falses, and consumes very little CPU in testing on a loaded DS3. Beta testing has caught some new attacks using this detector and shown that turned up to high sensitivity levels it functions as a good lycos cookie, nntp, and streaming media detector too :-). It should detect any ordinary shellcode that has NOP sleds even it it is not polymorphically mutated, as well as codes mutated with K2's ADMmutate. The options on the preprocessor definitions can override the default sensitivity on a per port basis. (shorter = more sensitive) A sorted list of NOP codes can be found at http://cansecwest.com/noplist-v1-1.txt Please mail additions/edits to this list to dr@kyx.net. This preprocessor can be found at http://cansecwest.com/spp_fnord.c and will shortly be integrated into the snort 1.9 source tree. It should work with any version of snort 1.7 or later by following the integration instructions below. As usual send the inevitable bug reports to dr@kyx.net :-). Seems to be relatively stable in testing so far. cheers, --dr (see you at cansecwest/core02 in two months. :-) */ /*****************************/ /* spp_fnord: snort preprocessor - Multi-architecture mutated NOP sled detector - copyright 2002 Dragos Ruiu (dr@kyx.net) info on this at CanSecWest and at SANS Real World IDS Workshop. (a.k.a. Martycon :) Version 2.1 2002 February 26 */ /* LESS code == BETTER ! :-) */ /* USAGE: use this line in your snort.conf file to activate it preproc fnord: 80:768; 110:768; The format for the options is port:length; Lengths are rounded to modulo 8. Maximum is 2048 currently. Length 0 will disable checking on that port. Default length is 384 (MAXNOP) except for port 80 which is 768 (MAXNOPMORE). to use this module make sure you add spp_fnord.c and spp_fnord.o to the makefile objects and these lines in generators.h (maybe incrementing 114 to the next available highest number) #define GENERATOR_SPP_FNORD 114 #define FNORD_NOPSLED 1 and add the line below in plugbase.c in the routine InitPreprocessors() (void) SetupFnord(); */ #include "decode.h" #include <string.h> #include <stdlib.h> #include <limits.h> #define MAXNOP 384 /* NOTE this must be a multiple of 4!!!*/ #define MAXNOPMORE 768 /* This is the lower sensitivity level (also must be amultiple of 4!!!) */ #define MAXFUZZ 3 #define SKIP 0 #define BACKTRACK 1 #define SKIPIA32 11 #define SKIPHPPA 12 #define SKIPSPARC 13 #define WALK 20 #define WALKIA32 21 #define WALKHPPA 22 #define WALKSPARC 23 #define VAL (*pointer) #define CMP(x) (*pointer == x) #define CMPL(l,x) (*(pointer+l) == x) #define CMP2(x,y) ((*pointer == x) && (*(pointer+1) == y)) #define CMPL2(l,x,y) ((*(pointer+l) == x) && (*(pointer+l+1) == y)) #define CMP3(x,y,z) ((*pointer == x) && (*(pointer+1) == y) && (*(pointer+2) == z)) #define CMPL3(l,x,y,z) ((*(pointer+l) == x) && (*(pointer+l+1) == y) && (*(pointer+l+2) == z)) #define CMP4(x,y,z,q) ((*pointer == x) && (*(pointer+1) == y) && (*(pointer+2) == z) && (*(pointer+3) == q)) #define CMPL4(l,x,y,z,q) ((*(pointer+l) == x) && (*(pointer+l+1) == y) && (*(pointer+l+2) == z) && (*(pointer+l+3) == q)) #define INC(val) ((pointer < (max - val)) ? (pointer += val) : (pointer = max)) u_int8_t ports[65536]; void PreprocFnord(Packet *p); void FnordInit(); /*************Parsing Routines*************/ inline char *strquotchr(char *str, char c) { if(!str) return NULL; again: if(strchr(str,(int)'\"') && strchr(str,(int)'\"') < strchr(str,(int)c)) { str = strchr(str,(int)'\"'); if(*(str-1) == '\\') { str++; goto again; } if(!str || !*str) return NULL; while((*str != '\"' || (*str == '\"' && *((char*)str-1) != '\\')) && *str != c) { str++; if(!str || !*str) return NULL; } if(*str == c) return str; return(strquotchr(str,c)); } else return(strchr(str,(int)c)); } inline void splitstr(char *main[], char **split) { if(*split) { *((*split)++) = '\0'; while(isspace(**split)) (*split)++; } if(*main) while(isspace((*main)[strlen(*main)-1])) (*main)[strlen(*main)-1] = '\0'; } inline void trim(char *str[]) { if(*str) { while(isspace(**str)) (*str)++; while(isspace((*str)[strlen(*str)-1])) (*str)[strlen(*str)-1] = '\0'; } } void parseopts(char *opts) { char *x, *y, *tmp, *tmporg; int tmplen = 0; tmporg = NULL; if(opts && *opts) { tmplen = strlen(opts); tmp = calloc(tmplen+1, sizeof(char)); bcopy((void *) opts, (void *) tmp, tmplen); tmp[tmplen+1] = '\0'; tmporg = tmp; trim(&tmp); while(*tmp == ';') { *tmp++ = NULL; trim(&tmp); } } while(tmp && *tmp) { x = strquotchr(tmp,';'); splitstr(&tmp, &x); if(tmp && !*tmp) ErrorMessage("fnord init: Empty parameter before \';\', ignoring.\n"); else { if((y = strquotchr(tmp,':'))) { splitstr(&tmp, &y); if(tmp && !*tmp) ErrorMessage("fnord init: Empty port number before \':\', ignoring. \n"); else { if(y && !*y) ErrorMessage("fnord init: Empty parameter after \':\'.\n"); else { #ifdef DEBUG fprintf(stdout,"init: %d:%d\n", (int)(strtol(tmp,0,0)&0xffff), (int)(strtol(y,0,0)&0x7ff)); fflush(stdout); #endif /* DEBUG */ ports[strtol(tmp,0,0)&0xffff] = (u_int8_t) ((strtol(y,0,0)&0x7ff)>>3); } } // end if } // end if else ErrorMessage("fnord init: Expecting port:length after ; , ignoring.\n"); } // end if tmp = x; } // end while if(tmporg) { free(tmporg); } } // end parseopt /**************Snort Stupf****************/ /* * Function: SetupFnord() * Purpose: * Registers the preprocessor keyword and initialization function * into the preprocessor list. This is the function that gets called from * InitPreprocessors() in plugbase.c. * Arguments: None. * Returns: void function */ void SetupFnord() { RegisterPreprocessor("fnord", FnordInit); DebugMessage(DEBUG_STREAM, "Preprocessor: fnord is setup...\n"); } void FnordInit(u_char *opts) { int i; AddFuncToPreprocList(PreprocFnord); // default port sensitivity for(i = 0; i < 65536; i++) ports[i] = MAXNOP >> 3; // default lower port sensitivity ports[80] = MAXNOPMORE >> 3; // user overrides defaults parseopts(opts); } /******************* Main Logic *********************/ /* When adding codes please pay attention to */ /* overlaps and side effects in logic flow */ /****************************************************/ void PreprocFnord(Packet *p) { u_int8_t * pstart; register u_int8_t * pointer; u_int8_t * max; int fuzz, len, mode, plen, maxnop; Event event; if(!p || !p->pkth || !p->pkt) { if(pv.verbose_flag) { ErrorMessage("%s\n","Garbage Packet with Null Pointer discarded!"); } return; } /* check to make sure the IP header exists and that * there isn't a bad IP checksum */ if(!p->iph || (p->csum_flags & CSE_IP)) { return; } /* OK here we go, let's git us some mutants */ pstart = ((u_int8_t *)p->data); plen = p->dsize; pointer = pstart; max = pstart + plen - 4; mode = SKIP; fuzz = 0; len = 0; /* port based desensitizer */ maxnop = (ports[p->dp] > ports[p->sp] ? ports[p->dp] : ports[p->sp]) << 3; if(maxnop == 0) return; while(pointer < max) { #ifdef DEBUG fprintf(stdout,"pointer: %08X max: %08X count: %d val: %02X %02X %02X %02X len: %d mode: %d fuzz: %d\n", pointer, max, (plen - (max - pointer)), VAL, *(pointer+1), *(pointer+2), *(pointer+3), len, mode, fuzz); fflush(stdout); #endif /* DEBUG */ /* SPARC 4 byte nop detector */ /* note it is important to check these before intel because 0x96 and 0x98 overlap */ if( CMP3(0x20,0xBF,0xBF) || /* bn -random */ CMP3(0x81,0xD0,0x20) || /* tn random */ CMP4(0x89,0xA5,0x08,0x22) || /* fadds %f20,%f2,%f4*/ (CMP(0x96) && ( CMPL2(1,0x23,0x60) || /* sub %o5,0x42,%o3 */ CMPL3(1,0x24,0x80,0x12))) || /* sub %l2,%l2,%o3 */ CMP4(0x98,0x3E,0x80,0x12) || /* xnor %i2,%l2,%o4 */ CMP3(0xA0,0x26,0xE0) || /* sub %i3,0x42,%l0 */ (CMP(0xA2) && ( CMPL3(1,0x03,0x40,0x12) || /* add %o5,%l2,%l1 */ CMPL3(1,0x0E,0x80,0x13) || /* and %i2,%l3,%l1 */ CMPL3(1,0x1A,0x40,0x0A) || /* xor %o1,%o2,%l1 */ CMPL3(1,0x1C,0x80,0x12))) || /* xor %l2,%l2,%l1 */ (CMP(0xA4) && ( CMPL2(1,0x04,0xE0) || /* add %l3,0x42,%l2 */ CMPL3(1,0x27,0x40,0x12) || /* sub %i5,%l2,%l2 */ CMPL2(1,0x32,0xA0))) || /* orn %o2,0x42,%l2 */ (CMP(0xB2) && ( CMPL2(1,0x03,0x60) || /* add %o5,0x42,%i1 */ CMPL3(1,0x26,0x80,0x19))) || /* sub %i2,%i1,%i1 */ (CMP(0xB6) && ( CMPL3(1,0x06,0x40,0x1A) || /* add %i1,%i2,%i3 */ CMPL3(1,0x16,0x40,0x1A) || /* or %i1,%i2,%i3 */ CMPL3(1,0x04,0x80,0x12) || /* add %l2,%l2,%i3 */ CMPL2(1,0x03,0x60))) || /* add %o5,0x42,%i3 */ CMP3(0xBA,0x56,0xA0) /* umul %i2,0x42,%i5 */ ) { if(mode == SKIP) { mode = BACKTRACK; INC(0 - (fuzz + maxnop)); } else if(mode == WALKSPARC) { len += 4; INC(4); } else if(mode == SKIPSPARC) { mode = WALKSPARC; INC( - maxnop); } else { mode = SKIPSPARC; len = 0; fuzz = 0; INC(maxnop); } } /* HPPA nop detector */ else if( (CMP(0x08) && ( CMPL3(1,0x21,0x02,0x9A) || /* xor %r1,%r1,%r26 */ CMPL3(1,0x41,0x02,0x83) || /* xor %r1,%r2,%r3 */ CMPL3(1,0xA4,0x02,0x46))) || /* or %r4,%r5,%r6 */ (CMP(0x09) && ( CMPL3(1,0x04,0x06,0x8F) || /* shladd %r4,2,%r8,%r15 */ CMPL3(1,0x09,0x04,0x07) || /* sub %r9,%r8,%r7 */ CMPL3(1,0x6A,0x02,0x8C) || /* xor %r10,%r11,%12 */ CMPL3(1,0xCD,0x06,0x0F))) || /* add %r13,%r14,%r15 */ CMP4(0x94,0x6C,0xE0,0x84) || /* subi,OD 0x42,%r3,%r12 */ CMP4(0xD0,0xE8,0x0A,0xE9) || /* shrpw %r8,%r7,8,%r9 */ (CMP(0xB5) && ( CMPL2(1,0x03,0xE0) || /* addi,OD 0x42,%r8,%r3 */ CMPL2(1,0x4B,0xE0))) /* addi,OD 0x42,%r10,%r11 */ ) { if(mode == SKIP) { mode = BACKTRACK; INC(0 - (fuzz + maxnop)); } else if(mode == WALKHPPA) { len += 4; INC(4); } else if(mode == SKIPHPPA) { mode = WALKHPPA; INC( - maxnop); } else { mode = SKIPHPPA; len = 0; fuzz = 0; INC(maxnop); } } /* intel 3 byte with wildcard nop codes */ else if( CMP2(0x6B,0xC0) || /* imul N,%eax */ (CMP(0x83) && ( CMPL(1,0xE0) || /* and N,%eax */ CMPL(1,0xC8) || /* or N,%eax */ CMPL(1,0xE8) || /* sub N,%eax */ CMPL(1,0xF0) || /* xor N,%eax */ CMPL(1,0xF8) || /* cmp N,%eax */ CMPL(1,0xF9) || /* cmp N,%ecx */ CMPL(1,0xFA) || /* cmp N,%edx */ CMPL(1,0xFB) || /* cmp N,%ebx */ CMPL(1,0xC0))) || /* add N,%eax, N */ (CMP(0xC1) && ( CMPL(1,0xC0) || /* rol N,%eax */ CMPL(1,0xC8) || /* ror N,%eax */ CMPL(1,0xE8))) /* shr N,%eax */ ) { if(mode == SKIP) { mode = BACKTRACK; INC(0 - (fuzz + maxnop)); } else if(mode == WALKIA32) { len += 3; INC(3); } else if(mode == SKIPIA32) { mode = WALKIA32; INC(0 - (fuzz + maxnop)); } else { mode = SKIPIA32; len = 0; fuzz = 0; INC(maxnop); } } /* intel 2 byte nop codes */ else if( CMP2(0x33,0xC0) || /* xor %eax,%eax */ CMP2(0x85,0xC0) || /* test %eax,%eax */ (CMP(0x87) && ( CMPL(1,0xD2) || /* xchg %edx,%edx */ CMPL(1,0xDB) || /* xchg %ebx,%ebx */ CMPL(1,0xC9))) || /* xchg %ecx,%ecx */ (CMP(0x8C) && ( CMPL(1,0xC0) || /* mov %es,%eax */ CMPL(1,0xE0) || /* mov %fs,%eax */ CMPL(1,0xE8))) || /* mov %gs,%eax */ CMP(0xB0) || /* mov N,%eax */ CMP2(0xF7,0xD0) /* not %eax */ ) { if(mode == SKIP) { mode = BACKTRACK; INC(0 - (fuzz + maxnop)); } else if(mode == WALKIA32) { len += 2; INC(2); } else if(mode == SKIPIA32) { mode = WALKIA32; INC(0 - (fuzz + maxnop)); } else { mode = SKIPIA32; len = 0; fuzz = 0; INC(maxnop); } } /* one byte intel nop detector */ else if( ((VAL >= 0x3f) && (VAL <=0x60)) || /* inc, dec, push, pop */ ((VAL >= 0x90) && (VAL <=0x9F)) || /* nop, xchg, cwtl, fwait, pushf safh, lahf */ CMP(0x27) || /* daa "'" */ CMP(0x2F) || /* das "/" */ CMP(0x37) || /* aaa "7" */ CMP(0x60) || /* pusha "`" */ CMP(0xF5) || /* cmc */ CMP(0xF8) || /* clc */ CMP(0xF9) || /* stc */ CMP(0xFC) /* cld */ ) { if(mode == SKIP) { mode = BACKTRACK; INC(0 - (fuzz + maxnop)); } else if(mode == WALKIA32) { len += 1; INC(1); } else if(mode == SKIPIA32) { mode = WALKIA32; INC(0 - (fuzz + maxnop)); } else { mode = SKIPIA32; len = 0; fuzz = 0; INC(maxnop); } } else { /* NO NOP CODE */ if(mode == BACKTRACK) { INC(1); } else if(mode >= WALK) { mode = SKIP; INC(maxnop); fuzz = 0; } else { if(fuzz > MAXFUZZ || mode == SKIPHPPA || mode == SKIPSPARC) { mode = SKIP; INC(maxnop - fuzz); fuzz = 0; } else { /* only fuzz for SKIP and SKIPIA32 */ fuzz++; INC(1); } } } #define ALERT(msg) (CallAlertPlugins(p,msg,NULL,&event), CallLogPlugins(p,msg,NULL,&event)) if( len >= maxnop ) { SetEvent(&event, GENERATOR_SPP_FNORD, FNORD_NOPSLED, 1, 0, 0, 0); if(mode == WALKIA32) ALERT("spp_fnord: Possible Mutated IA32 NOP sled detected."); if(mode == WALKHPPA) ALERT("spp_fnord: Possible Mutated HPPA NOP sled detected."); if(mode == WALKSPARC) ALERT("spp_fnord: Possible Mutated SPARC NOP sled detected."); pointer = max; /* no real point in checking more now is there :-) */ } } } /***** end of spp_fnord.c *****/ -- --dr http://dragos.com/dr-dursec.asc CanSecWest/core02 - May 1-3 2002 - Vancouver B.C. - http://cansecwest.com