Home page logo
/

bugtraq logo Bugtraq mailing list archives

mutants! - spp_fnord.c (It can see the FNORDs! :-)
From: Dragos Ruiu <dr () kyx net>
Date: Fri, 1 Mar 2002 18:53:04 -0800

/* 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


  By Date           By Thread  

Current thread:
  • mutants! - spp_fnord.c (It can see the FNORDs! :-) Dragos Ruiu (Mar 05)
[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]