/* detection functions */
int rule17741eval(void *p)
{
    const uint8_t *cursor_normal = 0, *end_of_payload;    
    SFSnortPacket *sp = (SFSnortPacket *) p;

    BER_ELEMENT ber_element;
    BER_ELEMENT req_body;
    BER_ELEMENT req_body_SEQUENCE;       
    int         ret;

    DEBUG_SO(printf("rule17741eval enter\n"));

    if(sp == NULL)
       return RULE_NOMATCH;

    // flow:to_server;
    if(checkFlow(p, rule17741options[0]->option_u.flowFlags) <= 0 )
        return RULE_NOMATCH;
        
    if (getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
        return RULE_NOMATCH;
  
    ret = ber_get_element(sp, cursor_normal, &ber_element);

    DEBUG_SO(printf("First element type 0x%02x\n", ber_element.type));

    if(ret < 0 || ((ber_element.type != 0x6a) && (ber_element.type != 0x6c)))
       return RULE_NOMATCH;

    // We want to delve into this element
    cursor_normal = ber_element.data.data_ptr;
 
    BER_DATA(0x30);         // SEQUENCE
    BER_SKIP(0xA1);         // pvno [1] INTEGER (5)
    BER_SKIP(0xA2);         // msg-type [2] INTEGER (10 -- AS -- | 12 -- TGS --)
    
    // if optional PA-DATA exists, skip it
    // 10 1 00011  (context-specific, structured, tag number 3)
    if (cursor_normal < end_of_payload && *cursor_normal == 0xA3)
        BER_SKIP(0xA3);     // padata [3] SEQUENCE OF PA-DATA OPTIONAL
   
    // req-body [4] KDC-REQ-BODY
    // req-body is defined as SEQUENCE and if req-body's data size is not equal to 
    // the total size of SEQUENCE, it is malicious
    
    ret = ber_get_element(sp, cursor_normal, &req_body);
    if (ret < 0)
        return RULE_NOMATCH;
    
    BER_DATA(0xA4);         

    ret = ber_get_element(sp, cursor_normal, &req_body_SEQUENCE);
    if (ret < 0)
        return RULE_NOMATCH;
 
    DEBUG_SO(printf("req_body.data_len = 0x%08x, req_body_SEQUENCE.specified_total_len = 0x%08x\n", req_body.data_len, req_body_SEQUENCE.specified_total_len);)
        
    if (req_body.data_len != req_body_SEQUENCE.specified_total_len)
// #if 0 // Don't compile the detection functions if they're not used
int rule18101eval(void *p) {
   const uint8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   const uint8_t *end_of_payload;

   BER_ELEMENT ber_element;

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

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

   BER_DATA(0x30);
   BER_SKIP(0x02);

   if(ber_get_element(sp, cursor_normal, &ber_element) < 0)
      return RULE_NOMATCH;

   // Exploit condition is SearchRequest (0x63) or ModifyDNRequest (0x6c) with
   // large size.  We're going with 1000 as a max size, apparently.
   if(ber_element.data_len > 1000 && (ber_element.type == 0x63 || ber_element.type == 0x6c))
      return RULE_MATCH;

   return RULE_NOMATCH;
}
示例#3
0
/* ber_point_to_data

   If the element is the specified type, sets the pointer in
   the parameter list to the first byte of data.  Note no
   checks are made to ensure the pointer is still within the
   packet.  DO NOT USE THE UPDATED CURSOR W/OUT VERIFICATION.

   Return values:
      0 -- Success
     <0 -- Error
*/
int ber_point_to_data(SFSnortPacket *sp, const u_int8_t **cursor, u_int8_t type) {
   int retval;
   BER_ELEMENT ber_element;

   retval = ber_get_element(sp, *cursor, &ber_element);

   if(retval < 0 || ber_element.type != type) {
      return(-1);
   } else {
      (*cursor) = ber_element.data.data_ptr;
      return(0);
   }
}
示例#4
0
/* ber_skip_element

   If the element is the specified type, sets the pointer in the
   parameter list to the byte after the element.  Note no checks
   are made to ensure the pointer is still within the packet.

   Return values:
      0 -- Success
     <0 -- Error
*/
int ber_skip_element(SFSnortPacket *sp, const u_int8_t **cursor, u_int8_t type) {
   int retval;   
   BER_ELEMENT ber_element;

   retval = ber_get_element(sp, *cursor, &ber_element);

   if((retval < 0) || (ber_element.type != type) || (retval != ber_element.data_len)) {
      return(-1);
   } else {
      (*cursor) += ber_element.total_len;
      return(0);
   }
}
/* detection functions */
int rule15149eval(void *p) {
    const uint8_t *cursor_normal = 0, *end_of_payload;
    SFSnortPacket *sp = (SFSnortPacket *) p;

   int retval;
   BER_ELEMENT element;

    if(sp == NULL)
        return RULE_NOMATCH;

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

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

   // Universal Sequence
   if(ber_point_to_data(sp, &cursor_normal, 0x30) < 0)
      return RULE_NOMATCH;

   // message ID
   if(ber_skip_element(sp, &cursor_normal, 0x02) < 0)
      return RULE_NOMATCH;

   // find the bind request
   if(ber_point_to_data(sp, &cursor_normal, 0x60) < 0)
      return RULE_NOMATCH;

   /* bind requests are defined as:
      int version
      LDAPDN name
      auth authenticationChoice
      bug here is if the version element is specified as a cursor (0x30) vs. an int (0x02)
   */
   retval = ber_get_element(sp, cursor_normal, &element);

   if(retval == -1)
      return RULE_NOMATCH;

   if(element.type == 0x30)
      return RULE_MATCH;

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

    int retval;
    BER_ELEMENT element;

    if(sp == NULL)
        return RULE_NOMATCH;

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

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

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

    // Universal Sequence
    if(ber_point_to_data(sp, &cursor_normal, 0x30) < 0)
        return RULE_NOMATCH;

    // message ID
    if(ber_skip_element(sp, &cursor_normal, 0x02) < 0)
        return RULE_NOMATCH;

    // search request
    if(ber_point_to_data(sp, &cursor_normal, 0x63) < 0)
        return RULE_NOMATCH;

    // Here's the meat
    retval = ber_get_element(sp, cursor_normal, &element);

    if(retval == -1)
        return RULE_NOMATCH;

    //DEBUG_WRAP(printf("found baseObject string.  checking values type=0x%02x size=%d\n", element.type, element.data_len));

    /* vuln is 0x43 + len(baseObject first part) + 3 * len(baseObject dc) > 0xFFA
       We simplify to data_len > 1200 since that's more than reasonable and shortest possible
       length to exploit is len(baseObject) == len(d) == 1363
    */
    if(element.type == 0x04 && ((element.data_len > 1200) || (retval == -2)))
        return RULE_MATCH;

    return RULE_NOMATCH;
}
/* detection functions */
int rule16394eval(void *p) {
   const uint8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   const uint8_t *cursor_padata;

   BER_ELEMENT ber_element;
   int retval;

   uint32_t renew_realm_len;
   const uint8_t *renew_realm_str;

   uint32_t ticket_realm_len;
   const uint8_t *ticket_realm_str;

   DEBUG_SO(int i);

   DEBUG_SO(printf("rule16394eval enter\n"));

   if(sp == NULL)
      return RULE_NOMATCH;

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

   // For speed and for ease of programming, we take advantage of some content matches
   // here in some places at the expense of false negatives.
   // But they do also to give us a nice content match.

   // This content match skips over a few items then matches on Kerberos protocol
   // version (pvno) 5 and mesg type TGS-REQ (12)
   // content:"|A1 03 02 01 05 A2 03 02 01 0C|", payload raw, depth 22, fast_pattern;
   if(contentMatch(p, rule16394options[1]->option_u.content, &cursor_normal) <= 0)
      return RULE_NOMATCH;

   // Save the pointer so we can come back here, and we're going to jump ahead to make
   // sure this is a renew packet as well as store the pointer to and size of the
   // renewal realm
   cursor_padata = cursor_normal;

   BER_SKIP(0xa3); // This is a wrapper to ber_skip_element() that NOMATCH's on failure

   // We should now be at the start of the KDC_REQ_BODY

   BER_DATA(0xa4); // This is a wrapper to ber_point_to_data() that NOMATCH's on failure
   BER_DATA(0x30); 

   // We're going to cheat again here and do a quick content match.
   // Plus, this is our fast pattern match.
   // content:"|A0 07 03 05 00 00 00 00 02|", payload raw, depth 9, relative, fast_pattern;
   if(contentMatch(p, rule16394options[2]->option_u.content, &cursor_normal) <= 0)
      return RULE_NOMATCH;

   BER_DATA(0xa2);
 
   // Grab the piece of data we've been looking for
   retval = ber_get_element(sp, cursor_normal, &ber_element);

   // Type 0x1b is essentially a string.  Don't know why it's not type 0x04. 
   // Negative return value means error.  0 means 0 data bytes and therefore useless.
   // Also make sure there is the full amount of data present.
   if((retval <= 0) || (ber_element.type != 0x1b) || (retval < ber_element.data_len))
      return RULE_NOMATCH;

   renew_realm_len = ber_element.data_len;
   renew_realm_str = ber_element.data.data_ptr;

   DEBUG_SO(for(i=0; i<renew_realm_len; i++) printf("%c", renew_realm_str[i]); printf("\n"));

   // Now that we have our renew_realm info and we've verified the data is complete,
   // Let's go back to the beginning and get our ticket_realm information
   cursor_normal = cursor_padata;

   // This is a very long list.  Glad we made sure we were in a renewal packet and
   // had all of the data we need before we bother with this.  :)
   BER_DATA(0xa3);
   BER_DATA(0x30);
   BER_DATA(0x30); 
   BER_SKIP(0xa1);
   BER_DATA(0xa2);
   BER_DATA(0x04);
   BER_DATA(0x6e);
   BER_DATA(0x30);
   BER_SKIP(0xa0);
   BER_SKIP(0xa1);
   BER_SKIP(0xa2);
   BER_DATA(0xa3);
   BER_DATA(0x61);
   BER_DATA(0x30);
   BER_SKIP(0xa0);
   BER_DATA(0xa1);

   // Same code and checks as above for our ticket/home realm
   retval = ber_get_element(sp, cursor_normal, &ber_element);
   if((retval <= 0) || (ber_element.type != 0x1b) || (retval < ber_element.data_len))
      return RULE_NOMATCH;

   ticket_realm_len = ber_element.data_len;
   ticket_realm_str = ber_element.data.data_ptr;

   DEBUG_SO(for(i=0; i<ticket_realm_len; i++) printf("%c", ticket_realm_str[i]); printf("\n"));

   // Match if the realm names are different (either lens are diff or value is diff)
   if(ticket_realm_len != renew_realm_len || memcmp(ticket_realm_str, renew_realm_str, ticket_realm_len))
      return RULE_MATCH;

   return RULE_NOMATCH;
}
/* detection functions */
int rule16375eval(void *p) {
   const u_int8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   BER_ELEMENT ber_element;

   const u_int8_t *end_of_string;
   u_int32_t namelen = 0;

   if(sp == NULL)
      return RULE_NOMATCH;

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

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

   // Universal Sequence
   if(ber_point_to_data(sp, &cursor_normal, 0x30) < 0)
      return RULE_NOMATCH;

   // Message ID
   if(ber_skip_element(sp, &cursor_normal, 0x02) < 0)
      return RULE_NOMATCH;

   // Search Request
   if(ber_point_to_data(sp, &cursor_normal, 0x66) < 0)
      return RULE_NOMATCH;

   // Object
   if(ber_get_element(sp, cursor_normal, &ber_element) < 0)
      return RULE_NOMATCH;

   // Make sure it's a string
   if(ber_element.type != 0x04)
      return RULE_NOMATCH;

   // Move the cursor to the start of the string data
   cursor_normal = ber_element.data.data_ptr;

   end_of_string = cursor_normal + ber_element.data_len;

   // Check for end of buffer
   if(end_of_string > end_of_payload)
      end_of_string = end_of_payload;

   // Now we have cursor_normal pointing to the start of the
   // string and end_of_string set appropriately.  Now,
   // let's see how long the parameter names are.
   while(cursor_normal < end_of_string) {
      if(*cursor_normal != '=')
         namelen++;
      else {
         DEBUG_SO(printf("rule16375: namelen=%d\n", namelen));

         if(namelen > 100)
            return RULE_MATCH;

         // Length is fine.  Set back to zero.
         namelen = 0;

         // Now jump over the value
         while((cursor_normal < end_of_string) && (*cursor_normal != ','))
            cursor_normal++;
      }

      cursor_normal++;
   }

   // If we were in the middle of an overly long parameter name
   // when we ran out of data, match.
   if(namelen > 100)
      return RULE_MATCH;

   return RULE_NOMATCH;
}
int rule13773eval(void *p) {
    const u_int8_t *cursor_normal = 0;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    /*const u_int8_t *end_of_payload; */

    int retval;
    u_int32_t snmp_ver;
    u_int32_t size_len, size;
    BER_ELEMENT element;

    DEBUG_WRAP(printf("rule13773eval: (linux netfilter snmp nat) begin\n"));

    if(sp == NULL)
        return RULE_NOMATCH;

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

    // flow:to_server; XXX bleh flow with UDP
//   if(checkFlow(p, rule13773options[0]->option_u.flowFlags) <= 0 )
//      return RULE_NOMATCH;

    // content:"0", depth 1;
    if(contentMatch(p, rule13773options[1]->option_u.content, &cursor_normal) <= 0)
        return RULE_NOMATCH;

    // Custom detection -- step through the structure to find the SNMP Trap and any anomalies
    // The content match puts us just past the universal sequence; we'll start from there --

    DEBUG_WRAP(printf("rule13773eval: start custom detection\n"));

    // Jump over the size of the message
    retval = ber_get_size(p, cursor_normal, &size_len, &size);

    if(retval < 0)
        return(RULE_NOMATCH);

    cursor_normal += size_len;

    DEBUG_WRAP(printf("rule13773eval: checking SNMP version\n"));

    // SNMP version. Because we're going to check the version, we need to make sure
    // the full data is present
    retval = ber_get_element(p, cursor_normal, &element);

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

    // Get the value of the SNMP version
    retval = ber_extract_int_val(&element);
    if(retval < 0)
        return RULE_NOMATCH;

    snmp_ver = element.data.int_val;
    DEBUG_WRAP(printf("rule13773eval: snmp_ver=%d\n", snmp_ver));

    cursor_normal += element.total_len;

    DEBUG_WRAP(printf("rule13773eval: checking community string\n"));

    // Community string
    retval = ber_get_element(p, cursor_normal, &element);

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

    cursor_normal += element.total_len;

    DEBUG_WRAP(printf("rule13773eval: processing trap-pdu, snmp_ver=%d\n", snmp_ver));

    // Here's where the trap information is located
    if(snmp_ver <= 1) {  // PROTOS only does SNMPv1, but some other samples are using SNMPv2
        retval = ber_get_element(p, cursor_normal, &element);

        if(retval < 0) {
            DEBUG_WRAP(printf("rule13773eval: failed to read element\n"));
            return RULE_NOMATCH;
        }

        DEBUG_WRAP(printf("rule13773eval: element.type=%#x (want 0xA4)\n", element.type));
        cursor_normal = element.data.data_ptr;

        // Apparently, you can use V1 traps with SNMPv2.
        if(element.type == 0xA4) {
            if(process_v1_trap(sp, cursor_normal, element.data_len) > 0)
                return RULE_MATCH;
//      } else if(element.type == 0xA7) { // Schema for V2 trap in bug 42464 / RFC1905 if needed
//         if(process_v2_trap(sp, cursor_normal, element.data_len) > 0)
//            return RULE_MATCH;
        } else {
            return RULE_NOMATCH;
        }
    }

    return RULE_NOMATCH;
}
/* process_v1_trap
 * Determines if the trap is malicious or not.  *ANY* read error within the trap-PDU
 * is flagged as malicous, including wrong type, out of data in packet, out of data
 * in sequence, etc.
 *
 * returns <0 for error, 0 for not malicious, 1 for malicious
*/
int process_v1_trap(SFSnortPacket *sp, const u_int8_t *cursor_normal, u_int32_t trap_pdu_len) {
    int retval;
    BER_ELEMENT element;

    u_int32_t actual_len = 0;  // Tracks how much data is specified within the pdu

    DEBUG_WRAP(printf("rule13773eval: process_v1_trap: start\n"));

    // We should now be at the start of the trap-PDU
    // It is an implicit sequence, meaning there is no 0x30 at the start.
    // Instead, the first element is...

    DEBUG_WRAP(printf("rule13773eval: process_v1_trap: testing enterprise object\n"));

    // enterprise Object Identifier
    retval = ber_get_element(sp, cursor_normal, &element);

    if((retval < 0) || (element.type != 0x06) || (element.data_len == 0))
        return 1;

    actual_len += element.total_len;
    if(actual_len >= trap_pdu_len)
        return 1;

    cursor_normal += element.total_len;

    DEBUG_WRAP(printf("rule13773eval: process_v1_trap: testing agent-addr\n"));

    // agent-addr
    retval = ber_get_element(sp, cursor_normal, &element);

    if((retval < 0) || (element.type != 0x40) ||
            (element.data_len != 4)) { // 4-byte address
        return 1;
    }

    actual_len += element.total_len;
    if(actual_len >= trap_pdu_len)
        return 1;

    cursor_normal += element.total_len;

    // For whatever reason, snmp_trap_decode() in ip_nat_snmp_basic.c parses
    // through to the timestamp of the trap, rather than just stopping at the
    // address.  So we need to jump two ints and find the timestamp, which
    // apparently is either a basic int or an application data type 3 (SNMP_TIT)

    // We can't just use ber_skip_element because we need to make sure we don't
    // overstep trap_pdu_len (but honestly, looking at the code I don't think
    // linux cares.  But it's an error so alert, anyway.

    DEBUG_WRAP(printf("rule13773eval: addr fine, checking generic-trap\n"));

    // generic-trap
    retval = ber_get_element(sp, cursor_normal, &element);
    if((retval < 0) || (element.type != 0x02))
        return 1;

    actual_len += element.total_len;
    if(actual_len >= trap_pdu_len)
        return 1;

    cursor_normal += element.total_len;

    DEBUG_WRAP(printf("rule13773eval: checking specific-trap\n"));

    // specific-trap
    retval = ber_get_element(sp, cursor_normal, &element);
    if((retval < 0) || (element.type != 0x02))
        return 1;

    actual_len += element.total_len;
    if(actual_len >= trap_pdu_len)
        return 1;

    cursor_normal += element.total_len;

    DEBUG_WRAP(printf("rule13773eval: checking timestamp\n"));

    // Here's where it gets fun.  Either it's a timestamp that's just a
    // straight int (type 0x02) or it's application data that is a
    // timestamp (type 0x43)
    retval = ber_get_element(sp, cursor_normal, &element);
    if((retval < 0) || ((element.type != 0x02) && (element.type != 0x43)))
        return 1;

    actual_len += element.total_len;
    if(actual_len >= trap_pdu_len)
        return 1;

    DEBUG_WRAP(printf("rule13773eval: timestamp fine.  we're done\n"));

    // And now, after looking through the code, this statement seems true...
    //
    // After this point, we're going to assume the decoder doesn't care
    // The whole point is that the kernel goes into the structure and
    // modifies the address (and stores the timestamp in a structure).

    // Why would they do anything but blindly copy the rest of the data
    // from this point forward?

    return 0;
}
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; 
}
/* detection functions */
int rule27906eval(void *p) {
   const u_int8_t *cursor_normal = 0, *end_of_payload;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   BER_ELEMENT kerberos_string;
   int i;

   if(sp == NULL)
      return RULE_NOMATCH;

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

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

   BER_DATA(0x6c);          // KDC-REQ [12]
   BER_DATA(0x30);          // SEQUENCE [16]
   BER_SKIP(0xA1);          // pvno [1]
   BER_SKIP(0xA2);          // msg-type [2]

   // if optional PA-DATA exists, skip it
   // 10 1 00011 (context-specific, structured, tag number 3)
   if(cursor_normal+1 > end_of_payload)
      return RULE_NOMATCH;
   if(*cursor_normal == 0xA3)
      BER_SKIP(0xA3);

   // KDC-REQ-BODY [4] ::= SEQUENCE [16]
   //    kdc-options [0] 
   //    realm       [2]  server's realm
   //    sname       [3]  PrincipalName
   BER_DATA(0xA4);  // KDC-REQ-BODY
   BER_DATA(0x30);  // SEQUENCE
   BER_SKIP(0xA0);  // kdc-options
   BER_SKIP(0xA2);  // realm

   // PrincipalName [3] ::= SEQUENCE [16]
   //    name-type   [0] Int32
   //    name-string [1] SEQUENCE [16] of KerberosString
   BER_DATA(0xA3);
   BER_DATA(0x30);
   BER_SKIP(0xA0);
   BER_DATA(0xA1);
   BER_DATA(0x30);

   // check up to 20 strings for the vulnerable condition
   for(i=0; (ber_get_element(sp, cursor_normal, &kerberos_string) >= 0) && (i < 20); i++) {
      // verify we are looking at a string element
      if(kerberos_string.type != 0x1b)
         return RULE_NOMATCH;

      DEBUG_SO(fprintf(stderr,"kerberos_string:\n data_len = 0x%02x\n",kerberos_string.data_len);)

      // vulnerable condition is kerberos_string.data_len == 0
      if(kerberos_string.data_len == 0)
         return RULE_MATCH;

      // Move to the end of the current element.  Guaranteed to move us forward in the packet.
      cursor_normal += kerberos_string.total_len;
   }
int ruleOPENLDAP_BIND_DOSeval(void *p) {
   int retval;
   u_int32_t size_len, size;

   const u_int8_t *cursor_normal, *end_of_payload; /*, *end_of_payload;*/

   SFSnortPacket *sp = (SFSnortPacket *) p;

   BER_ELEMENT element;


   if(sp == NULL)
      return RULE_NOMATCH;

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

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

   if((end_of_payload - cursor_normal) < 285)   /* Minimum malicious BIND request length */
      return RULE_NOMATCH;

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

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

   /* our contentMatch already assures us the first byte is \x30, so we're pointing at the size */

   /* Begin packet structure processing */
   retval = ber_get_size(p, cursor_normal, &size_len, &size); // message length

   if(retval < 0)
      return(RULE_NOMATCH);

   cursor_normal += size_len;

   /* Message number (only care about width of the specifier) */
   retval = ber_get_element(p, cursor_normal, &element);

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

   cursor_normal += element.total_len;

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

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

   /* We're inside the BIND request.  Now we need to parse the internals */
   cursor_normal = element.data.data_ptr;

   /* bind version */
   retval = ber_get_element(p, cursor_normal, &element);
   if(retval < 0 || element.type != 0x02) 
      return RULE_NOMATCH;

   cursor_normal += element.total_len;

   /* DN */
   retval = ber_get_element(p, cursor_normal, &element);
   if(retval < 0 || element.type != 0x04)
      return RULE_NOMATCH;

   cursor_normal += element.total_len;

   /* SASL authtype request */
   retval = ber_get_element(p, cursor_normal, &element);

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

   /* We're inside the SASL BIND request.  Now we need to parse the internals */
   cursor_normal = element.data.data_ptr;

   /* SASL auth mechanism */
   retval = ber_get_element(p, cursor_normal, &element);
   if((retval < 0) || (element.type != 0x04) || (element.data_len != 8))
      return RULE_NOMATCH;

   /* call content match "CRAM-MD5" */
   /* This will modify element.data.data_ptr, but we don't care */
   if(contentMatch(sp, ruleOPENLDAP_BIND_DOSoptions[1]->option_u.content, &(element.data.data_ptr)) <= 0) {
      return RULE_NOMATCH;
   }

   cursor_normal += element.total_len;

   /* Credentials
      For our check, we need to have 255 bytes in the actual buffer, so we need to
      verify the number of bytes present, not just the data_len reported by the
      BER element.
   */
   retval = ber_get_element(p, cursor_normal, &element);

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

   /* Here's the actual exploit detection -- see if there's a space at 255 bytes */
   if(element.data.data_ptr[254] == ' ')
      return RULE_MATCH;

   return RULE_NOMATCH; 
}
/* detection functions */
int rule36153eval(void *p) {
   const uint8_t *cursor_normal = 0, *end_of_buffer,
                 *next_mod_item, *next_msg;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   BER_ELEMENT msg, req, mod, attribute;
   int i,j, bytes_available, stop;
   unsigned int subtag_len;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
   
   // flow:established, to_server;
   if(checkFlow(p, rule36153options[0]->option_u.flowFlags) <= 0)
      return RULE_NOMATCH;
   
   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_buffer) <= 0)
      return RULE_NOMATCH;

   // LDAPMessage ::= SEQUENCE [16]
   if(ber_get_element(sp, cursor_normal, &msg) < 0)
      return RULE_NOMATCH;

   if(msg.type != 0x30)
      return RULE_NOMATCH;

   // calculate position of next msg
   next_msg = cursor_normal + msg.total_len;

   // integer overflow check
   if(next_msg < cursor_normal)
      return RULE_NOMATCH;

   // move cursor to messageID
   cursor_normal = msg.data.data_ptr;

   // messageID [2]
   BER_SKIP(0x02);

   // Request ::= APPLICATION [6]
   if(ber_get_element(sp, cursor_normal, &req) < 0)
      return RULE_NOMATCH;

   // move cursor to LDAPDN
   cursor_normal = req.data.data_ptr; 

   if(req.type != 0x66)
   {
      // check second msg for modifyRequest
      cursor_normal = next_msg;
      BER_DATA(0x30); // LDAPMessage 
      BER_SKIP(0x02); // messageID
      BER_DATA(0x66); // Request
   }

   BER_SKIP(0x04);   // LDAPDN [4]
   BER_DATA(0x30);   // modification items ::= SEQUENCE [16]

   // check up to 5 modification items 
   for(i=0; i<5; i++, cursor_normal = next_mod_item)
   {
      // modification item ::= SEQUENCE [16]
      if(ber_get_element(sp, cursor_normal, &mod) < 0)
         return RULE_NOMATCH;

      if(mod.type != 0x30)
         return RULE_NOMATCH;

      DEBUG_SO(fprintf(stderr,"mod item [0x%04X]\n", mod.data_len);)

      // calculate position of next mod item
      next_mod_item = cursor_normal + mod.total_len;

      // integer overflow check
      if(next_mod_item < cursor_normal)
         return RULE_NOMATCH;

      // if modification item data_len < 256 skip
      if(mod.data_len < 256)
         continue;

      // move cursor to operation 
      cursor_normal = mod.data.data_ptr;

      BER_SKIP(0x0A); // operation [10]
      BER_DATA(0x30); // modification ::= SEQUENCE [16]

      // attribute [4]
      bytes_available = ber_get_element(sp, cursor_normal, &attribute);

      if(bytes_available <= 0)
         return RULE_NOMATCH;

      if(attribute.type != 0x04)
         return RULE_NOMATCH;

      DEBUG_SO(fprintf(stderr," attribute [0x%04X]\n", attribute.data_len);)

      // if the attribute len is < 256
      // it cannot possibly contain the vuln, skip
      if(attribute.data_len < 256)