CHAPTER 25
Intelligent Fuzzing with Sulley

In Chapter 22, we covered basic fuzzing. The problem with basic fuzzing is that you often only scratch the surface of a server’s interfaces and rarely get deep inside the server to find bugs. Most real servers have several layers of filters and challenge/response mechanisms that prevent basic fuzzers from getting very far. Recently, a new type of fuzzing has arrived called intelligent fuzzing. Instead of blindly throwing everything but the kitchen sink at a program, techniques have been developed to analyze how a server works and to customize a fuzzer to get past the filters and reach deeper inside the server to discover even more vulnerabilities. To do this effectively, you need more than a fuzzer. First, you need to conduct a protocol analysis of the target. Next, you need a way to fuzz that protocol and get feedback from the target as to how you are doing. The Sulley fuzzing framework automates this process and allows you to intelligently sling packets across the network. Thus, this chapter covers the following topics:

• Protocol analysis

• Sulley fuzzing framework

Protocol Analysis

Since most servers perform a routine task and need to interoperate with random clients and other servers, most servers are based on some sort of standard protocol. The Internet Engineering Task Force (IETF) maintains the set of protocols that forms the Internet as we know it. So, for example, the best way to find out how an LPR server operates is to look up the Request for Comments (RFC) document for the LPR protocol, which can be found on www.ietf.org as RFC 1179.

Here is an excerpt from RFC 1179 (see www.ietf.org/rfc/rfc1179.txt):


   3.1 Message formats
      
      LPR is a a TCP-based protocol. The port on which a line printer
      daemon listens is 515. The source port must be in the range 721 to
      731, inclusive. A line printer daemon responds to commands sent to
      its port. All commands begin with a single octet code, which is a
      binary number which represents the requested function. The code is
      immediately followed by the ASCII name of the printer queue name on
      which the function is to be performed. If there are other operands
      to the command, they are separated from the printer queue name with
      white space (ASCII space, horizontal tab, vertical tab, and form
      feed). The end of the command is indicated with an ASCII line feed
      character.


Image

NOTE

As we can see in the preceding excerpt, the RFC calls for the source port to be in the range 721–731, inclusive. This could be really important. If the target LPR daemon conformed to the standard, it would reject all requests that were outside this source port range. The target we are using (NIPRINT3) does not conform to this standard. If it did, no problem, we would have to ensure we sent packets in that source port range.


Further down in RFC 1179, you will see diagrams of LPR daemon commands:


   Source: http://www.ietf.org/rfc/rfc1179.txt
   5.1 01 - Print any waiting jobs

         +----+-------+----+
         | 01 | Queue | LF |
         +----+-------+----+
         Command code - 1
         Operand - Printer queue name

      This command starts the printing process if it not already running.
   5.2 02 - Receive a printer job

         +----+-------+----+
         | 02 | Queue | LF |
         +----+-------+----+
         Command code – 2
         Operand - Printer queue name

   Receiving a job is controlled by a second level of commands. The
   daemon is given commands by sending them over the same connection.
   The commands are described in the next section (6).
   After this command is sent, the client must read an acknowledgement
   octet from the daemon. A positive acknowledgement is an octet of
   zero bits. A negative acknowledgement is an octet of any other
   pattern.

And so on...

From this, we can see the format of commands the LPR daemon will accept. We know the first octet (byte) gives the command code. Next comes the printer queue name, followed by an ASCII line feed (LF) command (“ ”).

As we can see in section 5.2 of the preceding RFC, the command code of “x02” tells the LPR daemon to “receive a printer job.” At that point, the LPR daemon expects a series of subcommands, which are defined in section 6 of the RFC.

This level of knowledge is important, as now we know that if we want to fuzz deep inside an LPR daemon, we must use this format with proper command codes and syntax. For example, when the LPR daemon receives a command to “receive a printer job,” it opens up access to a deeper section of code as the daemon accepts and processes that printer job.

