int rule35943eval(void *p) { const uint8_t *cursor_normal = 0, *end_of_buffer; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if(checkFlow(p, rule35943options[0]->option_u.flowFlags) <= 0) return RULE_NOMATCH; // content:"|00 01|", offset 6, depth 2; if(contentMatch(p, rule35943options[2]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"|00 00 00|", offset 2, depth 3, relative; if(contentMatch(p, rule35943options[3]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_buffer) <= 0) return RULE_NOMATCH; // move cursor to flags // in the TCP case, flags are at offset 4 cursor_normal += 4; return DetectBindTkeyDos(cursor_normal, end_of_buffer); }
/* detection functions */ int rule15968eval(void *p) { const uint8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload; SFSnortPacket *sp = (SFSnortPacket *) p; uint32_t MsgLen, stringOffset; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if (checkFlow(p, rule15968options[0]->option_u.flowFlags) > 0 ) { // content:"heal", offset 14, depth 4, fast_pattern; if (contentMatch(p, rule15968options[1]->option_u.content, &cursor_normal) > 0) { // content:"sdfx", depth 4; if (contentMatch(p, rule15968options[2]->option_u.content, &cursor_normal) > 0) { // This protocol is goofy. stringOffset, in the payload, is little endian. // MsgLen, in the header, is big endian. // Vulnerable condition is if stringOffset is less than 0x24 or greater // than MsgLen. if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; if(end_of_payload - beg_of_payload < 38) return RULE_NOMATCH; cursor_normal = beg_of_payload + 34; stringOffset = *cursor_normal++; stringOffset |= *cursor_normal++ << 8; stringOffset |= *cursor_normal++ << 16; stringOffset |= *cursor_normal << 24; //printf("stringOffset = %d (0x%08x)\n", stringOffset, stringOffset); if(stringOffset < 0x24) return RULE_MATCH; cursor_normal = beg_of_payload + 4; MsgLen = *cursor_normal++ << 24; MsgLen |= *cursor_normal++ << 16; MsgLen |= *cursor_normal++ << 8; MsgLen |= *cursor_normal; //printf("MsgLen = %d (0x%08x)\n", MsgLen, MsgLen); if(stringOffset > MsgLen) return RULE_MATCH; // Note there is also a two-byte big endian value PayloadLen at offset 12 // and a four-byte little endian dataLen at offset 18. // We ignore them, because they do not seem relevant to detection. } } } return RULE_NOMATCH; }
/* detection functions */ int rule16180eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; const u_int8_t *beg_of_payload, *end_of_payload; const u_int8_t *end_of_CommonName; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule16180options[0]->option_u.flowFlags) > 0 ) { // content:"|16 03|", depth 0; if (contentMatch(p, rule16180options[1]->option_u.content, &cursor_normal) > 0) { // content:"U|04 03|", depth 0, relative, fast_pattern; if (contentMatch(p, rule16180options[2]->option_u.content, &cursor_normal) > 0) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // Skip single byte type value, we're not checking it cursor_normal++; if(cursor_normal + 1 > end_of_payload) return RULE_NOMATCH; // Extract one-byte length value and use it to set end_of_CommonName end_of_CommonName = cursor_normal + *cursor_normal + 1; // Skip length value cursor_normal++; // Ensure we don't go past the end of the payload if (end_of_payload < end_of_CommonName) { end_of_CommonName = end_of_payload; } // Zip through the field, stop at end or NULL while ((cursor_normal < end_of_CommonName) && (*cursor_normal++)) { // empty loop } // If we stop before the end, there was a NULL so alert if (cursor_normal < end_of_CommonName) { return RULE_MATCH; } } } } return RULE_NOMATCH; }
int rule37676eval(void *p) { const uint8_t *cursor_normal = 0, *end_of_buffer; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:to_server if(checkFlow(p, rule37676options[0]->option_u.flowFlags) <= 0) return RULE_NOMATCH; // content:"|84|", fast_pattern:only; // if(contentMatch(p, rule37676options[1]->option_u.content, &cursor_normal) <= 0) // return RULE_NOMATCH; // content:"|20|", offset 17, depth 1; if(contentMatch(p, rule37676options[2]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_buffer) <= 0) return RULE_NOMATCH; return DetectCiscoIkeBof(sp, cursor_normal, end_of_buffer); }
// #if 0 // Don't compile the detection functions if they're not used int rule18063eval(void *p) { const uint8_t *cursor_normal = 0, *OfficeArtSPContainer = 0, *OfficeArtSpgrContainer = 0, *OfficeArtFSP = 0, *OfficeArtFSPGR = 0; const uint8_t *beg_of_payload = 0, *end_of_payload = 0, *tmp_payload = 0; uint32_t OfficeArtSpgrContainer_size = 0, OfficeArtSPContainer_size = 0; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule18063options[0]->option_u.flowFlags) <= 0 ) { return RULE_NOMATCH; } // flowbits:isset "file.doc"; if (processFlowbits(p, rule18063options[1]->option_u.flowBit) <= 0) { return RULE_NOMATCH; } if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) { return RULE_NOMATCH; } // Start by finding inner container // content:"|0F 00 04 F0|", depth 0, fast_pattern; if (contentMatch(p, rule18063options[3]->option_u.content, &OfficeArtSPContainer) <= 0) { return RULE_NOMATCH; } DEBUG_SO(fprintf(stderr, "OfficeArtSPContainer found: %p\n", OfficeArtSPContainer);) // Find the outer container - This may or may not exist // content:"|0F 00 03 F0|", depth 0; if(contentMatch(p, rule18063options[2]->option_u.content, &OfficeArtSpgrContainer) <= 0) {
// #if 0 // Don't compile the detection functions if they're not used int rule23039eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:to_server; if(checkFlow(p, rule23039options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; // content:"|00 01 00 00 00 00|", offset 4, depth 8, fast_pattern; if(contentMatch( p, rule23039options[1]->option_u.content, &cursor_normal ) <= 0) return RULE_NOMATCH; // The last |00| in the match above drops the cursor at the beginning of the first query, // 12 bytes from the beginning of the payload. If the first 2 bits are set on this byte, // it means we're looking at a pointer. In that event, we'll go ahead and flag it, because // we've already verified that there is only one query. return detectDNSloop(sp, cursor_normal ); }
/* detection functions */ int rule23847eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; const u_int8_t *end_of_payload = 0; const u_int8_t *beg_of_payload = 0; const u_int8_t *beg_of_servers = 0; const u_int8_t *start_of_smb = 0; int16_t converter = 0; int16_t server_comment = 0; int16_t server_comment_position = 0; u_int32_t entries = 0; u_int32_t netbios_message_length = 0; int i; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule23847options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; // Verify we are looking at an SMB Trans (0x25) response. // Verify the SMB Request was STATUS_SUCCESS (0x00000000) // content:"|FF|SMB|25 00 00 00 00|", depth 0, fast_pattern; if (contentMatch(p, rule23847options[1]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // Verify LANMAN protocol is reporting additional data available. // content:"|EA 00|", offset 47, depth 2, relative; if(contentMatch(p, rule23847options[2]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // Get the beginning of payload and end of payload positions. if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; start_of_smb = beg_of_payload + 4; // Beginning of 9-byte content match netbios_message_length = READ_BIG_16(beg_of_payload + 2); // 2 byte size field DEBUG_SO(fprintf(stderr,"\nnetbios_message_length: %d\n", netbios_message_length);)
/* detection functions */ int rule15329eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; u_int8_t countOne = 0; u_int8_t countTwo = 0; const u_int8_t *beg_of_payload, *end_of_payload; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if (checkFlow(p, rule15329options[0]->option_u.flowFlags) > 0 ) { // content:"MODPROPS"; nocase; if (contentMatch(p, rule15329options[1]->option_u.content, &cursor_normal) > 0) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; while ((cursor_normal < end_of_payload) && (*cursor_normal != '\r') && (*cursor_normal != '\n')) { if (*cursor_normal == ',') { countOne++; } cursor_normal++; } if (contentMatch(p, rule15329options[2]->option_u.content, &cursor_normal) > 0) { while ((cursor_normal < end_of_payload) && (*cursor_normal != '\r') && (*cursor_normal != '\n')) { if (*cursor_normal == ',') { countTwo++; } cursor_normal++; } } // Yes, technically these are both off-by-one on the count...but why waste the CPU // necessary to increment them both when leaving them as-is yields the same result? if (countTwo > countOne) { return RULE_MATCH; } } } return RULE_NOMATCH; }
/* detection functions */ int rule16396eval(void *p) { const uint8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; const uint8_t *beg_of_payload, *end_of_payload; uint32_t length = 0; uint16_t WCT; uint16_t BCC; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if (checkFlow(p, rule16396options[0]->option_u.flowFlags) > 0 ) { // content:"|FF|SMBr", offset 4, depth 5, fast_pattern; if (contentMatch(p, rule16396options[1]->option_u.content, &cursor_normal) > 0) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // isdataat:offset 30, relative; if (cursor_normal + 30 <= end_of_payload) { //Extract length from Netbios session header //Previous content match assures us no buffer underread length = *(cursor_normal-8) << 16; length |= *(cursor_normal-7) << 8; length |= *(cursor_normal-6); //Extract WCT Word Count //Word Count should be usually be zero in negotiate requests //But we need to be able to handle the case where there is //some value in there. cursor_normal += 27; WCT = 2 * *cursor_normal++; cursor_normal += WCT; //Make sure there is enough room to extract BCC if (cursor_normal + 2 > end_of_payload) return RULE_NOMATCH; //Extract BCC Byte Count BCC = read_little_16_inc(cursor_normal); //Alert if your netbios session size is bigger than //Byte count + number of words * 2 + 32-byte SMB header //+ 3 bytes (1 for the WCT field, and 2 for the BCC field) if (length > (BCC + WCT + 35)) return RULE_MATCH; } } } return RULE_NOMATCH; }
/* detection functions */ int rule19187eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; u_int16_t flags, num_of_answers, data_len; const u_int8_t *beg_of_payload, *end_of_payload; const u_int8_t *start_hostent, *end_hostent; int i; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:to_client; if (checkFlow(p, rule19187options[0]->option_u.flowFlags) > 0 ) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // Ensure we have enough data // txid, flags, num records * 4, query response (unk), at least 256 bytes of hostent // So, at least 300 bytes, I guess. DEBUG_SO(printf("Payload size = %d\n", (int)(end_of_payload - beg_of_payload));) if(end_of_payload - beg_of_payload < 300) return RULE_NOMATCH; // Forcing to be only one response entry // content:"|00 01|", offset 4, depth 2, fast_pattern; if (contentMatch(p, rule19187options[1]->option_u.content, &cursor_normal) <= 0) { return RULE_NOMATCH; } // Now, let's make sure this is actually a standard query response. // Our content match above ensures we have requisite data. flags = *(cursor_normal - 4) << 8; flags |= *(cursor_normal - 3); if((flags & 0xFA0F) != 0x8000) { // 1 bit - response (1 = response) // 4 bits - opcode (0000 = standard query) // 1 bit - authoritative (don't care) // 1 bit - truncated (0 = not truncated) // 5 bits - recursion and other don't cares // 4 bits - reply code (0000 = no error) return RULE_NOMATCH; } // Get number of answers num_of_answers = *cursor_normal++ << 8; num_of_answers |= *cursor_normal; DEBUG_SO(printf("We have %d answers\n", num_of_answers);)
/* detection functions */ int rule15700eval(void *p) { const u_int8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // content:"|02|", depth 1; if(contentMatch(p, rule15700options[0]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"c|82|Sc", offset 236, depth 4, fast_pattern; if(contentMatch(p, rule15700options[1]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // "c|82|Sc" puts us at the beginning of the dhcp options // 0xFF is the end of DHCP options while((cursor_normal + 1 < end_of_payload) && (*cursor_normal != 0xFF)) { if(*cursor_normal == 0x01) { // subnet mask option // Alert if the subnet mask is more than four bytes long if(*(cursor_normal + 1) > 4) return RULE_MATCH; // Do not return RULE_NOMATCH in the negative case in case it's // possible to specify this option more than once and still hit // the vulnerable code } cursor_normal += *(cursor_normal + 1) + 2; // + option size + option type and size bytes } return RULE_NOMATCH; }
int ruleMYSQL_COM_TABLE_DUMPeval(void *p) { const uint8_t *cursor_normal = 0, *beg_of_payload = 0, *end_of_payload = 0; uint32_t packet_length; uint8_t db_name_length, table_name_length; SFSnortPacket *sp = (SFSnortPacket *) p; // flow:established, to_server; if(checkFlow(p, ruleMYSQL_COM_TABLE_DUMPoptions[0]->option_u.flowFlags) > 0 ) { /* Make sure the packet is long enough */ if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; if((end_of_payload - beg_of_payload) < 6) return RULE_NOMATCH; // content:"|13|", depth 1; offset 4; if(contentMatch(p, ruleMYSQL_COM_TABLE_DUMPoptions[1]->option_u.content, &cursor_normal) > 0) { // Grab the size of the "packet" -- this is the size (in bytes) of // data after the message number byte, including the command byte // Packet size is 3 bytes, little endian packet_length = beg_of_payload[0]; packet_length += beg_of_payload[1] << 8; packet_length += beg_of_payload[2] << 16; // Grab the length of the DB Name db_name_length = beg_of_payload[5]; // if db name length > packet length (+2 for length and command // bytes), flag if((db_name_length + 2) > packet_length) { return RULE_MATCH; } else { // else grab table name length // table_name_length is at offset of db_name + db_name_length // + size byte if((end_of_payload - beg_of_payload) < 5 + db_name_length + 2) return RULE_NOMATCH; table_name_length = beg_of_payload[5 + db_name_length + 1]; // if table name length + db name length + size bytes // + command byte > packet length, flag if((db_name_length + table_name_length + 3) > packet_length) return RULE_MATCH; } } } return RULE_NOMATCH; }
/* detection functions */ int rule13897eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; const u_int8_t *beg_of_payload, *end_of_payload; u_int32_t atom_size; u_int16_t region_size; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if(checkFlow(p, rule13897options[0]->option_u.flowFlags) > 0 ) { // flowbits:isset "file.quicktime"; if(processFlowbits(p, rule13897options[1]->option_u.flowBit) > 0) { // content:"crgn"; if(contentMatch(p, rule13897options[2]->option_u.content, &cursor_normal) > 0) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; if(cursor_normal + 2 >= end_of_payload) return RULE_NOMATCH; region_size = (*cursor_normal++) << 8; region_size |= *cursor_normal++; DEBUG_WRAP(printf("REGION SIZE: %d\n", region_size)); cursor_normal = cursor_normal - 10; if(cursor_normal < beg_of_payload) return RULE_NOMATCH; atom_size = (*cursor_normal++) << 24; atom_size |= (*cursor_normal++) << 16; atom_size |= (*cursor_normal++) << 8; atom_size |= *cursor_normal++; DEBUG_WRAP(printf("ATOM SIZE: %d\n", atom_size)); // Changed from region_size > (atom_size - 8) to avoid // integer underflow from (atom_size - 8) if(((u_int32_t)region_size + 8) > atom_size) return RULE_MATCH; } } } return RULE_NOMATCH; }
/* detection functions */ int rule13469eval(void *p) { const uint8_t *cursor_normal = 0; const uint8_t *beg_of_payload, *end_of_payload; uint32_t length; SFSnortPacket *sp = (SFSnortPacket *) p; // flow:established, to_client; if (checkFlow(p, rule13469options[0]->option_u.flowFlags) > 0 ) { // flowbits:isset "file.doc"; if (processFlowbits(p, rule13469options[1]->option_u.flowBit) > 0) { // content:"bjbj"; if (contentMatch(p, rule13469options[2]->option_u.content, &cursor_normal) > 0) { // content:"|00 00 00 00|", offset 62, depth 4, relative; if (!(contentMatch(p, rule13469options[3]->option_u.content, &cursor_normal) > 0)) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // At this point, cursor_normal is still right after bjbj because negative // content matches do not move the pointer // Start custom detection here if((cursor_normal + 572) > end_of_payload) return RULE_NOMATCH; cursor_normal += 568; length = *cursor_normal++; length |= *cursor_normal++ << 8; length |= *cursor_normal++ << 16; length |= *cursor_normal++ << 24; if((length - 4) % 0x1a) return RULE_MATCH; } } } } return RULE_NOMATCH; }
/* 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 rule15148eval(void *p) { const u_int8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload; SFSnortPacket *sp = (SFSnortPacket *) p; u_int16_t data_len; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if (checkFlow(p, rule15148options[0]->option_u.flowFlags) > 0 ) { // content:"RCH0", depth 4, nocase; if (contentMatch(p, rule15148options[1]->option_u.content, &cursor_normal) > 0) { // content:"RCHE", offset 4, depth 4, nocase, relative; if (contentMatch(p, rule15148options[2]->option_u.content, &cursor_normal) > 0) { cursor_normal = cursor_normal - 8; data_len = *cursor_normal++; data_len |= (*cursor_normal++) << 8; if(data_len >= 130) return RULE_MATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // Normally, we'd be nervous about this check with TCP, but given the // packet sizes involved, we'll take the false positives from segmentation // on such a small packet. if(cursor_normal + 6 + data_len != end_of_payload) return RULE_MATCH; } } } return RULE_NOMATCH; }
int rule23040eval(void *p) { const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if(checkFlow(p, rule23040options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; // content:"|00 01 00 00 00 00 00 00|", offset 6, depth 8, fast_pattern; if(contentMatch(p, rule23040options[1]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; return detectDNSloop( sp, cursor_normal ); }
/* detection functions */ int ruleDHCPCATeval(void *p) { const u_int8_t *end; const u_int8_t *ptr; unsigned short type; unsigned short size; unsigned short sizes[256]; SFSnortPacket *sp = (SFSnortPacket *) p; const u_int8_t *cursor_normal = 0, *beg_of_payload; if (NULL == sp) return RULE_NOMATCH; if (NULL == sp->payload) return RULE_NOMATCH; if (contentMatch(p, ruleDHCPCAToptions[0]->option_u.content, &cursor_normal)) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end) <= 0) return RULE_NOMATCH; /* offset for cookie + 2 options of size 500 */ if (740 > (end - beg_of_payload)) return RULE_NOMATCH; ptr = beg_of_payload + 240; memset(sizes, 0, sizeof(sizes)); while (ptr + 2 < end) { type = (((u_int8_t) *(ptr))&0xFF); size = (((u_int8_t) *(ptr+1))&0xFF); if ((sizes[type] += size) > 500) { return RULE_MATCH; } ptr += 2 + size; } } return RULE_NOMATCH; }
/* detection functions */ int ruleIGMPIPOPTDOSeval(void *p) { int i = 0; u_int8_t alert = 0; const u_int8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; u_int8_t *ip_options_data; if (checkHdrOpt(p, ruleIGMPIPOPTDOSoptions[0]->option_u.hdrData)) { if (contentMatch(p, ruleIGMPIPOPTDOSoptions[1]->option_u.content, &cursor_normal) > 0) { if (sp->ip4_options_data != NULL) { ip_options_data = (u_int8_t *) sp->ip4_options_data; if (sp->ip4_options_length >= 2) { if (*ip_options_data == 0 && *(ip_options_data+1) == 0) { return RULE_MATCH; } } } for(i=0; i< (int) sp->num_ip_options; i++) { if (sp->ip_options[i].option_code == 148) { return RULE_NOMATCH; } if (sp->ip_options[i].length == 1) { alert++; } } if (alert > 0) { return RULE_MATCH; } } } return RULE_NOMATCH; }
/* detection functions */ int rule15117eval(void *p) { const uint8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; const uint8_t *end_of_payload, *end_of_record; const uint8_t *cursor_nextloop; uint16_t obj_record_len; uint16_t ft; uint16_t cb; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if(checkFlow(p, rule15117options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; // flowbits:isset "file.xls"; if(processFlowbits(p, rule15117options[1]->option_u.flowBit) <= 0) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0) return RULE_NOMATCH; // content:"|5D 00|"; while(contentMatch(p, rule15117options[2]->option_u.content, &cursor_normal) > 0) { // If we need to search again, we want to start here in case we matched on data cursor_nextloop = cursor_normal; // The first sub-record is 22 bytes plus the 2-byte obj_record_len if(cursor_normal + 24 >= end_of_payload) return RULE_NOMATCH; // Verify the first sub-record is type 0x0015 to reduce false positives. // This also adds false negatives, but otherwise the FP rate will be // astronomical. if((*(cursor_normal + 2) != 0x15) || (*(cursor_normal + 3) != 0x00)) continue; // Verify the second sub-record is type 0x0012 to reduce false positives. if((*(cursor_normal + 4) != 0x12) || (*(cursor_normal + 5) != 0x00)) continue; // Verify the last 3 bytes in the sub-record are null if(*(cursor_normal + 12) != 0) continue; if(*((uint16_t*)(cursor_normal + 13)) !=0) // byte order doesn't matter here continue; obj_record_len = *cursor_normal++; obj_record_len |= *cursor_normal++ << 8; DEBUG_WRAP(printf("obj_record_len=0x%04x\n", obj_record_len)); if(obj_record_len == 0) // if 0, not a valid record return RULE_NOMATCH; obj_record_len -= 4; // minus 2 for tag and 2 for size to properly find end_of_record //the record with no variable length data will not exceed 160 ish bytes. Use this check to eliminate FPs if(obj_record_len > 1000) continue; // Stop at the end of the record or when we run out of data end_of_record = (cursor_normal + obj_record_len >= end_of_payload) ? end_of_payload : cursor_normal + obj_record_len; // Skip the first sub-record, it's always 22 bytes. Bounds check was prior to this cursor_normal += 22; while(cursor_normal + 2 < end_of_record) { ft = *cursor_normal++; ft |= *cursor_normal++ << 8; DEBUG_WRAP(printf("FT: 0x%02x\n", ft)); if(ft > 0x15) return RULE_MATCH; if(cursor_normal + 2 > end_of_record) break; // Get out of the loop, look for another OBJ record cb = *cursor_normal++; cb |= *cursor_normal++ << 8; DEBUG_WRAP(printf(" cb: 0x%02x\n", cb)); cursor_normal += cb; } DEBUG_WRAP(printf("Searching for new OBJ record (|5D 00|)\n")); // Start our next search immediately after the last "|5D 00|" cursor_normal = cursor_nextloop; } return RULE_NOMATCH; }
/* detection functions */ int rule17697eval(void *p) { const uint8_t *cursor_normal = 0; const uint8_t *beg_of_buffer, *end_of_buffer; uint8_t decodedbuf[MAX_BASE64_BUFFER_SIZE], *decodedbuf_ptr; uint32_t inputchars, decodedbytes; SFSnortPacket *sp = (SFSnortPacket *) p; uint32_t tmpval = 0; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; if (checkFlow(p, rule17697options[0]->option_u.flowFlags) > 0 ) { // content:"-----BEGIN PGP MESSAGE-----", depth 0, nocase, fast_pattern; if (contentMatch(p, rule17697options[1]->option_u.content, &cursor_normal) > 0) { DEBUG_SO(printf("Matched the PGP header\n")); // content:"Version|3A|", offset 2, depth 8, nocase, relative; if (contentMatch(p, rule17697options[2]->option_u.content, &cursor_normal) > 0) { DEBUG_SO(printf("Matched the version\n")); // content:"|0D 0A 0D 0A|", depth 0, relative; if (contentMatch(p, rule17697options[3]->option_u.content, &cursor_normal) > 0) { DEBUG_SO(printf("Matched the newline\n")); if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_buffer, &end_of_buffer) != CURSOR_IN_BOUNDS) return RULE_NOMATCH; // We should now be at the beginning of the PGP data // Four base64 input chars become three output chars inputchars = (end_of_buffer > cursor_normal + (sizeof(decodedbuf) * 4 / 3)) ? (sizeof(decodedbuf) * 4 / 3) : end_of_buffer - cursor_normal; DEBUG_SO(printf("Decoding %d bytes\n", inputchars)); // Only need 6 output bytes, plus 1 byte for the NULL added by base64decode() if(base64decode(cursor_normal, inputchars, decodedbuf, 7, &decodedbytes) < 0) { DEBUG_SO(printf("Failed to decode any data to work with\n")); return RULE_NOMATCH; } DEBUG_SO(printf("Decoded %d bytes\n", decodedbytes)); // Make sure we have enough data to work with if(decodedbytes >= 6) { decodedbuf_ptr = decodedbuf; // New format with content tag of 16 or 61 (both in the first byte) DEBUG_SO(printf("Packet format: %01x\n", decodedbuf[0])); // The top two bits are set, the lower six we want to be either 16 or 61. // 0xC0 + 16 = 0xD0, 0xC0 + 61 = 0xFD if((decodedbuf[0] == (uint8_t)0xD0) || (decodedbuf[0] == (uint8_t)0xFD)) { if(decodedbuf[1] == 0xFF) { decodedbuf_ptr = decodedbuf + 2; tmpval = *decodedbuf_ptr++; tmpval |= *decodedbuf_ptr++ << 8; tmpval |= *decodedbuf_ptr++ << 16; tmpval |= *decodedbuf_ptr++ << 24; DEBUG_SO(printf("Packet Size: 0x%08x\n", tmpval)); if((tmpval >= 0xF9FFFFFF) && (tmpval <= 0xFEFFFFFF)) { return RULE_MATCH; } } } } } } } } return RULE_NOMATCH; }
/* detection functions */ int rule33053eval(void *p) { const uint8_t *check, *end_of_buffer, *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; int i, j; uint8_t atype, alength; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // content:"|01|", depth 1; if(contentMatch(p, rule33053options[0]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"|1F 13|", depth 0, fast_pattern; if(contentMatch(p, rule33053options[1]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"|2D|", offset 2, depth 1, relative; if(contentMatch(p, rule33053options[2]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"|2D|", offset 2, depth 1, relative; if(contentMatch(p, rule33053options[3]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; if(getBuffer(p, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_buffer) <= 0) return RULE_NOMATCH; // following RFC 2865 Page 17 // skip code (1 byte), pkt identifier (1 byte), // length (2 bytes), authenticator (16 bytes) cursor_normal += 20; // parse up to 10 attributes (TLV) for(i = 0; i < 10; i++) { // make sure we can read type and length (1 byte each) if(cursor_normal + 2 > end_of_buffer) return RULE_NOMATCH; atype = *cursor_normal; alength = *(cursor_normal+1); DEBUG_SO(fprintf(stderr,"radius attribute type:0x%02X len:0x%02X\n",atype,alength);) // make sure we can read value check = cursor_normal + alength; // overflow check if(check <= cursor_normal) return RULE_NOMATCH; // overread check if(check > end_of_buffer) return RULE_NOMATCH; if(atype == 0x01) { // restrict how many bytes we will check // in the User-Name Attribute Value if(alength > 25) alength = 25; // User-Name Attribute, check for '(' or ')', if present, alert. // we start at index 2 because alength includes the Type and Length for(j = 2; j < alength; j++) { if(cursor_normal[j] == '(' || cursor_normal[j] == ')') return RULE_MATCH; } // only check one User-Name attribute return RULE_NOMATCH; } cursor_normal = check; }
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; }
/* detection functions */ int rule13666eval(void *p) { const uint8_t *cursor_normal = 0, *cursor_offBmi; SFSnortPacket *sp = (SFSnortPacket *) p; const uint8_t *record_size_ptr, *beg_of_payload, *end_of_payload; uint32_t recordType, offBmi, bcSize; uint16_t bcWidth,bcHeight,bcPlanes,bcBitCount; uint64_t Value; // For storing exploit calculations if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule13666options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; // flowbits:isset "file.emf"; if (processFlowbits(p, rule13666options[1]->option_u.flowBit) <= 0) return RULE_NOMATCH; // content:" EMF|00 00 01 00|"; if (contentMatch(p, rule13666options[2]->option_u.content, &cursor_normal) > 0) { // content:"|01 00 00 00|", offset -48, depth 4, relative; if (contentMatch(p, rule13666options[3]->option_u.content, &cursor_normal) > 0) { // byte_jump:size 4, relative, endian little; if (byteJump(p, rule13666options[4]->option_u.byte, &cursor_normal) > 0) { if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; // Now we're at the start of records. Find the interesting ones. while(cursor_normal < end_of_payload) { // We point to record size so we're at the right state for the relative jump // if it turns out the record doesn't have a BITMAPCOREHEADER. record_size_ptr = cursor_normal - 4; // content:"|5E 00 00 00|", offset -8, depth 4, relative; XXX /* EMR_CREATEDIBPATTERNBRUSHPT */ if (contentMatch(p, rule13666options[5]->option_u.content, &cursor_normal) > 0 // content:"|51 00 00 00|", offset -8, depth 4, relative; XXX /* EMR_STRETCHDIBITS */ || (contentMatch(p, rule13666options[6]->option_u.content, &cursor_normal) > 0)) { recordType = *(cursor_normal - 4); switch(recordType) { case EMR_CREATEDIBPATTERNBRUSHPT: DEBUG_SO(printf("matched EMR_CREATEDIBPATTERNBRUSHPT\n");) if (cursor_normal + 28 > end_of_payload) // Make sure there's enough room return RULE_NOMATCH; // extract offset to BITMAPCOREHEADER. can't use byte_jump because it could put us // past end of packet since the "jump" actually starts from an earlier offset cursor_offBmi = cursor_normal + 12; offBmi = read_little_32(cursor_offBmi); break; case EMR_STRETCHDIBITS: DEBUG_SO(printf("matched EMR_STRETCHDIBITS\n");) if (cursor_normal + 60 > end_of_payload) return RULE_NOMATCH; // read offBmiSrc field cursor_offBmi = cursor_normal + 44; offBmi = read_little_32(cursor_offBmi); break; default: DEBUG_SO(printf("This case must not happen\n");) return RULE_NOMATCH; } if (offBmi + cursor_normal - 4 < cursor_normal) // check integer overflow return RULE_NOMATCH; // move cursor to the beginning of a device independent bitmap (DIB) DEBUG_SO(printf("offBmi=0x%08x\n", offBmi);) cursor_normal += offBmi - 4; if(cursor_normal + 12 > end_of_payload) return RULE_NOMATCH; // match size of BITMAPCOREHEADER to determine if it's the proper structure bcSize = read_little_32(cursor_normal); cursor_normal += 4; DEBUG_SO(printf("bcSize=0x%08x\n", bcSize);) if (bcSize == 12) // checks BITMAPCOREHEADER
// #if 0 // Don't compile the detection functions if they're not used int rule24666eval(void *p) { const u_int8_t *cursor_normal = 0; const u_int8_t *beg_of_payload = 0; const u_int8_t *end_of_payload = 0; const u_int8_t *cursor_detect = 0; const u_int8_t *cursor_tmp = 0; u_int16_t cref = 0; // number of 8 byte refs u_int16_t rgbNameL = 0; // length of rgbName field u_int16_t cFieldData = 0; // number of Feat11FieldDataItems u_int16_t entryIDL = 0; // length of entryID field u_int16_t strFieldNameL = 0; // length of strFieldName u_int16_t strCaptionL = 0; // length of strCaption u_int32_t cbdxfHdrDiskL = 0; // length of cbdxfHdrDisk field u_int32_t cbFmtInsertRowL = 0; // length of cbFmtInsertRowL int i = 0; // iterator for for() loop SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule24666options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; // flowbits:isset "file.xls"; if (processFlowbits(p, rule24666options[1]->option_u.flowBit) <= 0) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; cursor_normal = beg_of_payload; // content:"x|08|", depth 0; // this is 0x7808, header for feature12 while(contentMatch(p, rule24666options[2]->option_u.content, &cursor_normal) > 0) { cursor_detect = cursor_normal; // jump to "x|08|" offset 2, depth 2 // there is a two byte length field and the 0x7808 header again cursor_detect += 2; // check if we can read two bytes if((cursor_detect + 2) > end_of_payload) return RULE_NOMATCH; // are those bytes 0x0878 (little endian) if((*cursor_detect != 0x78) || (*(cursor_detect+1) != 0x08)) continue; // jump to reserved fields cursor_detect += 14; // Two fields labeled reserved1 and reserved2 // total of 5 null bytes, plus 2 bytes for cref if((cursor_detect + 8) > end_of_payload) return RULE_NOMATCH; // make sure reserved fields are null // Normally we can't just typecase our cursor, but because we // are checking for NULL here, we can if(*((u_int32_t*)cursor_detect) != 0) continue; // jump to cref, after reserved fields cursor_detect += 5; // read the cref value, we checked for 7 bytes before reserved fields // so we don't need to check again here cref = READ_LITTLE_16(cursor_detect); // jump cref*8+8, this jumps over all the ref structures // must check for integer overflows cursor_tmp = cursor_detect + 16 + (cref * 8); if(cursor_tmp < cursor_detect) return RULE_NOMATCH; cursor_detect = cursor_tmp; // this jump puts us at the beginning of the rgbFeatData // make sure we can read crwHeader if((cursor_detect + 4) >= end_of_payload) return RULE_NOMATCH; // make sure crwHeader==0 if(*((u_int32_t*)cursor_detect) != 0) continue; // jump to rgbName, it is an XLUnicodeString, the first 2-bytes = length cursor_detect += 56; // make sure we can read rgbNameL if((cursor_detect + 2) > end_of_payload) return RULE_NOMATCH; // store rgbNameL rgbNameL = READ_LITTLE_16(cursor_detect); // jump over rgbName cursor_detect += rgbNameL + 3; // make sure we can read cFieldData and entryID if((cursor_detect + 4) > end_of_payload) return RULE_NOMATCH; // store cFieldData cFieldData = READ_LITTLE_16(cursor_detect); // jump over cFieldData cursor_detect += 2; // read entryIDL entryIDL = READ_LITTLE_16(cursor_detect); // jump over entryID cursor_detect += 3 + entryIDL; // max 10 iterations through Feat11FieldDataItem structures if(cFieldData > 10) cFieldData = 10; // loop through the Feat11FieldDataItem fields for(i = 0; i < cFieldData; i++) { // jump to cbFmtInsertRowL cursor_detect += 28; // make sure we can read length of cbFmtInsertRowL and strFieldNameL if((cursor_detect + 16) > end_of_payload) return RULE_NOMATCH; // read the cbFmtInsertRowL cbFmtInsertRowL = READ_LITTLE_32(cursor_detect); // jump to strFieldName cursor_detect += 8; // read stFieldNameL strFieldNameL = READ_LITTLE_32(cursor_detect); // jump over strFieldName, with overflow check cursor_tmp = cursor_detect + strFieldNameL + 3; if(cursor_tmp < cursor_detect) return RULE_NOMATCH; cursor_detect = cursor_tmp; // make sure we can read strCaptionL if((cursor_detect + 2) > end_of_payload) return RULE_NOMATCH; // read strCaptionL strCaptionL = READ_LITTLE_16(cursor_detect); // jump over strCaptionL and dxFmtInsertRow cursor_tmp = cursor_detect + strCaptionL + cbFmtInsertRowL + 3; if(cursor_tmp < cursor_detect) return RULE_NOMATCH; cursor_detect = cursor_tmp; // make sure we can read cbdxfHdrDiskL if((cursor_detect + 4) > end_of_payload) return RULE_NOMATCH; // read the cbdxfHdrDiskL cbdxfHdrDiskL = READ_LITTLE_32(cursor_detect); //DEBUG_SO(fprintf(stderr, "cbdxfHdrDiskL: %d\n", cbdxfHdrDiskL);) if(cbdxfHdrDiskL > 0x2020) return RULE_MATCH; // cbdxfHdrDiskL > 0x2020 is the vulnerable condition // if cbdxfHdrDiskL is <= 0x2020 than it's the length // used for the last field and we jump cursor_tmp = cursor_detect + cbdxfHdrDiskL + 4; if(cursor_tmp < cursor_detect) return RULE_NOMATCH; cursor_detect = cursor_tmp; } return RULE_NOMATCH; } 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 rule15734eval(void *p) { const u_int8_t *cursor_raw = 0, *end_of_payload; SFSnortPacket *sp = (SFSnortPacket *) p; const u_int8_t *junkptr; // for getBuffer() u_int16_t num_updates; u_int16_t num_addtl_rrs; u_int16_t data_len; u_int16_t record_type; // cruft int i; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_server; // if(checkFlow(p, rule15734options[0]->option_u.flowFlags) <= 0 ) // return RULE_NOMATCH; // content:"|28 00 00 01 00 01|", offset 2, depth 6, fast_pattern; if(contentMatch(p, rule15734options[1]->option_u.content, &cursor_raw) <= 0) return RULE_NOMATCH; DEBUG_WRAP(printf("passed content\n")); if(getBuffer(p, CONTENT_BUF_NORMALIZED, &junkptr, &end_of_payload) <= 0) return RULE_NOMATCH; if(cursor_raw + 25 >= end_of_payload) return RULE_NOMATCH; num_updates = *cursor_raw++ << 8; num_updates |= *cursor_raw++; DEBUG_WRAP(printf("num_updates=%d\n", num_updates)); if(num_updates == 0) return RULE_NOMATCH; num_addtl_rrs = *cursor_raw++ << 8; num_addtl_rrs |= *cursor_raw++; DEBUG_WRAP(printf("num_addtl_rrs=%d\n", num_addtl_rrs)); // Zone section (we force this to be one entry by content match) if(dns_skip_name(&cursor_raw, end_of_payload) <= 0) return RULE_NOMATCH; if(cursor_raw + 18 >= end_of_payload) return RULE_NOMATCH; DEBUG_WRAP(printf("SOA: 0x%02x%02x Class: 0x%02x%02x\n", cursor_raw[0], cursor_raw[1],cursor_raw[2],cursor_raw[3])); // Verify Type: SOA and Class: IN if(memcmp(cursor_raw, "\x00\x06\x00\x01", 4)) return RULE_NOMATCH; cursor_raw += 4; // Prerequisites section (we force this to be one entry by content match) if(dns_skip_name(&cursor_raw, end_of_payload) <= 0) return RULE_NOMATCH; if(cursor_raw + 14 >= end_of_payload) return RULE_NOMATCH; // FP reduction. Microsoft clients do ANY-ANY // Better solution is to make sure EXTERNAL_NET is set correctly // Verify Type: ANY Class: IN if(memcmp(cursor_raw, "\x00\xff\x00\x01", 4)) return RULE_NOMATCH; cursor_raw += 8; // Skip over class and type and Skip TTL data_len = *cursor_raw++ << 8; data_len |= *cursor_raw++; cursor_raw += data_len; // Updates (YAY!!) DEBUG_WRAP(printf("Checking updates\n")); for(i = 0; i < num_updates; i++) { DEBUG_WRAP(printf("Checking update %d...", i)); if(dns_skip_name(&cursor_raw, end_of_payload) <= 0) return RULE_NOMATCH; if(cursor_raw + 2 >= end_of_payload) return RULE_NOMATCH; record_type = *cursor_raw++ << 8; record_type |= *cursor_raw++; DEBUG_WRAP(printf("record_type 0x%04x\n", record_type)); // Alert if we see an update of type ANY (0x00FF) if(record_type == 0x00FF) return RULE_MATCH; if(cursor_raw + 8 >= end_of_payload) return RULE_NOMATCH; cursor_raw += 6; data_len = *cursor_raw++ << 8; data_len |= *cursor_raw++; cursor_raw += data_len; } // Currently, we don't care about the Additional RRs section, but if it turns // out we get false positives, we can add the requirement that there be no // TSIG (0x00fa) records aka unauthenticated update requests return RULE_NOMATCH; }
int ruleIMAIL_LDAPeval(void *p) { u_int32_t current_byte = 0; u_int32_t width, value, lengthwidth; int retval; u_int32_t payload_len; const u_int8_t *cursor_normal, *beg_of_payload, *end_of_payload; SFSnortPacket *sp = (SFSnortPacket *) p; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; /* call flow match */ if (checkFlow(sp, ruleIMAIL_LDAPoptions[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; /* call content match */ if (contentMatch(sp, ruleIMAIL_LDAPoptions[1]->option_u.content, &cursor_normal) <= 0) { return RULE_NOMATCH; } if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; payload_len = end_of_payload - beg_of_payload; if(payload_len < 10) /* Minimum bind request length */ return RULE_NOMATCH; /* our contentMatch already assures us the first byte is \x30, so just jump over it */ current_byte++; /* Begin packet structure processing */ /* Packet length (only care about width of the specifier) */ if(beg_of_payload[current_byte] & 0x80) { current_byte += beg_of_payload[current_byte] & 0x0F; /* Does imail do this properly? */ } current_byte++; /* Message number (only care about width of the specifier) */ if(payload_len < current_byte + 8) return RULE_NOMATCH; if(beg_of_payload[current_byte] != 0x02) /* Int data type */ return RULE_NOMATCH; current_byte++; /* int width specifier */ if(beg_of_payload[current_byte] & 0x80) { width = beg_of_payload[current_byte] & 0x0F; current_byte++; if(payload_len < current_byte + width) return RULE_NOMATCH; retval = process_val(&(beg_of_payload[current_byte]), width, &value); if(retval < 0) return RULE_NOMATCH; /* width is either 0 or > 4 */ current_byte += width; /* width of data width specifier */ current_byte += value; /* width of data itself */ } else { current_byte += beg_of_payload[current_byte] + 1; } /* Bind request */ if(payload_len < current_byte + 5) return RULE_NOMATCH; if(beg_of_payload[current_byte] != 0x60) return RULE_NOMATCH; current_byte++; /* Message length (only care about width of the specifier) */ if(beg_of_payload[current_byte] & 0x80) { current_byte += beg_of_payload[current_byte] & 0x0F; } current_byte++; /* ldap version */ if(payload_len < current_byte + 3) return RULE_NOMATCH; /* ldap version */ if(beg_of_payload[current_byte] != 0x02) /* Int data type */ return RULE_NOMATCH; current_byte++; /* Now check for funkiness with the version field */ /* Get width of version number */ if(beg_of_payload[current_byte] & 0x80) { /* Excess bits in the high nibble */ if(beg_of_payload[current_byte] & 0x70) return RULE_MATCH; lengthwidth = beg_of_payload[current_byte] & 0x0F; current_byte++; if(payload_len < current_byte + lengthwidth) return RULE_NOMATCH; retval = process_val(&(beg_of_payload[current_byte]), lengthwidth, &value); if(retval < 0) return RULE_MATCH; /* Something screwy's going on around here */ width = value; current_byte += lengthwidth; } else { width = beg_of_payload[current_byte]; current_byte++; } if(payload_len < current_byte + width) return RULE_NOMATCH; /* In this case, if the version value is this fubar, trigger */ retval = process_val(&(beg_of_payload[current_byte]), width, &value); if(retval < 0) return RULE_MATCH; /* LDAP version > 9 (currently, should be 1-3) */ if(value > 9) return RULE_MATCH; return RULE_NOMATCH; }
static int rule13921eval(void *p) { SFSnortPacket *sp = (SFSnortPacket *)p; const u_int8_t *cursor, *beg_of_payload, *end_of_payload; int16_t lm_x; int n; /* cruft */ /* Data for holding our base64 data */ u_int8_t decoded_data[16]; u_int32_t num_bytes_extracted; /* General sanity checking */ if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; if((end_of_payload - beg_of_payload) < 32) return RULE_NOMATCH; /* call flow match */ if (checkFlow(sp, rule13921options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; /* call first content match */ if (contentMatch(sp, rule13921options[1]->option_u.content, &cursor) <= 0) { return RULE_NOMATCH; } /* call second content match */ if (contentMatch(sp, rule13921options[2]->option_u.content, &cursor) <= 0) { return RULE_NOMATCH; } /* Decode the part containing "/P.\x03/" to ensure the proper header and message type */ n = base64decode(&(beg_of_payload[8]), 4, decoded_data, sizeof(decoded_data), &num_bytes_extracted); if((n < 0) || (num_bytes_extracted < 3)) return RULE_NOMATCH; /* verify contents */ if((decoded_data[0] != 'P') || (decoded_data[2] != 0x03)) return RULE_NOMATCH; /* Now decode the part containing LM_X */ n = base64decode(&(beg_of_payload[24]), 8, decoded_data, sizeof(decoded_data), &num_bytes_extracted); if((n < 0) || (num_bytes_extracted < 6)) return RULE_NOMATCH; /* Extract LM_X, a signed 16-bit entity in little-endian format */ lm_x = decoded_data[2]; lm_x += decoded_data[3] << 8; if((lm_x < 0) || (lm_x > 56)) return RULE_MATCH; return RULE_NOMATCH; }
/* detection functions */ int rule17700eval(void *p) { const u_int8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload; SFSnortPacket *sp = (SFSnortPacket *) p; const u_int8_t *cursor_detect; u_int32_t fourCC; u_int32_t listsize; u_int32_t chunksize; u_int32_t stringsize; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule17700options[0]->option_u.flowFlags) > 0 ) { // flowbits:isset "wav_file.request"; if (processFlowbits(p, rule17700options[1]->option_u.flowBit) > 0) { // content:"LIST", payload raw, depth 0, fast_pattern; if (contentMatch(p, rule17700options[2]->option_u.content, &cursor_normal) > 0) { // content:"INFO", payload raw, offset 4, depth 4, relative; if (contentMatch(p, rule17700options[3]->option_u.content, &cursor_normal) > 0) { if (getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) return RULE_NOMATCH; //Extract size of list chunk size listsize = *(cursor_normal-8); listsize |= *(cursor_normal-7) << 8; listsize |= *(cursor_normal-6) << 16; listsize |= *(cursor_normal-5) << 24; //Make sure we have 8 bytes to extact 4CC and chunk size while(cursor_normal + 8 < end_of_payload) { //Extract chunk size chunksize = *(cursor_normal+4); chunksize |= *(cursor_normal+5) << 8; chunksize |= *(cursor_normal+6) << 16; chunksize |= *(cursor_normal+7) << 24; //In a loop, check the 4CC tag to see if it's any of the vulnerable tags fourCC = *cursor_normal << 24; fourCC |= *(cursor_normal + 1) << 16; fourCC |= *(cursor_normal + 2) << 8; fourCC |= *(cursor_normal + 3); if((fourCC == 0x49415254) || // IART (fourCC == 0x494e414d) || // INAM (fourCC == 0x49434f50)) { // ICOP //If it is one of the vulnerable tags, jump to the subchunk data //and treat it as a string. Count the actual size of the string //in another loop. stringsize = 0; cursor_detect = cursor_normal + 8; //Match if stringsize > either chunksize or listsize //otherwise keep counting characters or break if we //encounter a null character or end_of_payload while(cursor_detect < end_of_payload) { if(!*cursor_detect++) break; // stop when we find a null } stringsize = cursor_detect - cursor_normal - 8; //printf("stringsize=%d, chunksize=%d, listsize=%d\n"); if (stringsize > chunksize || stringsize > listsize) return RULE_MATCH; } //We need to do a check before skipping to the next chunk to make sure that //addition won't cause an integer overflow on cursor_normal if (cursor_normal + chunksize + 8 <= cursor_normal) // <= to also avoid infinite loops return RULE_NOMATCH; // Jump to the start of the next chunk cursor_normal += chunksize + 8; // fourCC, 4 bytes size, chunk size } } } } } return RULE_NOMATCH; }