/* detection functions */
int ruleCITRIX_METAFRAME_BOeval(void *p) {
    const uint8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload;

    const uint8_t *cursor_extract = 0;
    uint32_t event_data_length, description_length, encr_data_length;

    SFSnortPacket *sp = (SFSnortPacket *) p;

    // flow:established, to_server;
    if (checkFlow(p, ruleCITRIX_METAFRAME_BOoptions[0]->option_u.flowFlags) > 0 ) {

        // content:"A|80 00 00 02|", offset 28, depth 5;
        if (contentMatch(p, ruleCITRIX_METAFRAME_BOoptions[1]->option_u.content, &cursor_normal) > 0) {

            // byte_test:size 4, value 16, operator <, offset 4, endian little;
            if (byteTest(p, ruleCITRIX_METAFRAME_BOoptions[2]->option_u.byte, cursor_normal) > 0) {

                if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
                     return RULE_NOMATCH;

                if((end_of_payload - beg_of_payload) < 0x28)
                   return RULE_NOMATCH;


                // Extract Event Data Length ( 4 bytes little endian )
                cursor_extract = &(beg_of_payload[0x0008]);
                event_data_length  = (*cursor_extract++) & 0xFF;
                event_data_length |= ((*cursor_extract++) & 0xFF) << 8;
                event_data_length |= ((*cursor_extract++) & 0xFF) << 16;
                event_data_length |= ((*cursor_extract++) & 0xFF) << 24;

                // Extract Description Length ( 2 bytes little endian )
                cursor_extract = &(beg_of_payload[0x0022]);
                description_length  = (*cursor_extract++) & 0xFF;
                description_length |= ((*cursor_extract++) & 0xFF) << 8;

		// extract Encrypted Data Length ( 4 bytes little endian )
                cursor_extract = &(beg_of_payload[0x00024]);
                encr_data_length  = (*cursor_extract++) & 0xFF;
                encr_data_length |= ((*cursor_extract++) & 0xFF) << 8;
                encr_data_length |= ((*cursor_extract++) & 0xFF) << 16;
                encr_data_length |= ((*cursor_extract++) & 0xFF) << 24;

                if(description_length + encr_data_length > event_data_length)
                   return RULE_MATCH;
            }
        }
    }
    return RULE_NOMATCH;
}
/* detection functions */
int rule15327eval(void *p) {
    
   const uint8_t *cursor_normal = 0;
   const uint8_t *end_of_payload;

   uint16_t numanswers;
   uint16_t numqueries;
	
   uint16_t len;
   uint32_t total = 0;

   int i;

   SFSnortPacket *sp = (SFSnortPacket *) p;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
    
   // flow:to_client;
   if (checkFlow(p, rule15327options[0]->option_u.flowFlags) > 0 ) {
      // byte_test:size 2, value 7999, operator >, offset 2;
      if (byteTest(p, rule15327options[1]->option_u.byte, cursor_normal) > 0) {
         
         if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
            return RULE_NOMATCH;  
			
         if ((cursor_normal+12) > end_of_payload)
            return RULE_NOMATCH;

         // Extract number of query and answer records
         numqueries = read_big_16(cursor_normal+4);
         numanswers = read_big_16(cursor_normal+6);
 
         cursor_normal += 12;			

         // Iterate through variable-sized query records to skip to the answer records.
         for(i = 0; i < numqueries; i++)
         {
            while(cursor_normal < end_of_payload && *cursor_normal != 0 && !((*cursor_normal & 0xc0) == 0xc0)) {
               cursor_normal += *cursor_normal + 1;
            }

            if (cursor_normal >= end_of_payload)
               return RULE_NOMATCH;

            cursor_normal += ((*cursor_normal & 0xc0) == 0xc0) ? 2 : 1 ;
            cursor_normal += 2+2;
         }

         if(cursor_normal >= end_of_payload)
            return RULE_NOMATCH;

         // Iterate through answer records
         for(i = 0; i < numanswers; i++)
         {
            // Reset the counter used to detect the overflow for each new answer record
            total = 0; 
            while(cursor_normal < end_of_payload && *cursor_normal != 0 && !((*cursor_normal & 0xc0) == 0xc0)) {
               cursor_normal += *cursor_normal + 1;
            }

            if(cursor_normal >= end_of_payload)
               return RULE_NOMATCH;

            cursor_normal += ((*cursor_normal & 0xc0) == 0xc0) ? 2 : 1 ;

			if ((cursor_normal + 1) >= end_of_payload)
			   return RULE_NOMATCH;

            // Check that the record type is TXT, that is, 0x10
            if (*(cursor_normal + 1) != 0x10)
               return RULE_NOMATCH;

            cursor_normal += 2+2+4;

            if (cursor_normal + 1 >= end_of_payload)
               return RULE_NOMATCH;

            // Extract the length field
            len = read_big_16_inc(cursor_normal);

            // Iterate through the byte[data] section and add each of the 
            // individual string lengths together
            while(cursor_normal < end_of_payload && total < len) {
               total += *cursor_normal + 1;
               cursor_normal += *cursor_normal + 1;
            }

            // Alert if the sum (total) of the inidividual string lengths
            // exceeds the length field for the entire data section
            if(cursor_normal >= end_of_payload)
               return RULE_NOMATCH;
            else if(total > len)
               return RULE_MATCH;
         }
      }
   }

   return RULE_NOMATCH;

}
Esempio n. 3
0
/* 
 *  ruleMatch
 * 
 *          p: packet data structure, same as the one found in snort.
 *    options: NULL terminated list of rule options
 *
 * Returns: 
 *    > 0 : match found
 *    = 0 : no match found
 *
 * Predefined constants: 
 *    (see sf_snort_plugin_api.h for more values)
 *    RULE_MATCH   -  if asn1 specifier is found within buffer 
 *    RULE_NOMATCH -  if asn1 specifier is not found within buffer 
 * 
 */