We have learned quite a bit about our target daemon that will be used throughout the rest of this chapter. As you have seen, the RFC is invaluable to understanding a protocol and allows you to know your target.

References

IETF RFC search www.ietf.org/

RFC 1179, “Line Printer Daemon Protocol” www.ietf.org/rfc/rfc1179.txt

Sulley Fuzzing Framework

Pedram Amini has done it again! He has brought us Sulley. Sulley gets its name from the fuzzy character in the movie Monsters, Inc. This tool is truly revolutionary in that it provides not only a great fuzzer and debugger, but also the infrastructure to manage a fuzzing session and conduct postmortem analysis.

Installing Sulley

Download the latest version of Sulley from http://code.google.com/p/sulley/. Install the Sulley program to a folder in the path of both your host machine and your target virtual machine. This is best done by establishing a shared folder within the target virtual machine and pointing it to the same directory in which you installed Sulley on the host. To make things even easier, you can map the shared folder to a drive letter from within your target virtual machine.

Powerful Fuzzer

Sulley is a nimble yet very powerful fuzzer based on Dave Aitel’s block-based fuzzing approach. In fact, if you know Dave’s SPIKE fuzzing tool, you will find yourself at home with Sulley. Sulley organizes the fuzzing data into requests. As you will see later, you can have multiple requests and link them together into what is called a session. You can start a request by using the s_initialize function; for example:


   s_initialize("request1")

The only required argument for the s_initialize() function is the request name.

Primitives

Now that we have a request initialized, let’s build on that by adding primitives, the building blocks of fuzzing. We will start out simple and build up to more complex fuzzing structures. When you want to request a fixed set of data that is static, you can use the s_static() function. The syntax follows:


   s_static("default value", <name>, <fuzzable>, <num_mutations>)


Image

NOTE

As with the other functions in this section, the required arguments are shown in quotes and the optional arguments are shown in angle brackets.


Following is a simple example of using s_static:


   s_static("hello haxor")

Sulley provides alternate but equivalent forms of s_static():


   s_dunno("hello haxor")
   s_unknown("hello haxor")
   s_raw("hello haxor")

All of these provide the same thing, a static string “hello haxor” that will not be fuzzed.

Using Binary Values

With Sulley it is easy to represent binary values in many formats using the s_binary primitive. It has the following syntax:


   s_binary("default value", <name>, <fuzzable>, <num_mutations>)

Here is an example:


   s_binary("xad 0x01 0x020x03 da bex0a", name="crazy")

Generating Random Data

With Sulley it is easy to generate random chunks of data, using the s_random primitive. This primitive will start with the default value, then generate from the minimum size to the maximum size of random blocks of data. When it finishes, the default value will be presented. If you want a fixed size of random data, then set min and max to the same value.

The syntax for s_random follows:


   s_random("default raw value", "min", "max", <name>, <fuzzable>, <num_mutations>)


Image

NOTE

Although min and max size are required arguments, if you want a random size of random data for each request, then set the max size to –1.


The following shows an example of s_random in action:


   s_random("xad 0x01 0x020x03 da bex0a", 1, 7, name="nuts")

Strings and Delimiters

When you want to fuzz a string, use the s_string() function. It has the following syntax:


   s_string("default value", <name>, <fuzzable>, <encoding>, <padding>, <size>)

The first fuzz request will be the default value; then, if the fuzzable argument is set (On by default), the fuzzer will randomly fuzz that string. When finished fuzzing that string, the default value will be sent thereafter.

Some strings have delimiters within them; they can be designated with the s_delim() function. The s_delim() function accepts the optional arguments fuzzable and name, as shown in the following examples:


   s_string("Hello", name="first_part")
   s_delim(" ")
   s_string("Haxor!", name="second_part")

The preceding sequence will fuzz all three portions of this string sequentially since the fuzzable argument is True by default.

Bit Fields

