Buffer Overrun

 

Description

  • A buffer overrun is caused when data is stored in a buffer, that is too small to contain the data being stored. This causes the data to overflow or overrun into adjacent memory.
  • If the software being tested is written in C or C++,even only in part, this is definitely something that should carefully tested.
  • In following figure, adjacent memory has been allocated as follows: a 2 byte integer with contents of 1(A), an 8-byte-long string buffer that is empty. application (B), a 2-byte integer buffer containing the number 3(C), and a 2-byte-long string buffer with the string “usr” and the string terminating zero byte (D).
  • Now the program attempts to store the character string “extrabits” and the string terminating zero byte in buffer B. No bounds checking in advance means it ills buffer B and overflows to buffer C. The buffer contents now look like those in following figure.

  • If an attacker is aware of this buffer overrun vulnerability and knows that buffer D contains an access level variable, the string being stored in buffer B can be crafted to overwrite the buffer D data with the string that grants administrator privileges. It now might look like following figure.

Types of buffer overruns

Stack buffer

There is a special stack that holds information about the active subroutines of a program. One of the main functions of the stack is to keep track of where to return control when the current subroutine finishes executing. This is often done by putting the return address into the call stack. Depending on the environment, programming language, and other variables, the stack can also be responsible for:
  • Local data storage
  • Parameter passing
  • Pointer to the current instance
  • Evaluation stack
  • Context of the enclosing subroutine
  • Other return state information
The stack is comprised of stack frames, each one of which corresponds to a call to a subroutine that has not yet terminated with a return. The stack frame at the top of the stack (the last one pushed on) belongs to the routine that is currently executing. Sack frames may be different sizes depending on the needs of the subroutines involved. Following figure is a sample view of a stack.

  • Stack buffer overruns depend on the fact that most C compilers store both return addresses and local variables on the same stack.
  • When this Is the case, an unbounded local variable can be used to overwrite the stack and create problems like substituting an address to an attacker's code or a command window in place of the subroutine's legitimate return address.

Heap Buffer Overruns

  • Heap is an area of memory, where dynamic memory allocation and deallocation is made at runtime by the C malloc() and calloc() functions, and the C++ new operator.
  • When the heap is corrupted by a heap-based buffer overrun, the results can be unpredictable, as some of the linked list structure is typically overwritten in the process of the overrun. A sample view of the heap is shown in following figure.

  • The number of vulnerabilities involving heap buffer overruns is expected to grow, as more and more steps are taken to protect the stack. and attackers turn to heap overruns instead.
  • There is still a belief that not much can be done with heap buffer overruns because of the inherently dynamic nature of its storage structure, and this leads to heap buffer overruns not being subjected to scrutiny as they should be.

Anatomy of an Exploit

  • Most exploits of buffer overruns start with first knowing that buffer overrun vulnerability exists.
  • Once the vulnerability is known, the attacker needs to carefully craft, String to exploit the buffer overrun.
  • Once the attacker finds out how long a string is required to achieve the desired effect, they have an exploit of the vulnerability. Now, they can combine this exploit with other codes to create malicious software to distribute or install.

Real-World Examples

  • Buffer Overrun
    • Example of a buffer overrun happened on November 3, 1988, now often known as “Black Thursday.” A 99-line bootstrap program plus a large object file, both in C, infected Sun Microsystems' Sun 3 systems and VAX Tm computers running variants of 4 BSD (Berkeley Software Distribution) Unix.
  • One of the exploits used was a buffer overrun of the fingered program's input buffer. Fingered (a background process or daemon) was using the C gets call that takes input to a buffer without any bounds check.
  • In this case, a string of 536 bytes was passed to fingered, which overflowed as input buffer and overwrote parts of the stack.
  • This string was specifically designed so the return stack frame for main was then a pointer into the buffer on the stack
  • There were instructions written into the passed string at that location, which were then executed when main attempted to return.
  • On VAXen. this meant the worm was connected to a remote shell via the transmission control protocol (TCP) connection, and the worm proceeded to infect the system.
  • On Sun, this resulted in a core file error because the code was in place to corrupt a Sun version of fingered.

