/* detection functions */
int rule13308eval(void *p) {
    const u_int8_t *cursor_normal = 0;
    const u_int8_t *beg_of_buffer, *end_of_buffer;

    SFSnortPacket *sp = (SFSnortPacket *) p;
    
    // Base64 stuff
    u_int8_t base64buf[256], decodedbuf[256];
    u_int32_t inputchars, base64bytes, decodedbytes;

    int i;

    if(sp == NULL)
        return RULE_NOMATCH;

    // flow:established, to_server;
    if (checkFlow(p, rule13308options[0]->option_u.flowFlags) <= 0)
        return RULE_NOMATCH;

    // Doing this content match is pretty useless because it's duplicated in our PCRE.
    // But we want to keep the structure for the pattern matcher.
//    // content:"Authorization|3A|", depth 0, nocase, fast_pattern;
//    if (contentMatch(p, rule13308options[1]->option_u.content, &cursor_normal) <= 0)
//        return RULE_NOMATCH;

    // pcre:"^Authorization\x3A\s*Basic[ \t]+", dotall, multiline, nocase;
    if (pcreMatch(p, rule13308options[2]->option_u.pcre, &cursor_normal) <= 0)
        return RULE_NOMATCH;

    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_buffer, &end_of_buffer) != CURSOR_IN_BOUNDS)
        return RULE_NOMATCH;

    // At this point, cursor should point to the start of the auth data
    inputchars = (end_of_buffer > cursor_normal + sizeof(base64buf)) ? sizeof(base64buf) : end_of_buffer - cursor_normal;

    DEBUG_SO(printf("%d input chars: %*s\n", inputchars, inputchars, cursor_normal));

    if(unfold_header(cursor_normal, inputchars, base64buf, sizeof(base64buf), &base64bytes) != 0)
        return RULE_NOMATCH;

    DEBUG_SO(printf("Successfully unfolded header (%s)(%d)\n", base64buf, base64bytes));

    if(base64decode(base64buf, base64bytes, decodedbuf, sizeof(decodedbuf), &decodedbytes) < 0)
        return RULE_NOMATCH;

    DEBUG_SO(printf("Successfully base64 decoded (%s)(%d)\n", decodedbuf, decodedbytes));
  
    for(i=0; i<decodedbytes; i++) {
        DEBUG_SO(printf("checking byte: %c\n", decodedbuf[i]));
        if(decodedbuf[i] == '%') {
            return RULE_MATCH;
        } else if(decodedbuf[i] == ':') {
            // Separator between username:password
            return RULE_NOMATCH;
        }
    }        
        
    return RULE_NOMATCH;
}
int ruleNOVELL_EVENTSREQUESTeval(void *p) {
   int retval;

   const uint8_t *cursor_normal; /*, *end_of_payload; */

   SFSnortPacket *sp = (SFSnortPacket *) p;

   BER_ELEMENT element;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;

   /* call flow match */
   if(checkFlow(sp, ruleNOVELL_EVENTSREQUESToptions[0]->option_u.flowFlags) <= 0 )
      return RULE_NOMATCH;

   /* call content match "|30|" */
   if(contentMatch(sp, ruleNOVELL_EVENTSREQUESToptions[1]->option_u.content, &cursor_normal) <= 0) {
      return RULE_NOMATCH;
   }

   /* The fast pattern matcher already assures us that the content match option containing the OID
      exists.  We're going to skip checking that content and go right for the PCRE to save time */

   if(pcreMatch(sp, ruleNOVELL_EVENTSREQUESToptions[3]->option_u.pcre, &cursor_normal) <= 0) {
      return RULE_NOMATCH;
   }

   /* So at this point, we know the first byte is "|30|" (the universal sequence), we know
      we have an OID, and the cursor is pointing to the byte following the OID.  We are going to
      make the reasonable assumption that if the anchors following this point all match up, the
      portion between the universal sequence and the OID contains the LDAP version and the
      extendedRequest sequence start (0x77 followed by a length field).
   */

   /* requestValue sequence start */
   retval = ber_get_element(p, cursor_normal, &element);

   if(retval < 0 || element.type != 0x81)
      return(RULE_NOMATCH);

   cursor_normal = element.data.data_ptr;

   /* sequence start */
   retval = ber_get_element(p, cursor_normal, &element);

   if(retval < 0 || element.type != 0x30)
      return RULE_NOMATCH;

   cursor_normal = element.data.data_ptr;

   /* eventCount */
   retval = ber_get_element(p, cursor_normal, &element);

   /* Because we're going to extract the int, we need to make sure
      the full data is present
   */
   if((retval < 0) || (element.type != 0x02) || (retval != element.data_len))
      return RULE_NOMATCH;

   /* get value of eventCount */
   retval = ber_extract_int_val(&element);

   if(retval < 0) 
      return RULE_NOMATCH;

   /* Malicious request if eventCount > 0x10000000 */
   if(element.data.int_val > 0x10000000)
      return RULE_MATCH;

   return RULE_NOMATCH; 
}
示例#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;
}
int ruleEXCHANGE_BASE64_DECODEeval(void *p) {
   /* data pointers */
   const u_int8_t *cursor_normal, *beg_of_payload, *end_of_payload, *line_ptr, *next_line_ptr;

   /* state variable */
   int in_base64_content, num_short_lines, cr;

   int line_text_len;

   SFSnortPacket *sp = (SFSnortPacket *) p;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;

   /* call flow match */
   if (checkFlow(sp, ruleEXCHANGE_BASE64_DECODEoptions[0]->option_u.flowFlags) <= 0 )
      return RULE_NOMATCH;

// This is our fast pattern, so we know it's here.  Plus, it's repeated in our PCRE.
// In theory, we could leave the content match then make the pcre relative and it
// could possible eek out a little more speed, but this should be fine.
//   /* call content match */
//   if (contentMatch(sp, ruleEXCHANGE_BASE64_DECODEoptions[1]->option_u.content, &cursor_normal) <= 0) {
//      return RULE_NOMATCH;
//   }

   /* call pcre match */
   if (pcreMatch(sp, ruleEXCHANGE_BASE64_DECODEoptions[2]->option_u.pcre, &cursor_normal) <= 0) {
      return RULE_NOMATCH;
   }
 
   /* At this point, cursor_normal is pointing to just past the base64
    * content-transfer-encoding tag.  So for now, we just need to read the
    * data looking for the malicious lines and stop when we get to the
    * end of the packet or /^--/, which denotes the end of the boundary.
    * 
    * If we don't flag, then we need to step through the rest of the
    * message looking for another base64-encoded C-T-E and look for
    * malicious data before /^--/ or EOF.  Rinse and repeat.
    */

   /* Since we're here, we are in a base64 content boundary.  Set things
    * up so we can use the same code for the rest of the message.
    */

   in_base64_content = 1;
   num_short_lines = 0;

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

   while(cursor_normal < end_of_payload) {
      if(in_base64_content) {
         /* Find the end of the line and set status variables */
         line_ptr = cursor_normal;
         line_text_len = 0;
         cr = 0;

         while(line_ptr < end_of_payload) {
            if(*line_ptr == '\r') {
               if(cr == 1) {       // We got two CRs in a row
                  line_text_len++; // Add the first to the count and keep going
               }
               cr = 1;
            }  else if(*line_ptr == '\n') {
               break;  // Found end of line, continue processing
            }  else {
               if(cr == 1) {  // This means we have a bare CR, just
                              // add it to the count and keep going
                  line_text_len++;
                  cr = 0;
               }
               line_text_len++;
            }
            line_ptr++;
         }

         // Move pointer after end of this line.  The loop will take care
         // of us stepping off of the payload.
         next_line_ptr = line_ptr + 1;

         // Check if base64 content is ending
         if((line_text_len >= 2) && (cursor_normal[0] == '-' &&
                                     cursor_normal[1] == '-')) {
            in_base64_content = 0;
            num_short_lines = 0;
         }  else {
            if(line_text_len == 1 || line_text_len == 2) {
               num_short_lines++;
               if(num_short_lines == 4) 
                  return RULE_MATCH;
            }  else {
               num_short_lines = 0;
            }
         }

         cursor_normal = next_line_ptr;

      }  else { /* !in_base64_content */
         // Find the next base64 content the easy way
         if(pcreMatch(sp, ruleEXCHANGE_BASE64_DECODEoptions[3]->option_u.pcre, &cursor_normal) <= 0)
            return RULE_NOMATCH;

         // Another base64 section was found, set up for another loop
         in_base64_content = 1;
         num_short_lines = 0;
      }
   } 

   return RULE_NOMATCH;
}