Bit fields are used to represent a set of binary flags. Some network or file protocols call for the use of bit fields. In Sulley, you can use the s_bit_field function, which has the following syntax:


   s_bit_field("default value", "size", <name>, <fuzzable>, <full range>,
   <signed>, <format>,
   <endian>)

Two other names for s_bit_field are

s_bit

s_bits

An example using the latter name follows:


   s_bits(5,3, full_range=True) # this represents 3 bit flags, initially "101"

Integers

Integers may be requested and fuzzed with the s_byte function, the syntax for which is shown here:


   s_byte("default value", <name>, <fuzzable>, <full range>, <signed>, <format>, <endian>)

Other sizes of integers may be requested and fuzzed with the following functions:

• 2 bytes: s_word(), s_short()

• 4 bytes: s_dword(), s_long(), s_int()

• 8 bytes: s_qword(), s_double()

Here are some examples:


   s_byte(1)
   s_dword(23432, name="foo", format="ascii")

Blocks

Now that you have the basics down, keep going by lumping several primitives together into a block. Here is the syntax:


   s_block_start("required name", <group>, <encoder>, <dep>,<dep_value>,
   <dep_values>, <dep_compare>)
   s_block_end("optional name")

The interesting thing about blocks is that they may be nested within other blocks. For example:


   if s_block_start("foo"):
      s_static("ABC")
      s_byte(2)
      if s_block_start("bar"):
         s_string("123")
         s_delim(" ")
         s_string("ABC")
         s_block_end("bar")
      s_block_end("foo")

We can test this fuzz block with a simple test harness:


   from sulley import *

   ############################################################################
   s_initialize("foo request")

   if s_block_start("foo"):
      s_static("ABC")
      s_byte(2) #will be fuzzed first
      if s_block_start("bar"):
         s_string("123") #will be fuzzed second
         s_delim(" ")
         s_string("ABC")
         s_block_end("bar")
      s_block_end("foo")

   #######################################

   req1 = s_get("foo request")
   for i in range(req1.names["foo"].num_mutations()) :
      print(s_render())
      s_mutate()

The preceding program is simple and will print our fuzz strings to the screen so we can ensure the fuzzer is working as we desire. The program works by first defining a basic request called “foo request.” Next the request is fetched from the stack with s_get function and a for loop is set up to iterate through the permutations of the fuzzed block, printing on each iteration. We can run this program from the sulley directory:

Image

Press CTRL-C to end the script. As you can see, the script fuzzed the byte first; a while later it started to fuzz the string, and so on.

Groups

Groups are used to pre-append a series of values on the block. For example, if we wanted to fuzz an LPR request, we could use a group as follows:


   from sulley import *

   ############################################################################
   s_initialize("LPR shallow request")
   #Command Code (1 byte)|Operand|LF
   s_group("command",values=['x01','x02','x03','x04','x05'])
   if s_block_start("rcv_request", group="command"):
       s_string("Queue")
       s_delim(" ")
       s_static(" ")
       s_block_end()

This script will pre-append the command values (one byte each) to the block. For example, the block will fuzz all possible values with the prefix ‘x01’. Then it will repeat with the prefix ‘x02’, and so on, until the group is exhausted. However, this is not quite accurate enough, as each of the different command values has a different format outlined in the RFC. That is where dependencies come in.

Dependencies

When you need your script to make decisions based on a condition, then you can use dependencies. The dep argument of a block defines the name of the object to check, and the dep_value argument provides the value to test against. If the dependent object equals the dependent value, then that block will be rendered. This is like using the if/ then construct in languages like C or Python.