Stack Buffer Overrun/Stack Smashing

  • The first variant of Win32/Blaster (also called the Win3/Msblast worm) appeared on August 11, 2003.
  • Microsoft released a security bulletin about the vulnerability exploited by this worm on July 16,2003.
  • This worm is notable because it was the first occurrence of a Windows command shell attack by a worm and because of the sheer volume of vulnerable machines despite the preexisting security bulletin.
  • It also attempted to cause a huge DDoS (distributed denial-of service) attack on the Windows update servers to prevent users from downloading the security patch.
  • This worm attacked vulnerable systems running Windows NT, Windows XP. Windows 2000, and Windows Server 2003.
  • Although the vulnerability existed in all these versions of Windows, the worm could only infect the systems running Windows 2000 and Windows XP.
  • This worm scanned IP addresses and tried to connect to port 135 with a TCP connection, then used a security vulnerability in the Windows Distributed Component Object Model (DCOM) Remote Procedure Call's (RPCs) CoGetlnstanceFromFile function.
  • This function has as unbounded input string called szName, which was designed to hold a 32-byte NetBIOS machine name.
  • The worm would then pass in an overly long crated file name, cause a stack overflow, and then bind a cmd.exe command shell to port 4444/tcp.
  • Newly created shell is given a command to cause it to download the worm file from the attacking host over port 69/udp using the Trivial File Transfer Protocol (tftp), which is supported by a default tftp client on most Windows systems.
  • Once the worm file (msblast.exe) is downloaded, the worm requests the remote system to execute the downloaded file. When the worm calls ExitProcess(), Windows XP systems will reboot and in Windows 2000 system there are a variety of side effects.
  • The worm added a key to the registry(HKLM \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Run \ windows auto update) to ensure that the worm is activated whenever Windows is started.
  • Now the worm waits for an active network connection to start searching for more systems to attack.
  • The worm also conducts a DDoS SYN-flooding attack against the Web site windowsupdate.com, which is an alias of the main Microsoft Windows update site, on a predetermined schedule.

Heap Buffer Overrun/Heap Smashing

  • The Apache/mod_ssl (also called Smasher) worm appeared in September 2002 and affected Linux systems running Apache with the OpenSSL module (mod_s-s1) on Intel Architectures.
  • This particular worm is notable, because it is the first widespread worm that utilized a heap buffer overrun or heap smashing attack.
  • When this worm would find a system running Apache, it would check to see if the server had the cipher the worm was interested again. If that checked out as well, the worm would begin the first of the two buffer overruns to carry out its attack.
  • The worm would hand the server a key argument longer than the 8-byte maximum size allowed. When the incoming data was parsed on receipt, the information was not bounds checked and was copied into an 8-byte fixed length buffer in a heap allocated SSL_SESSION.
  • Because the structure of the heap-allocated SSL_SESSION is known, the buffer was carefully overflowed with miscellaneous data until it reached the session_id length field in the SSL_SESSION buffer, and then overwrote the data in that field with a value of 0x70 (112).
  • Then the worm terminated the connection with a “client finished' message in order to have the server respond with its standard “server finished” message, which contains the session_id data. But, because the worm overrode the session_id_length with a much larger value, the server tells the worm the entire 112 bytes of the SSL_SESSION structure, starting at the session_id.
  • Now that the worm has this information, it reuses the same attack vector again but this time it sets up shell code to run when the “client finished” message is received by the server.

Test Techniques

  • Testing for buffer overruns means testing every variable to see if it is hounded or not. The basic stages of buffer oven -un testing are the same for both black-box and white-box testing.
  • Find and Document All Entry Points into the Product You Are Testing:
    • Be sure that all entries are examined, not just the UI (user interface) or application programming interfaces (APIs). It is common for software that was once written as a standalone program to have been include a public API, Web methods, or other ways of exposing the program's functionality to other uses.
    • As the entry points are documented, make special note of any UW that may later be used to calculate a buffer allocation size.
  • Create an Attack That Targets Each Variable at Each Entry Point:
    • If a passed variable is used to calculate the size of a buffer, some common values that should be attempted are as follows:
      • 0
      • 1
      • Maximum size of datatype 1
      • Maximum size of datatype
      • Minimum size of datatype
      • Minimum size of datatype + 1
  • For the strings, if the datatype is unknown, start with a string of 16,000 characters. If the input is supposed to conform to a particular format, each segment of the string should be tried with the test string separately and then in combination. For example, if you were submitting an e-mail address, you might try:
    • <test string>@email.com
    • <test string>.me@email.com
    • Me.<test string>@email.com
    • Me.me@<test string>.com
    • Me.me@email.<test string>
  • Pass the Attack Data to Each Entry Point:
    • Manual testing is usually possible but often time consuming and rather monotonous. This type of testing is a great place for test case automation.
  • Look for Any Crashes or Unexpected Behavior:
    • Carefully examine any error messages for clues that will indicate what k–happening behind the scenes. Creating separate tests for each variable at each entry point will be helpful in tracking down the particular variable that is unbounded or improperly bounded.
  • Black Box
    • Use a trial-and-error approach to discover the data details like format and length. If there is a specification, then that can be used to determine the expected data details, but the actual boundaries must be tested for.
    • Just because the specification says something does not mean it exists that way.
  • White Box
    • In white-box testing, all variables need to be traced back to where they enter the system, and followed through the system from there. Any time the variable is manipulated or has memory allocated for it, the code must be carefully examined for any problems.
    • Run the test under a debugger. This makes it easy to examine items such as what is on the heap or stack, or what is in the registers.
    • If a portion of the test string appears on the stack, you may have found a stack overrun.
    • If a portion of the appears on the heap, you may have a heap overrun.
    • If a portion of the test string appears in the process register, you may have a buffer overrun and it is exploitable.