Fuzzing test stands
for…
Fuzz
testing or fuzzing is a software testing technique used to discover coding
errors and security loopholes in software, operating systems or networks by
inputting massive amounts of random data, called fuzz, to the system in an
attempt to make it crash. Fuzzing is a process of sending deliberately
malformed data to a program in order to generate failures, or errors in the
application. Fuzzing usually focuses on discovery of bugs that can be exploited
to allow an attacker to run their own code, and along with binary and source
code analysis fuzzing is one of the primary ways in which exploitable software
bugs are discovered. Fuzzers work best
for problems that can cause a program to crash, such as buffer overflow, cross-site scripting, denial of service attacks, format bugs
and SQL
injection. These schemes are often used by malicious hackers intent on wreaking the greatest possible
amount of havoc in the least possible time.
What are we facing?
It is relatively easy to find
possible weaknesses in an application if the source code is available and the
better it is annotated the easier it is to analyse. Access to binary code is
the next best thing but arguably too time consuming to analyse by disassembly.
Often the code is housed on a protected, remote server. A seemingly impregnable
housing that even keeps the binary shielded from prying eyes.
A burglar faced with a house that is
locked uses guile to force an entry. Locksmiths produce tumbler locks that can
only be opened with the correct key. The burglar often ignores the complexities
of lock-picking and will try to slide a flexible plastic sheet through the gap
between the door and the door jamb to push the catch back and the door
sometimes opens with ease. In other words, they attack the door in a way that
was not foreseen. If this does not work they may look elsewhere and smash a
window to gain entry.
Similarly, server attackers work on
accepted entry points by treating them in ways that they were not intended to
be used to force an entry. The more complex the program, the more likely there
is a flaw or a bug that can be worked on.
When looking for likely areas to
work on, access to annotated source code can show possible areas to work on, but
applications tend to have thousands of lines of code that need to be sifted
through. This becomes even worse if all you have is the compiled binary code
which has to be disassembled first. Then the hacker has to sift through the
commands without any annotations to guide them through the logic.
These two methods are the equivalent
of picking locks. In the first case using source code is akin to accessing the
locksmith’s original designs or an impression of the actual key and in the
second using picks and experience to prise the lock open. With so much code to
sift through, both methods are time consuming and require specialist knowledge
and patience. It is the preserve of the dedicated professional.
Often the code in any format is
unavailable and the average hacker has to stand back and look at the bigger
picture. Applications process data and that information is supplied externally
using keyboard input or from strings provided by ancillary applications. These
use specific formats, called protocols. A protocol may dictate that the
information is a field of characters or digits of a specific maximum length,
such as a name or a telephone number. The protocol may be more complex and
recognise only Adobe Acrobat pdf files or JPEG image files. If the input comes
from another application it might have a proprietary protocol.
Open backdoor
The question is how to subvert these
official entry points and use them to possibly crash the application or, even
better, to open up a way to inject new code that allows the hacker to take
control of the server. The incoming data needs to be stored in a buffer so that
it can be processed by the application and this is the key to opening up an
entry point.
In November, 1988, the Morris worm
gave the world a reality check on how hackers can disrupt computer systems and
inject disruptive code using weaknesses in software design. The worm exploited
flaws in BSD Unix running on DEC Vax and Sun servers and succeeded in bringing
10% of the Internet servers down. This alerted the world to the dangers of
buffer overflows.
Buffer overflows occur when
malformed data or oversized data fields are fed into an application. The
program is expecting input that complies with a specific protocol but what
happens if the input does not comply? In many cases the answer is that it will
disrupt the execution of the application in some way. This brute-force
technique has proved to be a rich source for code injection on many computer
applications and operating systems and 20 years on from the Morris exploit, it
still figures highly in the list of common attack methods.
It may seem strange that after so
many years there are still loopholes that can be exploited but this has a lot
to do with the way in which applications are tested before finally being
released to the users. The pre-launch quality assurance (QA) checking looks for
obvious problems by testing the protocols work. Initially this is performed by
doing everything in the way that the developer intended it to be done. If that
checks out there is a phase where the application is subjected to a range of
common methods of attack.
The problem is that the developer
should have protected the code from these exploits but the application is new
and may contain flaws that have never been exploited before. Even the best QA
department cannot test for everything and this is obvious when we see
Microsoft, Oracle and other software specialists rushing out fixes after an
application has been released for sale. There are just too many options
available and hackers always seem to find new ways to exploit code that could
never have been dreamed of by the QA team.
I’m Fuzzing
The process of feeding in false
inputs is known as fuzzing and this has become a small industry of its own. Wide
ranges of fuzzing tools have been developed by the elite hacker community to
enable the rank and file to execute exploits beyond their own natural
abilities. These tools are also adopted or adapted in the QA world to test
applications before they are released.
Buffer overflow attacks are well
known and a number of tools, or fuzzers, are openly available on the Internet.
Some of these are used by QA but new tools using sophisticated techniques are
appearing all the time and many target specific applications.
Fuzzing techniques are used to find
all manner of security vulnerabilities. Apart from the highly publicised buffer
overflows, there are the related integer overflows, race condition flaws, SQL
injection, cross-site scripting. In fact, the majority of vulnerabilities can
be exploited or detected using fuzzing techniques. When the applications for
exploiting the range of possible vulnerabilities are added to the buffer
overflow fuzzing tools, the list is long and daunting.
With direct access to the server at
the focus of the fuzzing attack, it is easy to monitor the effects on the host.
Valuable information can be gained by using a suitable debugger such as the
open sourced OllyDbg for Windows system or the GDB debugger that comes free
with most Unix systems. Specific parameters can also be revealed, such as
memory usage, network activity, file system actions and, for Windows, registry
file access. Tools for these purposes can be found as part of the Sys internals
Suite, now owned by Microsoft.
Remote hacking, also known as black
box fuzzing, lacks this refined option. Instead, monitoring network traffic may
provide clues as to whether a system has become unstable or crashed. The
absence of reply packets, or the presence of unusual packets, or the absence of
a service for long periods may indicate a crash. Applications like Autodafe are
examining the possibility of analysing program reactions using tracers in an
attempt to improve detection of the server status.
Fuzzing techniques
Fuzzing tools are useful because
they automate the drudgery of the task. For example, when transmitting data
fields of various sizes, it is boring to do this by manually incrementing field
lengths when the task can be easily handled in code. Practice has shown that
buffer lengths often follow a power of two sequence so test data tends to
increase in sizes over the normal size. This means that the sequence 16, 32,
64,128 would be matched by data lengths of 20, 40, 70, 130.
Fuzzing techniques fall into three
basic types: session data, specialised and generic. Session data fuzzing
is the
simplest because it transforms legal data incrementally. For example, the
starting point could be a SMTP protocol:
mail from:
sender@testhost
This would then be sent in the
following forms to see what effect they have:
mailmailmailmail from: sender@testhost
mail fromfromfromfrom:
sender@testhost
mail from:::: sender@testhost
mail from:
sendersendersendersender@testhost
mail from: sender@@@@testhost
mail from:
sender@testhosttesthosttesthosttesthost
Specialised fuzzers are the ones
that target specific protocols. Typically these would be the network protocols
such as SMTP, FTP, SSH and SIP but these have now expanded to include file
types such as documents, image files, video formats, and Flash animations.
The most flexible type is the
generic fuzzer which allows the user to define the packet type, the protocol
and the elements within it to be fuzzed. The flexibility is balanced by the
fact that the user has to be aware of the vulnerabilities to be tested and some
may be overlooked. It is crucial that every element in the protocol is tested,
no matter how unimportant it may seem. In the above example, it may seem
pointless to repeat the colon but this could be the flaw that the hacker is
looking for. The lesson is that nothing should be taken for granted.
Developers are not infallible. When
buffer overflows started to hit the headlines, many C programmers switched to
using bounded string operations as a cure-all. Unfortunately, the strncpy()
command was often implemented incorrectly resulting in the Off By One error.
This was caused by setting a buffer size to, say, 32. It sounds logical enough
but the input field has to have a null value terminator and that has to be
allowed for in the character count and added by the application. The null is
there when the buffer is set up but would be overwritten by an apparently legal
32 character input. This means that the boundary between neighbouring buffers
disappears and future accesses may treat the two strings as a larger single
buffer opening up the possibility of a buffer overflow exploit where one may
not have existed before.
Hacker’s activities
Having determined that the buffer is
lying adjacent to a stack, carefully crafting an oversized buffer input
will
overwrite the jump address at the top of the stack with a pointer to executable
code stored elsewhere in RAM instead of just arbitrary bites as before. Usually
the pointer is set to the beginning of the buffer. When writing the new input
padding is used to ensure the four bytes carrying the jump location is
correctly placed on the stack. Rather than just any kind of padding, the bytes
used form a shellcode routine in
assembly code. When the pointer redirects program execution to the buffered
code, the attacker has taken control of program execution and can take control
of the server, assuming the interrupted application had suid root, or
administrator, rights. Obviously, the
larger the buffer, the larger the chunk of code that can be inserted.
Summary
The growth of fuzzing has been
remarkable. From the QA perspective it offers a very effective way to discover
flaws early. For attackers it is presenting a way to penetrate black box
servers that would otherwise be difficult to penetrate. Reports of fuzzing
exploits are vague and merely say that a specific program crashes when it opens
a file containing a particular malformed file. There is no clue as to why or
how this happens, leaving the security experts to have to recreate the
conditions to find out the mechanics of the exploits.
The number of fuzzer programs is
increasing in both specialisms and subtlety. As the tools become more sophisticated,
developers become bogged down with patch requests. This results in rising
maintenance costs and a point is reached where a trade-off between increasing
the security and financial considerations may start to affect the reliability
of software. There is a danger that vulnerability detection will become much
more reactive rather than proactive.
Vulnerability testing is more
important now than ever before as the financial gains from professional hacking
becomes more attractive as finance is increasingly directed through the
Internet. The current pressures on in-house departmental QA to keep up with
faster moving changes in the breadth and scope of exploits is making
outsourcing of the responsibility more attractive than ever before .