For example, to use a group and change the fuzz block for each command code, we could do the following:


   ############################################################################
   s_initialize("LPR deep request")

   #Command Code (1 byte)|Operand|LF
   s_group("command",values=['x01','x02','x03','x04','x05'])

   # Type 1,2: Receive Job
   if s_block_start("rcv_request", dep="command", dep_values=['x01', 'x02']):
       s_string("Queue")
       s_delim(" ")
       s_static(" ")
       s_block_end()

   #Type 3,4: Send Queue State
   if s_block_start("send_queue_state", dep="command", dep_values=['x03','x04']):
       s_string("Queue")
       s_static(" ")
       s_string("List")
       s_static(" ")
       s_block_end()

   #Type 5: Remove Jobs
   if s_block_start("remove_job", dep="command", dep_value='x05'):
       s_string("Queue")
       s_static(" ")
       s_string("Agent")
       s_static(" ")
       s_string("List")
       s_static(" ")
       s_block_end()
   # and so on... see RFC for more cases

To use this fuzz script later, add the two earlier code blocks (“LPR shallow request” and “LPR deep request”) to a file called {common host-guest path to sulley} equest lpr.py.


Image

NOTE

There are many other helpful functions in Sulley, but we have enough information to illustrate an intelligent LPR fuzzer at this point.


Sessions

Now that we have defined several requests in a fuzz script called sulley equestlpr.py, let’s use them in a fuzzing session. In Sulley, sessions are used to define the order in which the fuzzing takes place. Sulley uses a graph with nodes and edges to represent the session and then walks each node of the graph to conduct the fuzz. This is a very powerful feature of Sulley and will allow you to create some very complex fuzzing sessions. We will keep it simple and create the following session driver script in the sulley main directory:


   {common host-guest path to sulley}fuzz_niprint_lpr_servert_515.py
   import time

   from sulley import *
   from requests import lpr

   # establish a new session
   sess = sessions.session(session_filename="audits/niprint_lpr_515_a.session",
                                crash_threshold=10)

   # add nodes to session graph.
   sess.connect(s_get("LPR shallow request")) #shallow fuzz
   sess.connect(s_get("LPR deep request")) #deep fuzz, with correct formats

   # render the diagram for inspection (OPTIONAL)
   fh = open("LPR_session_diagram.udg", "w+")
   fh.write(sess.render_graph_udraw())
   fh.close()
   print "graph is ready for inspection"


Image

NOTE

The crash_threshold option allows us to move on once we get a certain number of crashes.


Now we can run the program and produce the session graph for visual inspection:


   {common host-guest path to sulley}>mkdir audits # keep audit data here
   {common host-guest path to sulley}>python fuzz_niprint_lpr_servert_515.py
   graph is ready for inspection

Next, open the session graph with uDraw:


   {common host-guest path to sulley}>"c:Program FilesuDraw(Graph)in
   uDrawGraph.exe"
   LPR_session_diagram.udg

The window shown in Figure 25-1 should appear. As you can see, Sulley will first fuzz the “LPR shallow request,” and then fuzz the “LPR deep request.”

Image

Figure 25-1 uDraw representation of the Sulley session graph


Image

NOTE

We are not doing justice to the session feature of Sulley; see the tool’s documentation for a description of the full capability here.


Before we put our fuzzer into action, we need to instrument our target (which is running in VMware) so that we can track faults and network traffic.

Monitoring the Process for Faults

Sulley provides a fantastic fault monitoring tool that works within the target virtual machine and attaches to the target process and records any nonhandled exceptions as they are found. The request ID number is captured and feedback is given to the Sulley framework through the PEDRPC custom binary network protocol.


Image

NOTE

To start the process_monitor.py script, you need to run it from a common directory with the host machine.


We will create a place to keep our audit data and launch the process_monitor.py script from within the target virtual machine as follows:


   {common host-guest path to sulley}>mkdir audits # not needed if done
   previously
   {common host-guest path to sulley}>python process_monitor.py -c audits
   niprint_lpr_515_a.crashbin -l 5
   [02:00.15]  Process Monitor PED-RPC server initialized:
   [02:00.15]        crash file: audits iprint_lpr_515_a.crashbin
   [02:00.15]        # records: 0
   [02:00.15]        proc name: None
   [02:00.15]        log level: 5
   [02:00.15] awaiting requests...