int ruleMatchInternal(SFSnortPacket *p, Rule* rule, u_int32_t optIndex, u_int8_t **cursor)
{
    u_int8_t *thisCursor = NULL, *startCursor = NULL;
    u_int8_t *tmpCursor = NULL;
    int retVal = RULE_NOMATCH;
    u_int32_t notFlag = 0;
    int thisType;
    ContentInfo *thisContentInfo = NULL;
    int startAdjust = 0;
    u_int32_t origFlags = 0;
    int32_t origOffset = 0;
    u_int32_t origDepth = 0;
    int continueLoop = 1;
    PCREInfo *thisPCREInfo = NULL;

    if (cursor)
        startCursor = thisCursor = *cursor;

    if (optIndex >= rule->numOptions || !rule->options[optIndex] )
        return RULE_NOMATCH;

    thisType = rule->options[optIndex]->optionType;

    /* Do some saving off of some info for recursion purposes */
    switch (thisType)
    {
        case OPTION_TYPE_CONTENT:
            thisContentInfo = rule->options[optIndex]->option_u.content;
            origFlags = thisContentInfo->flags;
            origDepth = thisContentInfo->depth;
            origOffset = thisContentInfo->offset;
            break;
        case OPTION_TYPE_PCRE:
            thisPCREInfo = rule->options[optIndex]->option_u.pcre;
            origFlags = thisPCREInfo->flags;
            break;
        default:
            /* Other checks should not need to check again like
             * PCRE & Content do.
             */
            break;
    }

    do
    {
        switch (thisType)
        {
        case OPTION_TYPE_CONTENT:
            retVal = contentMatch(p, rule->options[optIndex]->option_u.content, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.content->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_PCRE:
            retVal = pcreMatch(p, rule->options[optIndex]->option_u.pcre, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.pcre->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_FLOWBIT:
            retVal = processFlowbits(p, rule->options[optIndex]->option_u.flowBit);
            notFlag = rule->options[optIndex]->option_u.flowBit->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_BYTE_TEST:
            retVal = byteTest(p, rule->options[optIndex]->option_u.byte, thisCursor);
            notFlag = rule->options[optIndex]->option_u.byte->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_BYTE_JUMP:
            retVal = byteJump(p, rule->options[optIndex]->option_u.byte, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.byte->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_FLOWFLAGS:
            retVal = checkFlow(p, rule->options[optIndex]->option_u.flowFlags);
            notFlag = rule->options[optIndex]->option_u.flowFlags->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_ASN1:
            retVal = detectAsn1(p, rule->options[optIndex]->option_u.asn1, thisCursor);
            notFlag = rule->options[optIndex]->option_u.asn1->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_CURSOR:
            retVal = checkCursor(p, rule->options[optIndex]->option_u.cursor, thisCursor);
            notFlag = rule->options[optIndex]->option_u.cursor->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_SET_CURSOR:
            retVal = setCursor(p, rule->options[optIndex]->option_u.cursor, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.cursor->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_HDR_CHECK:
            retVal = checkHdrOpt(p, rule->options[optIndex]->option_u.hdrData);
            notFlag = rule->options[optIndex]->option_u.hdrData->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_BYTE_EXTRACT:
            retVal = extractValue(p, rule->options[optIndex]->option_u.byteExtract, thisCursor);
            notFlag = rule->options[optIndex]->option_u.byteExtract->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_LOOP:
            retVal = loopEval(p, rule->options[optIndex]->option_u.loop, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.loop->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_PREPROCESSOR:
            retVal = preprocOptionEval(p, rule->options[optIndex]->option_u.preprocOpt, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.preprocOpt->flags & NOT_FLAG;
            break;
        }

        if ( notFlag )
        {
            if ((retVal <= RULE_NOMATCH))
            {
                /* Set this as a positive match -- a ! was specified. */
                retVal = RULE_MATCH;
            }
            else  /* Match */
            {
                retVal = RULE_NOMATCH;
            }
        }

        if (retVal > RULE_NOMATCH)
        {
            /* This one matched.  Depending on type, check the next one
             * either in a loop, or not, saving cursor temporarily.
             */
            if (optIndex < rule->numOptions -1) /* hehe, optIndex is 0 based */
            {
                int nestedRetVal;
                /* Here's where it gets tricky... */
                if (thisType == OPTION_TYPE_CONTENT)
                {
                    /* If this is a content option, we've found a match.
                     * Save off the end-point of the current match.
                     * Less the length of current match plus 1.
                     *
                     * This gives us the starting point to check for this
                     * content again if subsequent options fail.  That starting
                     * point is the byte AFTER the beginning of the current
                     * match.
                     */
                    if ((origFlags & CONTENT_RELATIVE) && startCursor)
                    {
                        /* relative content.
                         * need to adjust offset/depth as well
                         */
                        tmpCursor = thisCursor -
                            thisContentInfo->patternByteFormLength + thisContentInfo->incrementLength;

                        /* Start Adjust is the difference between the old
                         * starting point and the 'next' starting point. */
                        startAdjust = tmpCursor - startCursor;
                    }
                    else
                    {
                        /* non-relative content */
                        tmpCursor = thisCursor -
                            thisContentInfo->patternByteFormLength + thisContentInfo->incrementLength;
                    }
                }
                else if (thisType == OPTION_TYPE_PCRE)
                {
                    /* Start next search at end of current pattern */
                    /* XXX: Could miss something here if part of pattern
                     * repeats but not easy to tell with PCRE.
                     */
                    tmpCursor = thisCursor;
                }

                nestedRetVal = ruleMatchInternal(p, rule, optIndex+1, &thisCursor);
                if (nestedRetVal == RULE_MATCH)
                {
                    /* Cool, everyone after us matched, we're done with a match */
                    if (cursor)
                        *cursor = thisCursor;
                    break;
                }

                /* If Content or PCRE, look farther into the packet
                 * for another match. */
                if (((thisType == OPTION_TYPE_CONTENT) ||
                     (thisType == OPTION_TYPE_PCRE))
                     && !notFlag)
                {
                    /* Only try to find this content again if it is a
                     * positive match.
                     */

                    /* And only if the next option is relative */
                    if (!isRelativeOption(rule->options[optIndex+1]))
                    {
                        /* Match failed, next option is not relative. 
                         * We're done. */
                        retVal = nestedRetVal;
                        break;
                    }

                    switch (thisType)
                    {
                    case OPTION_TYPE_CONTENT:
                        if (origFlags & CONTENT_RELATIVE)
                        {
                            if ((int32_t)(origDepth - startAdjust) < (int32_t)thisContentInfo->patternByteFormLength)
                            {
                                /* Adjusted depth would be less than the content we're searching for?
                                 * we're done. */
                                retVal = nestedRetVal;
                                continueLoop = 0;
                            }
                            else
                            {
                                /* For contents that were already relative, adjust the offset & depth fields
                                 * from their original values.  Makes it easy to determine when we'll be out
                                 * of the original bounds, relative to the original cursor. */
                                thisContentInfo->offset = origOffset + startAdjust;
                                thisContentInfo->depth = origDepth - startAdjust;

                                /* And use the original cursor that was passed in */
                                thisCursor = startCursor;
                            }
                        }
                        else
                        {
                            thisContentInfo->flags |= CONTENT_RELATIVE;
                            /* For contents that were not already relative, we simply use the adjusted
                             * cursor.  Set thisCursor to tmpCursor as calculated above */
                            thisCursor = tmpCursor;
                        }
                        break;
                    case OPTION_TYPE_PCRE:
                        /* Doesn't matter if it was already relative,
                         * just make it relative anyway.
                         */
                        thisPCREInfo->flags |= CONTENT_RELATIVE;
                        /* For PCREs that were not already relative, we use the cursor
                         * that was returned at the end of the pattern to start searching
                         * again. */
                        thisCursor = tmpCursor;
                        break;
                    }
                    continue;
                }


                /* Only need to search again when this is a
                 * content option.  If its not, we're done. */
                if (nestedRetVal <= RULE_NOMATCH)
                {
                    /* Handle the case when an error is propigated
                     * via nestedRetVal.
                     */
                    retVal = RULE_NOMATCH;
                }
                break;
            }
            else
            {
                /* Cool, nobody after us, we're done with a match */
                if (cursor)
                    *cursor = thisCursor;
                break;
            }
        }
        else
        {
            /* No match, get outta dodge */
            break;
        }
    } while (continueLoop);
    /* Keep looping until we break or serialized content checks returns no match. */

    /* Reset the flags for this content in case we added the
     * relative flag above.
     */
    if (thisContentInfo)
    {
        thisContentInfo->flags = origFlags;
        thisContentInfo->offset = origOffset;
        thisContentInfo->depth = origDepth;
    }
    if (thisPCREInfo)
    {
        thisPCREInfo->flags = origFlags;
    }

    return retVal;
}
/* detection functions */
int rule13887eval(void *p) {
   const uint8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   uint16_t num_of_queries, num_of_answers, num_of_authorities;

   uint16_t comp_offset;
   uint16_t data_len;
   uint8_t seglen;
   uint8_t new_root_name[ROOT_NAME_BUF_LEN], prev_root_name[ROOT_NAME_BUF_LEN];
   const uint8_t *rr_ptr;
   uint8_t root_name_len = 0;

   // flags
   uint8_t first_NS_record;

   // cruft
   uint16_t i,j;
   DEBUG_WRAP(uint16_t k);

   DEBUG_WRAP(printf("rule13887eval dns auth nameserver enter\n"));

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
    
   // flow:established, to_client;
   if(checkFlow(p, rule13887options[0]->option_u.flowFlags) <= 0 )
      return RULE_NOMATCH;

   DEBUG_WRAP(printf("passed flow\n"));

   // content:"|00 01|", offset 4, depth 2;
   if(contentMatch(p, rule13887options[1]->option_u.content, &cursor_normal) <= 0)
      return RULE_NOMATCH;

   DEBUG_WRAP(printf("passed content\n"));

   // byte_test:size 2, value 1, operator >, offset 8;
   if(byteTest(p, rule13887options[2]->option_u.byte, cursor_normal) <= 0)
      return RULE_NOMATCH;

   DEBUG_WRAP(printf("passed byte test\n"));

// This check doesn't have to actually be here; the structure is only for the fast pattern matcher
//   // content:"|00 02 00 01|", depth 0, relative, fast_pattern;
//   if (contentMatch(p, rule13887options[3]->option_u.content, &cursor_normal) <= 0) 
//       return RULE_MATCH;
//
//   DEBUG_WRAP(printf("passed content\n"));   

   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
      return RULE_NOMATCH;

   if(end_of_payload - beg_of_payload < 25)
      return RULE_NOMATCH;

   cursor_normal = beg_of_payload + 4;

   num_of_queries = *cursor_normal++ << 8;
   num_of_queries |= *cursor_normal++;

   num_of_answers = *cursor_normal++ << 8;
   num_of_answers |= *cursor_normal++;

   // Our byte_test assures us num_of_authorities is >= 2 so we have
   // something to compare later
   num_of_authorities = *cursor_normal++ << 8;
   num_of_authorities |= *cursor_normal++;

   cursor_normal += 2; // skip over count of additional RRs

   DEBUG_WRAP(printf("num_of_queries=%d num_of_answers=%d num_of_authorities=%d\n", num_of_queries, num_of_answers, num_of_authorities));
 
   // Jump over the query records
   for(i = 0; i < num_of_queries; i++) {
      DEBUG_WRAP(printf("processing query %d, payload_offset=%x\n", i, cursor_normal - beg_of_payload));

      while(cursor_normal < end_of_payload && *cursor_normal != 0 && *cursor_normal < 0xc0) // !((*cursor_normal & 0xc0) == 0xc0)
         cursor_normal += *cursor_normal + 1; 

      if(cursor_normal >= end_of_payload)
         return RULE_NOMATCH;

      // (two bytes for pointer (2 bytes)  or null byte (1 byte)) + type (2 bytes) + class (2 bytes)
      cursor_normal += (*cursor_normal >= 0xc0) ? 6 : 5;  // (*cursor_normal & 0xc0) == 0xc0
   }

   //if(cursor_normal >= end_of_payload)
   //   return RULE_NOMATCH;

   // Jump over the answers
   for(i = 0; i < num_of_answers; i++) {
      DEBUG_WRAP(printf("processing answer %d, payload_offset=%x\n", i, cursor_normal - beg_of_payload));

      while(cursor_normal < end_of_payload && *cursor_normal != 0 && *cursor_normal < 0xc0) { // performance improvement, !((*cursor_normal & 0xc0) == 0xc0)  
         cursor_normal += *cursor_normal + 1; 
      }

      if(cursor_normal + 4 > end_of_payload)
         return RULE_NOMATCH;

      // (two bytes for pointer (2 bytes) or a single null byte (1 byte)) + type (2 bytes) + class (2 bytes) + ttl (4 bytes)
      cursor_normal += (*cursor_normal >= 0xc0) ? 2 : 1;
 
      if(*cursor_normal++ == 0x00 && *cursor_normal++ == 0x05) {
         DEBUG_WRAP(printf("We found a CNAME record; let's just bail rather than figure this out for real.\n"));
         return RULE_NOMATCH;
      }
      cursor_normal += 6;

      if(cursor_normal + 2 > end_of_payload)
         return RULE_NOMATCH;

      data_len = (*cursor_normal++) << 8;
      data_len |= *cursor_normal++;

      cursor_normal += data_len;
   } 
      
   // Now on to the meat... The Authoritative nameservers.  If we
   // change roots, alert.
   first_NS_record = 1;
   for(i = 0; i < num_of_authorities; i++) {
      DEBUG_WRAP(printf("processing authority %d, payload_offset=%x\n", i, cursor_normal - beg_of_payload));
      if(cursor_normal + 12 >= end_of_payload)
         return RULE_NOMATCH;

      j=0; // index into expanded root name
      while(cursor_normal < end_of_payload && *cursor_normal != 0 && *cursor_normal < 0xc0) { // performance improvement, !((*cursor_normal & 0xc0) == 0xc0)
         // copy the size and data for each segment
         seglen = *cursor_normal++;
         new_root_name[j++] = seglen;
         DEBUG_WRAP(printf("seglen = %d\n", seglen));
         if((j + seglen < ROOT_NAME_BUF_LEN) && (cursor_normal + seglen < end_of_payload)) {
            DEBUG_WRAP(printf("copying bytes\n"));
            memcpy(&(new_root_name[j]), cursor_normal, seglen);
            cursor_normal += seglen;
            j += seglen;
         } else {
            return RULE_NOMATCH;
         }
      }
   
      // Either we're at a pointer, a null, or out of data
      if((cursor_normal + 1 < end_of_payload) && *cursor_normal >= 0xc0) { // pointer // performance improvement, (*cursor_normal & 0xc0) == 0xc0
         // Set the pointer
         comp_offset = (*cursor_normal++ & 0x3F) << 8;
         comp_offset |= *cursor_normal++;
         rr_ptr = beg_of_payload + comp_offset;
   
         DEBUG_WRAP(printf("offset = %d\n", comp_offset));
   
         while(rr_ptr < end_of_payload && *rr_ptr != 0 && *rr_ptr < 0xc0) { // performance improvement, !((*rr_ptr & 0xc0) == 0xc0)
            // copy the size and data for each segment
            seglen = *rr_ptr++;
            new_root_name[j++] = seglen;
            DEBUG_WRAP(printf("seglen = %d\n", seglen));
            if((j + seglen < ROOT_NAME_BUF_LEN) && (rr_ptr + seglen < end_of_payload)) {
               DEBUG_WRAP(printf("copying bytes\n"));
               memcpy(&(new_root_name[j]), rr_ptr, seglen);
               rr_ptr += seglen;
               j += seglen;
            } else {
               return RULE_NOMATCH;
            }
         }
   
         DEBUG_WRAP(printf("root: "));
         DEBUG_WRAP(for(k=0; k < j; k++) printf("%c", new_root_name[k]));
         DEBUG_WRAP(printf("\n"));
   
         // we only handle one level of compression
         if(rr_ptr >= end_of_payload || *rr_ptr >= 0xc0) { // performance improvement, ((*rr_ptr & 0xc0) == 0xc0)
            DEBUG_WRAP(printf("Multiple compression - Stopping\n"));
            return RULE_NOMATCH;
         }
      } else if((cursor_normal < end_of_payload) && (*cursor_normal == 0)) { // null (end of name string)