mutants! - spp_fnord.c (It can see the FNORDs! :-)

From: Dragos Ruiu (drat_private)
Date: Fri Mar 01 2002 - 18:53:04 PST

  • Next message: Harmen van der Wal: "Java HTTP proxy vulnerability"

    /* 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 drat_private
    
     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 drat_private :-).  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 (drat_private) 
    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
    



    This archive was generated by hypermail 2b30 : Tue Mar 05 2002 - 01:21:16 PST