As you can see, we created a crashbin to hold all of our crash data for later inspection. By convention, use the audits folder to hold current fuzz data. We have also set the logging level to 5 in order to see more output during the process.

At this point, the process_monitor.py script is up and running and ready to attach to a process.

Monitoring the Network Traffic

After the fuzzing session is over, we would like to inspect network traffic and quickly find the malicious packets that caused a particular fault. Sulley makes this easy by providing the network_monitor.py script.

We launch the network_monitor.py script from within the virtual machine as follows:


   {common host-guest path to sulley}>mkdir audits iprint_lpr_515
   {common host-guest path to sulley}>python network_monitor.py -d 1 -f "src or dst
   port 515" -–log_path audits iprint_lpr_515 -l 5
   [02:00.27] Network Monitor PED-RPC server initialized:
   [02:00.27]      device:    DeviceNPF_{F581AFA3-D42D-4F5D-8BEA-55FC45BD8FEC}
   [02:00.27]      filter:    src or dst port 515
   [02:00.27]      log path:  audits iprint_lpr_515
   [02:00.27]      log_level: 5
   [02:00.27] Awaiting requests...

Notice we have started sniffing on interface [1]. We assigned a pcap storage directory and a Berkley Packet Filter (BPF) of “src or dst port 515” since we are using the LPR protocol. Again, we set the logging level to 5.

At this point, we ensure that our target application (NIPRINT3) is up and running, ensure that we can successfully connect to it from our host, and save a snapshot called “sulley.” Once the snapshot is saved, we close VMware.

Controlling VMware

Now that we have our target set up in a virtual machine and saved in a snapshot, we can control it from the host with the vmcontrol.py script.

We launch the vmcontrol.py script in interactive mode from the host as follows:


   C:Program FilesSulley Fuzzing Framework>python vmcontrol.py –i
   [*] Entering interactive mode...
   [*] Please browse to the folder containing vmrun.exe...
   [*] Using C:Program FilesVMwareVMware Workstationvmrun.exe
   [*] Please browse to the folder containing the .vmx file...
   [*] Using G:VMsWinXP5Windows XP Professional.vmx
   [*] Please enter the snapshot name: sulley
   [*] Please enter the log level (default 1): 5
   [02:01.49] VMControl PED-RPC server initialized:
   [02:01.49]       vmrun:      C:PROGRA~1VMwareVMWARE~1vmrun.exe
   [02:01.49]       vmx:        G:VMsWinXP5WINDOW~1.VMX
   [02:01.49]       snap name:  sulley
   [02:01.49]       log level:  5
   [02:01.49] Awaiting requests...

At this point, vmcontrol.py is ready to start accepting commands and controlling the target virtual machine by resetting the snapshot as necessary. You don’t have to worry about this; it is all done automagically by Sulley.


Image

NOTE

If you get an error when running this script that says “[!] Failed to import win32api/win32com modules, please install these! Bailing...,” you need to install the win32 extensions to Python, which can be found at http://starship.python.net/crew/mhammond/win32/.


Putting It All Together

We are now ready to put it all together and start our fuzzing session. Since we have already built the session, we just need to enable a few more actions in the fuzzing session script.

The following code can be placed at the bottom of the existing file:


   {common host-guest path to sulley}fuzz_niprint_lpr_servert_515.py
   #######################################################################
   #set up target for session
   target = sessions.target("10.10.10.130", 515)

   #set up pedrpc to talk to target agent.
   target.netmon     = pedrpc.client("10.10.10.130", 26001)
   target.procmon    = pedrpc.client("10.10.10.130", 26002)
   target.vmcontrol  = pedrpc.client("127.0.0.1",    26003)

   target.procmon_options = 
   {
        "proc_name"  : "NIPRINT3.exe",
   #      "stop_commands"  : ['net stop "NIPrint Service"'],
   #      "start_commands" : ['net start "NIPrint Service"'],
   }
   #start up the target.
   target.vmcontrol.restart_target()
   print "virtual machine up and running"

   # add target to session.
   sess.add_target(target)
   
   #start the fuzzing by walking the session graph.
   sess.fuzz()
   print "done fuzzing. web interface still running."

