/* 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; }
/* 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); } }
/* 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)