/* detection functions */ int rule13308eval(void *p) { const u_int8_t *cursor_normal = 0; const u_int8_t *beg_of_buffer, *end_of_buffer; SFSnortPacket *sp = (SFSnortPacket *) p; // Base64 stuff u_int8_t base64buf[256], decodedbuf[256]; u_int32_t inputchars, base64bytes, decodedbytes; int i; if(sp == NULL) return RULE_NOMATCH; // flow:established, to_server; if (checkFlow(p, rule13308options[0]->option_u.flowFlags) <= 0) return RULE_NOMATCH; // Doing this content match is pretty useless because it's duplicated in our PCRE. // But we want to keep the structure for the pattern matcher. // // content:"Authorization|3A|", depth 0, nocase, fast_pattern; // if (contentMatch(p, rule13308options[1]->option_u.content, &cursor_normal) <= 0) // return RULE_NOMATCH; // pcre:"^Authorization\x3A\s*Basic[ \t]+", dotall, multiline, nocase; if (pcreMatch(p, rule13308options[2]->option_u.pcre, &cursor_normal) <= 0) return RULE_NOMATCH; if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_buffer, &end_of_buffer) != CURSOR_IN_BOUNDS) return RULE_NOMATCH; // At this point, cursor should point to the start of the auth data inputchars = (end_of_buffer > cursor_normal + sizeof(base64buf)) ? sizeof(base64buf) : end_of_buffer - cursor_normal; DEBUG_SO(printf("%d input chars: %*s\n", inputchars, inputchars, cursor_normal)); if(unfold_header(cursor_normal, inputchars, base64buf, sizeof(base64buf), &base64bytes) != 0) return RULE_NOMATCH; DEBUG_SO(printf("Successfully unfolded header (%s)(%d)\n", base64buf, base64bytes)); if(base64decode(base64buf, base64bytes, decodedbuf, sizeof(decodedbuf), &decodedbytes) < 0) return RULE_NOMATCH; DEBUG_SO(printf("Successfully base64 decoded (%s)(%d)\n", decodedbuf, decodedbytes)); for(i=0; i<decodedbytes; i++) { DEBUG_SO(printf("checking byte: %c\n", decodedbuf[i])); if(decodedbuf[i] == '%') { return RULE_MATCH; } else if(decodedbuf[i] == ':') { // Separator between username:password return RULE_NOMATCH; } } return RULE_NOMATCH; }
/* 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)
/* 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 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; }
/* detection functions */ int rule16415eval(void *p) { const uint8_t *cursor_normal = 0; SFSnortPacket *sp = (SFSnortPacket *) p; const uint8_t *beg_of_payload, *end_of_payload; uint16_t biBitCount; uint32_t biClrUsed; if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; // flow:established, to_client; if (checkFlow(p, rule16415options[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; //file_data; #ifndef MISSINGFILEDATA if (fileData(p, rule16415options[1]->option_u.cursor, &cursor_normal) <= 0 ) return RULE_NOMATCH; #else if (setCursor(p, rule16415options[1]->option_u.cursor, &cursor_normal) <= 0 ) return RULE_NOMATCH; #endif // content:"RIFF", depth 0, fast_pattern; if (contentMatch(p, rule16415options[2]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"AVI ", offset 4, depth 4, relative; if (contentMatch(p, rule16415options[3]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"strl", offset 0, relative; if (contentMatch(p, rule16415options[4]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"vids", offset 8, depth 4, relative if (contentMatch(p, rule16415options[5]->option_u.content, &cursor_normal) <= 0) return RULE_NOMATCH; // content:"strf", depth 0, relative; if (contentMatch(p, rule16415options[6]->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; if(cursor_normal + 40 > end_of_payload) return RULE_NOMATCH; //Jump past strf size DWORD and beginning of bitmapheader cursor_normal += 18; //extract biBitCount biBitCount = *cursor_normal++; biBitCount |= *cursor_normal++ << 8; //Jump to and extract biClrUsed cursor_normal += 16; biClrUsed = *cursor_normal++; biClrUsed |= *cursor_normal++ << 8; biClrUsed |= *cursor_normal++ << 16; biClrUsed |= *cursor_normal << 24; DEBUG_SO(printf("biBitCount = 0x%02x, biClrUsed = 0x%04x\n", biBitCount, biClrUsed)); /* Avoid potential wrap and false positive. * A shift larger than uint32 will always be greater. */ if (biBitCount >= 32) return RULE_NOMATCH; //Alert when... if (biClrUsed > (1 << biBitCount)) return RULE_MATCH; return RULE_NOMATCH; }