This code sets up the target for the fuzzing session and provides arguments for the process_monitor.py script. Next the virtual machine target snapshot is reset, we add the target to the session, and the fuzzing begins. We commented out the service start and stop commands, as the version of NIPRINT3 we are using has a demo banner that requires user interaction when the process starts, so we will not be using the service start/stop capability of Sulley for this server.

We can run this program as before; however, now the fuzzing session will begin and requests will be sent to the target host over port 515:


   {common host-guest path to sulley}>python fuzz_niprint_lpr_servert_515.py
   graph is ready for inspection
   virtual machine up and running
   [02:02.17] current fuzz path: -> LPR shallow request
   [02:02.18] fuzzed 0 of 12073 total cases
   [02:02.18] fuzzing 1 of 5595
   [02:02.31] xmitting: [1.1]
   [02:02.45] netmon captured 451 bytes for test case #1
   [02:02.50] fuzzing 2 of 5595
   [02:02.50] xmitting: [1.2]
   [02:02.53] netmon captured 414 bytes for test case #2
   [02:02.54] fuzzing 3 of 5595
   [02:02.55] xmitting: [1.3]
   [02:02.56] netmon captured 414 bytes for test case #3

   ...truncated for brevity...
   
   [02:03.06] fuzzing 8 of 5595
   [02:03.06] xmitting: [1.8]
   [02:03.07] netmon captured 909 bytes for test case #8
   [02:03.07] fuzzing 9 of 5595
   [02:03.08] xmitting: [1.9]
   [02:03.09] netmon captured 5571 bytes for test case #9
   [02:03.16] procmon detected access violation on test case #9
   [02:03.16] [INVALID]:41414141 Unable to disassemble at 41414141 from thread 452
   caused access violation
   [02:03.17] restarting target virtual machine
   PED-RPC> unable to connect to server 10.10.10.130:26002
   PED-RPC> unable to connect to server 10.10.10.130:26002
   [02:06.26] fuzzing 10 of 5595
   [02:06.34] xmitting: [1.10]
   [02:06.36] netmon captured 5630 bytes for test case #10
   [02:06.43] procmon detected access violation on test case #10
   [02:06.44] [INVALID]:41414141 Unable to disassemble at 41414141 from thread 452
   caused access violation
   [02:06.44] restarting target virtual machine
   Tuesday, November 27, 2007 3:04:58 PM

You should see your VMControl window react by showing the communication with VMware. Next you should see the virtual machine target reset and start to register packets and requests. You will then see the request being sent to the target virtual machine from the host, as shown earlier.

After the first request is sent, open your browser and point it to http://127.0.0.1:26000/. Here you should see the Sulley Fuzz Control screen.

Image

As of the writing of this book, you have to refresh this page manually to see updates.

Postmortem Analysis of Crashes

When you have seen enough on the Sulley Fuzz Control screen, you may stop the fuzzing by killing the fuzzing script or by clicking Pause on the Sulley Fuzz Control screen. At this point, you can browse the crashes you found by clicking the links in the Sulley Fuzz Control screen or by using the crash_explorer.py script.

You may view a summary of the crashes found by pointing the script to your crashbin:


   {common host-guest path to sulley}>python utilscrashbin_explorer.py audits
   niprint_lpr_515_a.crashbin
   [2] [INVALID]:41414141 Unable to disassemble at 41414141 from thread 452 caused
   access violation
           9, 10,

   [1] [INVALID]:5c2f5c2f Unable to disassemble at 5c2f5c2f from thread 452 caused
   access violation
           17,

   [1] [INVALID]:2e2f2e2f Unable to disassemble at 6e256e25 from thread 452 caused
   access violation
           18,

We stopped our fuzz session after a few minutes, but we already have some juicy results. As you can see bolded in the preceding output, it looks like we controlled eip already. Wow, as we know from Chapter 15, this is going to be easy from here.

Now, if we wanted to see more details, we could drill down on a particular test case:


   {common host-guest path to sulley}>python utilscrashbin_explorer.py audits
   niprint_lpr_515_a.crashbin -t 9
   [INVALID]:41414141 Unable to disassemble at 41414141 from thread 452 caused
   access violation when attempting to read from 0x41414141
   
   CONTEXT DUMP
      EIP: 41414141 Unable to disassemble at 41414141
      EAX: 00000070 ( 112) -> N/A
      EBX: 00000000 ( 0) -> N/A
      ECX: 00000070 ( 112) -> N/A
      EDX: 00080608 ( 525832) -> |ID{,9, (heap)
      EDI: 004254e0 ( 4347104) -> Q|` (NIPRINT3.EXE.data)
      ESI: 007c43a9 ( 8143785) -> /.:/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (heap)
      EBP: 77d4a2de (2010424030) -> N/A
      ESP: 0006f668 ( 456296) -> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (stack)
      +00: 41414141 (1094795585) -> N/A
      +04: 41414141 (1094795585) -> N/A
      +08: 41414141 (1094795585) -> N/A
      +0c: 41414141 (1094795585) -> N/A
      +10: 41414141 (1094795585) -> N/A
      +14: 41414141 (1094795585) -> N/A
   disasm around:
           0x41414141 Unable to disassemble
   SEH unwind:
           0006fd50 -> USER32.dll:77d70494
           0006ffb0 -> USER32.dll:77d70494
           0006ffe0 -> NIPRINT3.EXE:00414708
           ffffffff -> kernel32.dll:7c8399f3

The graphing option comes in handy when you have complex vulnerabilities and need to visually identify the functions involved. However, this is a straightforward buffer overflow and eip was smashed.

Analysis of Network Traffic

Now that we have found some bugs in the target server, let’s look at the packets that caused the damage. If you look in the sulleyaudits iprint_lpr_515 folder, you will find too many pcap files to sort through manually. Even though they are numbered, we would like to filter out all benign requests and focus on the ones that caused crashes. Sulley provides a neat tool to do just that, called pcap_cleaner.py. We will use the script as follows:


   {common host-guest path to sulley}>python utilspcap_cleaner.py audits
   niprint_lpr_515_a.crashbin audits iprint_lpr_515

Now we are left with only pcap files containing the request that crashed the server. We can open them in Wireshark and learn what caused the crash.

From Figure 25-2 we can see that a request was made to “start print job,” which started with ‘x01’ and a queue name ‘x2fx2ex3ax2f’ and then many A’s. The A’s overwrote eip somewhere due to a classic buffer overflow. At this point, we have enough information to produce a vulnerability notice to the vendor...oh wait, it has already been done!

Exploring Further

As you have seen, we have rediscovered the NIPRINT3 buffer overflow (see “References”). However, there may be more bugs in that server or any other LPR server. We will leave it to you to use the tools and techniques discussed in this chapter to explore further.

References

Fuzzing: Brute Force Vulnerability Discovery (M. Sutton, A. Greene, and P. Amini) Addison-Wesley Professional, 2007

Fuzzing resources (Securitytools) securitytools.wikidot.com/fuzzing Pedram Amini pedram.openrce.org/and dvlabs.tippingpoint.com/team/pamini Sulley framework code.google.com/p/sulley/

“The Advantages of Block-Based Protocol Analysis for Security Testing”

(Dave Aitel) www.immunitysec.com/downloads/advantages_of_block_based_ analysis.pdf

NIPRINT Vulnerability www.securityfocus.com/bid/8968

Image

Figure 25-2 Wireshark showing the packet that crashed the LPR server

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.105.114