10.4. Current and Previous Threats
This section describes a variety of blended attacks in more detail. These include the infamous Morris worm and CodeRed, which use a buffer overflow; threats such as W32/Badtrans and W32/Nimda, which use input validation exploits; and W32/Bolzano and VBS/Bubbleboy, which use application or user rights exploits.
10.4.1. The Morris Internet Worm, 1988 (Stack Overflow to Run Shellcode)
The Morris worm implemented a buffer overflow attack against the fingerd program. This program runs as a system background process and satisfies requests based on the finger protocol on the finger port (79 decimal). The problem in fingerd was related to its use of the gets() library function. The gets() function contained an exploitable vulnerability; a couple of other functions on BSD systems had a similar problem.
Because fingerd declared a 512-byte buffer for gets() calls without any bounds checking, it was possible to exploit this and send a larger string to fingerd. The Morris worm crafted a 536-byte "string" containing Assembly code (so-called shellcode) on the stack of the remote system to execute a new shell via a modified return address.
The 536-byte buffer was initialized with zeros, filled with data, and sent over to the machine to be attacked, followed by a \n to indicate the end of the string for gets(). This attack worked only against VAX (virtual address extension) systems that were designed and built between 1977 and the retirement of the system in 19992000. VAX is a 32-bit CISC architecture. The system was a successor to PDP-11. VAX uses 16 registers numbered from r0 to r15, but several of these registers are special and map to SP (stack pointer), AP (argument pointer), and so on.
The actual instructions responsible for the attack were on the stack, as shown in Listing 10.9, but inside the originally reserved buffer at position 400 decimal.
Listing 10.9. The Shellcode of the Morris Worm
VAX Opcode Assembly Comment DD8F2F736800 pushl $68732f ; '/sh\0' DD8F2F62696E pushl $6e69622f ; '/bin' D05E5A movl sp, r10 ; save pointer to command DD00 pushl $0 ; third parameter DD00 pushl $0 ; second parameter DD5A pushl r10 ; push address of '/bin/sh\0' DD03 pushl $3 ; number of arguments for chmk D05E5C movl sp, ap ; Argument Pointer register ; = stack pointer BC3B chmk $3b ; change-mode-to-kernel
This code is an execve("/bin/sh", 0, 0) system call8 to execute a shell.
Bytes 0 through 399 of the attack buffer were filled with the 01 opcode (NOP). An additional set of longwords was also changed beyond the original buffer size, which in turn smashed the stack with a new return address that the author hoped would point into the buffer and eventually hit the shellcode within it. When the attack worked, the new shell took over the process, and the worm could successfully send new commands to the system via the open network connection.
The worm modified the original return address of main() on the stack of fingerd. When main() (or any function) is called on a VAX machine with a calls or callg instruction, a call frame is generated on the stack. Because the first local variable of fingerd was the actual buffer in question, main's call frame was placed next to the buffer. Overflow of the buffer caused the call frame to be changed.
The Morris worm modified this call frame, rewriting six entries in it, and specified the return address in the position of the saved PC (program counter), which would hopefully point into its own crafted buffer. (The PC is the equivalent of the EIP on the Intel CPU.) The NOPs in the worm's attack buffer increased the chance that control would eventually arrive at the shell code. The worm's code specifies the call as a call instruction by setting the S bit of the Mask field. Figure 10.13 shows the call frame layout on a VAX.
Figure 10.13. The call frame layout on a VAX.
Eventually, a ret instruction would access the altered call frame (which is likely to be very similar to its original content except for the saved PC), pick up the new PC, and return to a location that hit the shellcode of the worm.
Shellcode is always crafted to be as short as possible. In the case of the Morris worm, it is 28 bytes. Shellcode often needs to fit in small buffers to exploit the maximum set of vulnerable applications. In the case of the finger daemon, the actual available buffer size was 512 bytes, but obviously some other applications might not have that much space for the shellcode.
10.4.2. Linux/ADM, 1998 ("Copycatting" the Morris Worm)
In 1998, around the tenth anniversary of the Morris worm, a group of hackers created a new Linux worm called Linux/ADM, which quickly spread in the wild. The worm utilized a buffer overflow technique to attack BIND (Berkeley Internet name domain) servers.
BIND is a service listening on the NAMESERVER_PORT (53 decimal). The worm attacked the server with a malformed IQUERY (inverse query) by specifying a long request body (packet) for the query. Certain BIND versions have had a couple of similar buffer overflow vulnerabilities in several places, but the bug in question was in the ns_req.c module. A function called req_iquery() is called to satisfy any incoming IQUERY request.
The packet size for the query is crafted to be long enough to hit a return address. Thus the function does not return but hopes to execute the attack buffer, which is filled with NOP instructions and shellcode to call execve on Intel-based Linux systems (Function=0x0b, INT 80h). Thus Linux/ADM's attack is very similar to that of the Morris worm. Linux/ADM also uses a shellcode-based attack; the important difference is that Linux/ADM recompiles itself entirely, whereas the Morris worm did not have more than a short boot code that was compiled to target platforms.
The Linux/ADM worm consists of several C files, as well as other script files, in a TAR file. It compiles these modules as "test," "Hnamed," "gimmeRAND," "scanco," and "remotecmd," respectively.
When the worm is executed, it looks for new hosts to infect at a random IP address generated using gimmeRAND and then by checking the vulnerable systems using scanco.
When a vulnerable system is detected, the module, Hnamed, is used with parameters passed to it. The parameters to Hnamed specify the machine to be attacked and the attack shell string, which is a piped /bin/sh command chain.
The worm can snatch its source from the attacker machine and restart the compilation process on the new host. Linux/ADM makes sure to install an additional remote command prompt.
This remote command prompt gets particularly interesting because a former white hat security person, Max Butler, created a counterattack in the form of a worm to install security patches on Linux systems to stop the Linux/ADM worm and similar attacks. Butler modified the worm to install patches, but it appears that he forgot to remove the remote prompt that opened the systems to outside attacks in the first place.
Max Butler was sentenced to 18 months in prison for launching the worm that crawled through hundreds of military and defense contractor computers over a few days in 1998 as reported by Security Focus.
10.4.3. The CodeRed Outbreak, 2001 (The Code Injection Attack)
The CodeRed worm was released to the wild in July 2001. The worm replicated to thousands of systems in a matter of a few hours. It has been estimated that well over 300,000 machines were infected by the worm within 24 hours. All of these machines were Windows 2000 systems running vulnerable versions of Microsoft IIS.
Interestingly, the worm did not need to create a file on the remote system to infect it, but existed only in memory of the target system. The worm accomplished this by getting into the process context of the Microsoft IIS with an extremely well-crafted attack via port 80 (Web service) of the target system.
The IIS Web server receives GET /default.ida?, followed by 224 characters, URL encoding for 22 Unicode characters (44 bytes), an invalid Unicode encoding of %u00=a, HTTP 1.0, headers, and a request body.
For the initial CodeRed worm, the 224 characters are N, but there were other implementations that used other filler bytes, such as X. In all cases, the URL-encoded characters are the same (they look like %uXXXX, where X is a hex digit). The request body is different for each of the known variants.
IIS keeps the body of the request in a heap buffer. Note that a GET request is not allowed to have a request body, but IIS dutifully reads it anyway, according to the header's instructions.
10.4.3.1 Buffer Overflow Details
While processing the 224 characters in the GET request, functions in IDQ.DLL overwrite the stack at least twice (see Figure 10.14): once when expanding all characters to Unicode and again when decoding the URL-escaped characters. However, the overwrite that results in the transfer of control to the worm body happens when IDQ.DLL calls DecodeURLEscapes() in QUERY.DLL20.
Figure 10.14. Stack, heap, and frame layout of a CodeRed attack.
The caller is supposed to specify a length in wide chars but instead specifies a number of bytes. As a result, DecodeURLEscapes() thinks it has twice as much room as it actually has, so it overwrites the stack. Some of the decoded Unicode characters specified in URL encoding end up overwriting a stack frame-based exception block. Even after the stack has been overwritten, processing continues until a routine is called in MSVCRT.DLL (the C runtime library). This routine notices that something is wrong and throws an exception.
Exceptions are thrown by calling the KERNEL32.DLL routine RaiseException(). RaiseException() transfers control to KiUserExceptionDispatcher() in NTDLL.DLL. When KiUserExceptionDispatcher() is invoked, EBX points to the exception frame that was overwritten.
The exception frame is composed of four DWORDs (32-bit each), the second of which is the address of the exception handler for the represented frame. The URL encoding whose expansion overwrote this frame starts with the third occurrence of %u9090 in the URL encoding, and is
This decodes as the four DWORDs: 0x68589090, 0x7801CBD3, 0x90909090, and 0x00C38190.
The address of the exception handler is set to 0x7801CBD3 (second DWORD), and KiUserExceptionDispatcher() calls there, with EBX pointing at the first DWORD via CALL ECX.
Address 0x7801CBD3 in IIS's address space is within the memory image for the C runtime DLL, MSVCRT.DLL, which is loaded to a fixed address. At this address in MSVCRT.DLL is the instruction CALL EBX. When KiUserExceptionDispatcher() invokes the exception handler, it calls to the CALL EBX, which in turn transfers control to the first byte of the overwritten exception block. When interpreted as code, these instructions find and then transfer control to the main worm code, which is in a request buffer in the heap.
The author of this exploit needed the decoded Unicode bytes to function both as the frame-based exception block containing a pointer to the "exception handler" at 0x7801CBD3, and as runable code. The first DWORD of the exception block is filled with four bytes of instructions arranged so that they are harmless, but that also place the 0x7801CBD3 at the second DWORD boundary of the exception block. The first two DWORDs (0x68589090, 0x7801CBD3) disassemble into the instructions nop, nop, pop eax, push 7801CBD3h, which accomplish this task easily.
Having gained execution control on the stack (and avoiding a crash while running the "exception block"), the code finds and executes the main worm code.
This code knows that there is a pointer (call it pHeapInfo) on the stack 0x300 bytes from EBX's current value. At pHeapInfo+0x78, there is a pointer (call it pRequestBuff) to a heap buffer containing the GET request's body, which contains the main worm code. With these two key pieces of information, the code transfers control to the worm body in the heap buffer. The worm code does its work but never returnsthe thread has been hijacked, along with the request buffer owned by the thread.
10.4.3.2 Exception Frame Vulnerability
This technique of usurping exception handling is complicated, and crafting it must have been difficult. The brief period between the eEye description of the original exploit and the appearance of the first CodeRed worm suggested that this technique was already generic at the time when the attacker used it in CodeRed. Certainly, the exception handling technique had been known to a few buffer overflow enthusiasts for some time, and this particular overflow was a perfect opportunity to use it.
Having exception frames on the stack makes them extremely vulnerable to overflows. In addition, the exception handling dispatcher of certain Windows versions does not make a perfect job in validating exception handlers. The combination leads to a critical vulnerability in certain Windows systems. In other words, the CodeRed worm relied on the Windows exception frame vulnerability to carry out a quick buffer overflow attack against IIS systems. Although Microsoft introduced vector-based exception handling in XP, vectored-based exception handling helps attackers to carry out heap overflows more easily21.
10.4.4. Linux/Slapper Worm, 2002 (A Heap Overflow Example)
On July 30, 2002, a security advisory from A.L. Digital Ltd. and The Bunker disclosed four critical vulnerabilities in the OpenSSL package. OpenSSL is a free implementation of the secure socket layer (SSL) protocol used to secure network communications. It provides cryptographic primitives to many popular software packages, among them the Apache Web server. Less than two months later, the Linux/Slapper worm successfully exploited one of the buffer overflows described in the advisory and, in a matter of days, spread to thousands of machines around the world.
So far, Linux/Slapper22 has been one of the most significant outbreaks on Linux systems. Slapper shows many similarities to the BSD/Scalper worm, hence the name. The worm skips some of the local networks, such as 10.*.*.*, which somewhat limits its spread on local networks.
10.4.4.1 Under Attack
Linux/Slapper spreads to Linux machines by exploiting the overlong SSL2 key argument buffer overflow in the libssl library that is used by the mod_ssl module of Apache 1.3 Web servers. When attacking a machine, the worm attempts to fingerprint the system by first sending an invalid GET request to the http port (port 80), expecting Apache to return its version number and the Linux distribution on which it was compiled, along with an error status.
The worm contains a hard-coded list of 23 architectures on which it was tested and compares the returned version number with the list. Later, it uses this version information to tune the attack parameters. If Apache is configured not to return its version number or if the version is unknown to the worm, it will select a default architecture (Apache 1.3.23 on Red Hat) and the "magic" value associated with it.
This "magic" value is very important for the worm. It turns out to be the address of the GOT (global offset table) entry of the free() library function. GOT entries of ELF files are the equivalent of IAT (import address table) entries of PE files on Windows systems. They hold the addresses of the library functions to call. The address of each function is placed into the GOT entries when the system loader maps the image for execution. Slapper wants to hijack the free() library function calls to run its own shellcode on the remote machine.
10.4.4.2 The Buffer Overflow
In the past, some worms exploited stack-based buffer overflows. Stack-based overflows are the low-hanging fruits compared to second-generation overflows that exploit heap structures. Because the OpenSSL vulnerability affected a heap allocated structure, the worm's author had to deal with many minor details to get the attack right for most systems. Exploiting the vulnerability was not trivial and required someone with a lot of time and experience.
When Apache is compiled and configured to use SSL, it listens on port https (port 443). Slapper opens a connection to this port and initiates an SSLv2 handshake. It sends a client "hello" message advertising eight different ciphers (although the worm only supports one, RC4 128-bit with MD5) and gets the server's certificate in reply. Then it sends the client master key and the key argument, specifying a key argument length greater than the maximum allowed SSL_MAX_KEY_ARG_LENGTH (eight bytes).
When the packet data is parsed in the get_client_master_key() function of libssl on the server, the code does no boundary check on the key argument length and copies it to a fixed-length buffer key_arg of size SSL_MAX_KEY_ARG_LENGTH, in a heap-allocated SSL_SESSION structure. Thus anything following key_arg can be overwritten with arbitrary bytes. This includes both the elements after key_arg in the SSL_SESSION structure and the heap management data following the memory block containing the structure.
Manipulation of the elements in the SSL_SESSION structure is crucial to the success of the buffer overflow. The author of the exploit took great care to overwrite these fields in a way that does not affect the SSL handshake too much.
Interestingly, instead of using this overflow mechanism just once, the worm uses it twice: first to locate the heap in the Apache process address space and then to inject its attack buffer and shellcode. There are two good reasons for splitting the exploit into two phases.
The first reason is that the attack buffer must contain the absolute address of the shellcode, which is hardly predictable across all servers because it is placed in memory dynamically allocated on the heap. To overcome this problem, the worm causes the server to leak the address where the shellcode will end up and then sends an attack buffer patched accordingly.
The second reason is that the exploit necessitates overwriting the cipher field of the SSL_SESSION structure located after the unchecked key_arg buffer. This field identifies the cipher to use during the secure communication; if its value were lost, the session would come to an end too quickly. So the worm collects the value of this field during the first phase and then injects it back at the right location in the SSL_SESSION structure during the second phase.
This two-phased approach requires two separate connections to the server and succeeds only because Apache 1.3 is a process-based server (as opposed to a thread-based server). The children spawned by Apache to handle the two successive connections will inherit the same heap layout from their parent process. All other things being equal, the structures allocated on the heap will end up at the same addresses during both connections.
This assumes that two fresh "identical twin" processes are spawned by Apache to handle the two connections, but under normal conditions this might not always be the case. Apache maintains a pool of servers already running, waiting for requests to handle. To force Apache to create two fresh processes, the worm exhausts Apache's pool of servers before attacking by opening a succession of 20 connections at 100-millisecond intervals.
10.4.4.4 Getting the Heap Address
The first use of the buffer overflow by the worm causes OpenSSL to reveal the location of the heap. It does this by overflowing the key_arg buffer by 56 bytes, up to the session_id_length field in the SSL_SESSION structure. The session_id_length describes the length of the 32-byte-long session_id buffer located after it in the SSL_SESSION structure. The worm overwrites the session_id_length with the value 0x70 (112). Then the SSL conversation continues normally until the worm sends a "client finished" message to the server, indicating that it wants to terminate the connection.
Upon reception of the "client finished" message, the server replies with a "server finished" message including the session_id data. Once again, no boundary check is performed on the session_id_length, and the server sends not only the content of the session_id buffer, but the whole 112 bytes of the SSL_SESSION structure as well, starting at session_id. Among other things, this includes a field called ciphers that points to the structure allocated on the heap right after the SSL_SESSION structure where the shellcode will go, along with the cipher field that identifies the encryption method to use.
The worm extracts the two heap addresses from the session_id data received from the server and places them in its attack buffer. The TCP port of the attacker's end of the connection is also patched into the attack buffer for the shellcode to use later. The worm then performs the second SSL handshake and triggers the buffer overflow again.
10.4.4.5 Abusing the glibc
The second use of the buffer overflow is much more subtle than the first. It can be seen as three steps leading to the execution of the shellcode:
The attack buffer used in the second overflow is composed of three parts:
When the buffer overflow happens, all members of the SSL_SESSION structure after the key_arg buffer are overwritten. The numeric fields are filled with A bytes, and the pointer fields are set to NULL, except the cipher field, which is restored to the same value that was leaked in the first phase.
The 24 bytes of memory following the SSL_SESSION structure are overwritten with false heap management data. The glibc allocation routines maintain so-called boundary tags between memory blocks for management purposes. Each tag consists of the sizes of the memory blocks before and after it, plus one bit indicating whether the block before it is in use or available (the PREV_IN_USE bit). Additionally, free blocks are kept in doubly linked lists formed by forward and backward pointers maintained in the free blocks themselves.
The false heap management data injected by the worm after the SSL_SESSION structure poses as a minimal-sized unallocated block, just containing the forward and backward pointers set respectively to the address of the GOT entry of free() minus 12 and the address of the shellcode. The address of the GOT entry is the "magic" value determined by fingerprinting, and the address of the shellcode is the value of the ciphers field leaked by OpenSSL in the first phase of the attack, plus 16 to account for the size of the false block content and trailing boundary tag.
After the preceding conditions are set up on the server, the worm sends a "client finished" message specifying a bogus connection ID. This causes the server to abort the session and attempt to free the memory associated with it. The SSL_SESSION_free() function of the OpenSSL library is invoked; this in turn calls the glibc free() function with a pointer to the modified SSL_SESSION structure as an argument.
One might think that freeing memory is a rather simple task. In fact, considerable bookkeeping is performed by free() when a memory block is released. Among other tasks, free() takes care of consolidating blocks, that is, merges contiguous free blocks into one to avoid fragmentation. The consolidation operation uses the forward and backward pointers to manipulate the linked lists of free blocks and trusts these to be pointing to heap memory (at least in the release build).
The exploit takes advantage of the forward consolidation of the SSL_SESSION memory block with the false block created after it by setting the PREV_IN_USE bits of the boundary tags appropriately. The forward pointer in the false block that points to the GOT is treated as a pointer to a block header and dereferenced, and the value of the backward pointer (the shellcode address) is written to offset 12 of the header. Thus the shellcode address ends up in the GOT entry of free().
It is worth noting that the false backward pointer is also dereferenced, so the beginning of the shellcode is also treated as a block header and patched at offset 8 with the value of the false forward pointer. To avoid corruption of the shellcode during this operation, it will start with a short jump followed by 10 unused bytes filled with NOPs. Thus no shell-code instructions are corrupted during the consolidation.
Finally, on the next call to free() by the server, the modified address in the GOT entry of free() is used, and the control flow is directed to the shellcode.
10.4.4.6 Shellcode and Infection
When the shellcode is executed, it first searches for the socket of the TCP connection with the attacking machine. It does this by cycling through all file descriptors and issuing a getpeername() call on each until the call succeeds and indicates that the peer TCP port is the one that was patched into the shellcode. Then it duplicates the socket descriptor to the standard input, output, and error.
Next, it attempts to gain root privilege by calling setresuid() with UIDs all set to zero. Apache usually starts running as root and then switches to the identity of an unprivileged user "apache" using the setuid() function. Thus the setresuid() call will fail because setuid() is irreversible, unlike the seteuid() function. See Chapter 15, "Malicious Code Analysis Techniques," for a systrace of a Slapper shell-code attack that clearly shows that setresuid() function fails and returns 1 to the caller. The systrace in Chapter 15 also can help to explain the free() call sequence of the attack.
It seems the author of the shellcode overlooked this fact, but the worm does not need root privileges to spread, anyway, because it only writes to the /tmp folder.
Finally, a standard shell /bin/sh is executed with an execve() system call. A few shell commands are issued by the attacker worm to upload itself to the server in uuencoded form, and to decode, compile, and execute itself. The recompilation of the source on various platforms makes the identification of the worm in binary form a bit more difficult. The operations are done in the /tmp folder, where the worm files reside under the names .uubugtraq, .bugtraq.c, and .bugtraq (notice the leading dots to hide the files from a simple ls command).
10.4.4.7 Now You See Me, Now You Don't!
Because the worm hijacks an SSL connection to send itself, it is legitimate to wonder if it travels on the network in encrypted form. This question is particularly crucial for authors of IDS systems that rely on detecting signatures in raw packets. Fortunately, the buffer overflow occurs early enough in the SSL handshake before the socket is used in encrypted mode, so the attack buffer and the shellcode are clear on the wire. Later, the same socket is used to transmit the shell commands, and the uuencoded worm also, in plain text. The "server verify," "client finished," and "server finished" packets are the only encrypted traffic, but they are not particularly relevant for detection purposes.
10.4.4.8 P2P Attack Network
When an instance of the worm is executed on a new machine, it binds to port 2002/UDP and becomes part of a peer-to-peer network. Notice that although a vulnerable machine can be hit multiple times and exploited again, the binding to port 2002 prevents multiple copies of the worm from running simultaneously.
The parent of the worm (on the attacking machine) sends to its offspring the list of all hosts on the P2P network and broadcasts the address of the new instance of the worm to the network. Periodic updates to the hosts list are exchanged among the machines on the network. The new instance of the worm also starts scanning the network for other vulnerable machines, sweeping randomly chosen class Bsized networks.
The protocol used in the peer-to-peer network is built on top of UDP and provides reliability through the use of checksums, sequence numbers, and acknowledgment packets. The code has been taken from an earlier tool, and each instance of the worm acts as a DDoS agent and a backdoor.
10.4.4.9 Conclusions on the Linux/Slapper Attack
Linux/Slapper is an interesting patchwork of a DDoS agent, some functions taken straight from the OpenSSL source code, and a shellcode the author says is not its own. All this glued together results in a fair amount of code not easy to figure out rapidly. Like BSD/Scalper, most of the worm was probably already written when the exploit became available. For the author, it was just a matter of integrating the exploit as an independent component.
As in Scalper, which exploited the BSD memcpy() implementation, the target of the exploit is not just an application but a combination of an application and the run-time library underneath it. One would expect memcpy() and free() to behave in a certain way, consistent with one's everyday programming experience. But when used in an unusual state or passed invalid parameters, they behave erratically.
Linux/Slapper shows that Linux machines, just as easily as Windows machines, can become the target of widespread worms. For those with Slapper-infected Linux servers, it is going to be a day to remember.
10.4.5. W32/Slammer Worm, January 2003 (The Mini Worm)
The Slammer worm23 targets vulnerable versions of Microsoft SQL Server 2000 products, as well as MSDE 2000 (Microsoft SQL Server 2000 Desktop Engine) and related packages. Due to the integration of MSDE in many client software packages, such as Microsoft Visio 2000 Enterprise Edition, not only server systems, but many workstation systems also became vulnerable to Slammer as well. Unfortunately, many users believed that their workstations were safe from Slammer and got infected by Slammer repeatedly.
The worm caused serious outbreaks all around the world. The attack started on January 25, 2003. According to early reports, the worm had a significant population around the world in about 15 minutes. During the worm's initial outbreak, Internet users experienced a large percentage of packet drops that developed into a largescale DoS attack.
The worm attacks by exploiting a stack-based overflow that occurs in a DLL implementing the SQL Server Resolution Service. This DLL (ssnetlib.dll) is used by the SQL server service process called SQLSERVR.EXE. This vulnerability was reported to Microsoft by David Litchfield (NGSSoftware), along with a few others. Actual exploit code was made available on the BlackHat conference. It is clear that this code was used as a base to develop the worm.
10.4.5.1 Exploit Setup
The SQL server process listens on TCP and UDP ports. The worm targets UDP port 1434, sending a special request (0x04) specified as the first character of the payload. In the datagram, this is followed by a specially crafted "string" that contains the worm code. The worm code is extremely small. It is only 376 bytes, which makes it the shortest binary worm known today. (376 bytes is the length of the UDP datagram without the protocol headers.)
Because the worm can use a UDP datagram for the attack, the source IP address of the original attacker was probably spoofed. The worm spreads to randomly generated IP addresses. As a result, it is very difficult to figure out from which country the attack originated.
10.4.5.2 Problems in ssnetlib.dll (in SQL Server 2000)
The function is supposed to build a string for Registry access by concatenating three strings into a 128-byte buffer. This string will be built on the stack, and there are no input validations for the size of the middle string parameter. String 1 and string 3 are constant and located in the ssnetlib.dll.
Whenever a too-long string is passed to the function, the stack is corrupted (smashed). String 2 is a SQL Server instance name. According to the Microsoft Knowledge Base, this string should be at most 16 characters long. However, this is not enforced in the server and not even in some of the common clients.
The worm is specially crafted. Its code is not only compact, it does not have any zeros in it. This is because the buffer is used in a string parameter to an sprintf() library function call.
As a result of the overflow, a concatenated string is built on the stack, where string 2 is the worm body itself.
10.4.5.3 Getting Control
Because the worm cannot contain zeros, the author of the worm uses a lot of 01 filler bytes. Furthermore, all the attempts are made to use addresses that do not contain any zeros, and in some cases the code uses XOR to mask zero bytes, which is a known shell-code technique.
The worm starts with a header posing as local variables of the buggy function. A new return address (0x42B0C9DC) follows these filler bytes. This address is a pointer to a JMP ESP instruction inside SQLSORT.DLL, another module of the SQL Server process.
To make sure that the vulnerable function gives control to the worm body, the header section of the worm also uses dummy values ("crash test dummies," 0x42AE7001) to replace function arguments on the stack. It is necessary to do so because these arguments are used after the call to sprintf(), triggering the overflow. Failure to replace these arguments would cause an exception, and the function would not return normally.
When the function returns, control flows to the JMP ESP instruction, which jumps on the stack to the location right after the hijacked return address. The first instruction will be a short jump around false function arguments to the main worm code.
Because the worm header section contains local variables, these could change during the time between the actual faulty sprintf() and the function return to the worm body. This could corrupt the worm's header. Thus the worm will rebuild this area first to make sure that its header section remains constant for the next attack. Because the query type field (0x04) is missing from the top of the worm on the stack, it is also rebuilt by pushing a 0x04000000 DWORD whose high byte is later referenced by the replication code.
Now the worm only needs a few functions to call. Following the original exploit code, the worm's author uses the import address directory of SQLSORT.DLL to make calls to LoadLibraryA() and GetProcAddress() function calls. This routine is compatible with different service pack releases and patches of SQL Server. Therefore, the code of GetProcAddress() is checked first to be sure that it is the proper function entry point.
The worm gets access to the handlers (base addresses) of WS2_32.DLL and KERNEL32.DLL. Next it gets the addresses of socket(), sendto(), and GetTickCount() APIs, all of which it needs to replicate.
The replication is extremely simple. The worm sends 376 bytes to UDP port 1434 to a randomly generated IP address in an endless loop. This causes the server CPU usage to go up; thousands of packets will be sent, effectively causing a DoS attack and at the same time compromising a large number of new systems around the world.
The random number used to generate IP addresses is a variant of the Microsoft Basic random number generator. It uses the same multiplier. This results in an effective-enough randomness in the distribution of targeted systems.
10.4.5.6 Conclusions on the Slammer Worm Attack
It is interesting to note that Microsoft had a patch available for six months to cover not only this vulnerability but others related to it (Microsoft Security Bulletin MS02-039 and Security Bulletin MS02-061). Patches would effectively block the attack if applied properly, but they are often too costly for large corporations to deploy. Also the patching process was not particularly easy, due to the large number of Microsoft and third-party products, including SQL Server as a component. In addition, many users simply did not realize that they run a vulnerable SQL Server as part of client software such as Visio, and failed to patch their vulnerable systems.
Although SQL Server offers various user rights for the installation of the server process, such server processes often enjoy system context or admin privileges. This provides attackers with efficient access to any resources on the system because the hijacked thread runs with significant privileges to do further damage on the system.
According to estimations, Slammer infected at least 75,000 hosts. As Slammer began spreading, it doubled in size every 8.5 seconds24.
10.4.6. Blaster Worm, August 2003 (Shellcode-Based Attack on Win32)
On August 11, 2003the same day it was completeda UPX-compressed bug, 6,176 bytes long, started to invade the world using a vulnerability described in Microsoft's Security Bulletin MS03-2625. This particular vulnerability affected even Windows Server 2003. Unfortunately, the RPC/DCOM vulnerability could be exploited by non-authenticated client connections. Patches were made available by Microsoft, but this time there was much less delay between the announcement of the vulnerability and the worm that exploited it.
10.4.6.1 All Systems Go
The first thing W32/Blaster26 does when it runs on a system is to create a value "windows auto update" in the HKLM/.../Run Registry key, pointing to the bare file-name msblast.exe (for variant .A). This relies on the assumption that the executable ends up in a directory that Windows searches by default, which is usually the case. Then the worm attempts to create a mutex named BILLY and aborts if it exists already, to avoid multiple instances of the worm running simultaneously.
W32/Blaster then waits for an active network connection and starts searching for machines to infect.
10.4.6.2 SP4, SP3, SP2, SP1, Ignition!
The target selection in Blaster is somewhat different from the one in CodeRed and Slammer. Sixty percent of the time, Blaster goes after entirely random IP addresses, and 40% of the time it attacks machines on the same class Bsized network as the host, hoping to take over pools of vulnerable systems on the local area network. The scanning for targets is linear (the target address is increased monotonically until it reaches the end of the IP space) and, in the case of a local attack, starts at or slightly below the class C of the host.
The worm targets machines running Windows 2000 and Windows XP, intentionally favoring the exploitation of Windows XP machines (probably because the payload relies on the increased availability of raw sockets therethe requirement to be Administrator was removed). Eighty percent of the time, the exploit is tuned for Windows XP systems, and 20% for Windows 2000 systems. This choice is made only once whenever the worm initializes. All unpatched service packs of both systems are affected, but because of this random tuning, sometimes the worm will just cause a denial of service on the attacked machines, crashing the RPC service.
10.4.6.3 Second Stage: The Shell
The infection of a new machine is a three-phase process, involving quite a lot of network activity compared to the single-connection CodeRed and the lightweight Slammer. First, the worm sends its attack buffer over port 135/tcp, which exploits the RPC DCOM vulnerability and causes the remote machine to bind a shell in the SYSTEM context (CMD.EXE) to port 4444/tcp. Second, the worm sends a command to the newly created shell to request a download of the worm file from the attacking host to the victim. The transfer is done over port 69/udp using the TFTP protocol: The worm implements its own crude TFTP server, which formats sent data according to RFC 1350 and uses the TFTP client that is present by default on most Windows systems. Finally, once msblast.exe has been downloaded successfully, or after 21 seconds, the worm requests that the remote system execute the downloaded file.
10.4.6.4 "We Have a Problem"
After the shell exits, the hijacked RPC service thread running the shellcode calls ExitProcess(), causing the service to terminate. The termination of the RPC service, for whatever reason, triggers a reboot in Windows XP systems after one minute. On Windows 2000 systems, the termination results in a variety of unusual side effects, among the most critical of which is the inability to use the Windows Update Web service.
10.4.6.5 Pan Galactic Gargle Blaster
As is common for fast-spreading worms, W32/Blaster reuses an exploit code that was posted previously to various security mailing lists. The exploit uses two so-called universal offsets as return addresses in a classic stack buffer overflow, each of which is compatible with multiple service packs of one Windows version. The vulnerability is located in the code of the rpcss.dll file, in a function related to the activation of DCOM objects. The buggy function extracts a NetBIOS server name from a UNC path specified by a DCOM client and attempts to place it into a 32-byte buffer on the stack without bounds checking.
When the stack is smashed, the hijacked return address leads to a call ebx instruction (in a "well-known" constant data tablethe memory mapped Unicode.nls file) that then jumps back to a nop ramp in the shellcode. This is possible because the ebx register is pointing to a local variable in an earlier stack frame (that is, at a higher memory address) created by the fourth-level (!) caller of the buggy function. See Figure 10.15 for an illustration.
Figure 10.15. The memory layout and control flow during a Blaster attack.
The shellcode retrieves some useful API addresses, binds to port 4444/tcp, accepts one incoming connection, spawns the shell and ties its input to the port 4444 socket, waits for the shell process to finish, and then exits.
W32/Blaster implements a SYN-flooding distributed denial of service attack against the Web site windowsupdate.com, an alias of the Windows Update site. The attack is carried out if the day of the month is greater than 15 or if the month is greater than 8 (that is, every day from August 16 to the end of the year, and then starting again on January 16 to January 31, February 16 to February 29, and so on).
Blaster worm has been quickly counter-attacked by W32/Welchia25. However, Welchia worm often failed to deliver the patches correctly, and thus it was considered harmful in its "worm war" attempts.
10.4.7. Generic Buffer Overflow Usage in Computer Viruses
Based on the previous examples, it is easy to see that computer worms typically attack service processes and daemon programs that are waiting to handle incoming requests by listening on various TCP/UDP ports. Any such communication service could potentially contain flaws, as did the fingerd (in the case of the Morris worm), the BIND (in the case of the ADM worm27), and Microsoft IIS (in the case of the CodeRed worm).
It should be noted that best practices prescribe the removal of all unneeded services from the system. Any such services should be removed to make the execution environment safer and to reduce the opportunities for malicious hackers and virus attacks to break into internal networks. In the case of CodeRed, for example, the vulnerable IDQ.DLL was only loaded to support the rarely used indexing service. Had this service been disabled on systems that didn't need it (which was probably most systems), CodeRed would not have been so successful.
10.4.8. Description of W32/Badtrans.B@mm
10.4.8.1 MIME Exploit
The worm used a MIME header exploit, which allows the worm to execute when previewing or reading an infected e-mail message. Affected are Microsoft Outlook (Express) and potentially any mail client that utilizes Internet Explorer for rendering of HTML mail. Users did not have to detach or execute the attachment in order to become infectedsimply reading or previewing the message caused the attachment to be executed.
The MIME exploit utilized is described in Section 10.3.4.5, "MIME Header Parsing."
The MIME header vulnerability was corrected by Microsoft in Security Bulletin MS01-20 in March 2001, yet the worm was still effective eight months later in November. If a vulnerable system were patched, the risk would have been significantly lower.
Unfortunately, many machines today are still vulnerable to this exploit. All variants of W32/Klez (first discovered in October 2001) use the same exploit. W32/Klez topped the virus infection charts in May 2002more than a year after a patch became available.
10.4.9. Exploits in W32/Nimda.A@mm
W32/Nimda.A@mm is a worm that utilizes multiple methods to spread itself. The worm sends itself by e-mail, searches for open network shares, and attempts to copy itself to unpatched or already vulnerable Microsoft IIS Web servers. Nimda also infects local files and files on remote network shares.
The worm uses the Web Server Folder Traversal exploit to infect IIS Web servers and a MIME header exploit that allows it to be executed just by someone reading or previewing an infected e-mail.
W32/Nimda.A@mm began infecting around 12:00 GMT on September 18, 2001. Within the first 12 hours, W32/Nimda.A@mm infected at least 450,000 unique hosts, with 160,000 hosts simultaneously infected at the peak rate of infection. The ability to infect IIS Web servers via an input validation exploit and autoexecute upon users' reading or previewing e-mail played a large role in W32/Nimda.A@mm's ability to spread so rapidly.
10.4.9.1 IIS Exploit Explained
W32/Nimda.A@mm exploits the Microsoft Internet Information Server MS01-026 vulnerability or previously compromised installations to copy itself to the server via TFTP. It then executes itself remotely.
W32/Nimda.A@mm probes the server by issuing a dir command using an exposed CMD.EXE from a previously compromised machine or winnt\system32\cmd.exe by using multiple variations of a URL canonicalization and encoding vulnerability, as described earlier.
If the server responds with a "200" success message, W32/Nimda.A@mm records that the server is vulnerable and begins its uploading routine. If the server does not respond with a success message, another malformed URL is tried. In total, W32/Nimda.A@mm attempts 13 different URL-encoding attacks and four specific URLs for servers that have been previously compromised.
The worm uploads itself by executing tftp.exe on the remote server, again using a malformed URL to break out of the Web root. The worm uses the TFTP client to connect back to the infecting server, downloading a copy of itself as the filename, admin.dll.
When copied to the victim Web server, W32/Nimda.A@mm executes itself by simply performing an HTTP GET request of itself on the remote Web server (that is, GET /directory/<exploit string>/<filename>.dll).
10.4.10. Description of W32/Bolzano
W32/Bolzano is a direct-action appending virus that infects PE files. Although the virus replication routine is simple, the modification of the Windows NT kernel to turn off user rights verification was novel.
10.4.10.1 OS Kernel Modification
W32/Bolzano and W32/Funlove need administrative rights on a Windows NT server or Windows NT workstation during the initial infiltration. OS kernel modification is not a major security risk, but it is still a potential threat because many users run their systems as Administrator. Furthermore, viruses can always wait until the Administrator or someone with equivalent rights logs on.
In such a case, W32/Bolzano has the chance to patch ntoskrnl.exe, the Windows NT kernel, located in the WINNT\SYSTEM32 directory. The virus modifies only two bytes in a kernel API called SeAccessCheck(), which is part of ntoskrnl.exe. In this way, W32/Bolzano can give full access to all users for every file, regardless of its protection, whenever the machine is booted with the modified kernel. This means that a Guesthaving the lowest possible rights on the systemis able to read and modify all files, including files that are normally accessible only by the Administrator.
This is a potential problem because the virus can spread everywhere it wants to, regardless of the actual access restrictions on the particular machine. Furthermore, after the attack no data can be considered protected from any user. This happens because the modified SeAccessCheck() API is forced always to return 1, instead of 0 or 1. A value of 1 means that the particular user has the necessary rights to access a particular file or directory placed on an NTFS partition, whereas 0 means the user has no access. SeAccessCheck() is called each time when the file access rights should be checked.
Unfortunately, the consistency of ntoskrnl.exe is checked in only one place. The loader, ntldr, is supposed to check ntoskrnl.exe when loading it into physical memory during machine boot-up. If the kernel gets corrupted, ntldr is supposed to stop loading ntoskrnl.exe and display an error message even before a "blue screen" appears. To avoid this particular problem, W32/Bolzano also patches the ntldr so that no error message will be displayed and Windows NT will boot just fine, even if its checksum does not match the original.
Because no code checks the consistency of ntldr itself, the patched kernel will be loaded without notification to the user. Because ntldr is a hidden, system, read-only file, Bolzano changes its attributes to "archive" before trying to patch it.
The modification of ntoskrnl.exe and other system executables was somewhat resolved by the System File Checker feature of Windows 2000/XP systems. Unfortunately, malicious code can easily disable this feature. Furthermore, the primary function of SFC is not virus protection, but a solution to the so-called "DLL hell" problem.
On certain systems, such as Linux, the kernel source files are commonly directly available on the systems, even on those not used for development. Although not installed by default, sources are commonly used to install new drivers by recompiling their source, which requires the sources to be in place. In addition, several people keep up to date with the recent kernel fixes by recompiling the source as bug fixes are made available. Therefore, any virus or other malware can easily alter the source of the system to gain root privileges once the kernel is recompiled, assuming that the virus has write access to the source at some point.
10.4.11. Description of VBS/Bubbleboy
VBS/BubbleBoy is a Visual Basic Script worm that works under English and Spanish versions of Windows 98 and Windows 2000. The worm e-mails itself to every e-mail address in the Microsoft Outlook address book. The worm utilizes an ActiveX control safe-for-scripting exploit to execute itself without the user detaching or executing the attachment. This exploit was first used by JS/Kak28.
10.4.11.1 ActiveX Safe-for-Scripting Exploit
VBS/Bubbleboy uses the Scriptlet.Typelib control to create a malicious HTA (HTML application) file in the Windows Startup directory. Thus the next time the computer is restarted, the HTA file executes the replication routine. By exploiting a safe-for-scripting vulnerability, the file is created when one simply reads or previews e-mail using a vulnerable version of Internet Explorer 5.
Scriptlet.Typelib allows one to generate a type library dynamically for a Windows Script Component. Such a type library would normally contain information about its interfaces and members. Type libraries are useful for statement completion in Visual Basic and also for displaying exposed properties and methods in the Object Browser.
Thus Scriptlet.Typelib provides a Path property, specifying where the type library will be written, and a Doc property, which is normally used for a string with the type library information. Finally, the Write method creates the type library file.
Unfortunately, the Scriptlet.Typelib control was marked safe for scripting. This allowed remote scripts (script files in the Internet zone, such as HTML e-mail or remote Web pages) to utilize the methods and properties of Scriptlet.Typelib and thus write out a file, which normally would be a .tlb type library file, to the local system with arbitrary content defined by the Doc property. Listing 10.11 demonstrates how to use Scriptlet.Typelib to write out a file.
Listing 10.11. Using Scriptlet.Typelib to Write into a File
Set oTL = CreateObject("Scriptlet.Typelib") oTL.Path="C:\file.txt" ' .tlb path/filename oTL.Doc="Hello World!" ' .tlb file contents oTL.Write ' write out file to local disk
10.4.12. Description of W32/Blebla
This worm uses variable subject lines and has two attachments named Myjuliet.chm and Myromeo.exe. After a message is read or previewed in vulnerable installations of Microsoft Outlook (Express), the two attachments are automatically saved and launched. When launched, this worm attempts to send itself out to all names in the Microsoft Outlook address book, using one of several Internet mail servers located in Poland.
10.4.12.1 ActiveX and Cache Bypass Exploit Explained
W32/Blebla uses a combination of an ActiveX control, which is marked safe for scripting, and another vulnerability that allows saving files locally to known locations.
The worm uses the showHelp method of the HHCtrl (HTML Help) ActiveX control. This method allows one to displayand thus executeCHM (compiled HTML) files via scripting. Originally, showHelp contained a vulnerability that allowed one to provide a full UNC path as the filename to open. This allowed one to launch remote CHM files locally. Microsoft fixed this vulnerability in Security Bulletin MS00-37.
Although the ability to launch remote CHM files was corrected, however, the control was still marked safe for scripting. Furthermore, the control could still launch local CHM files, so one could launch local CHM files via remote scripts (such as HTML e-mail and remote Web pages). This wasn't viewed as a vulnerability because the malicious CHM file would already need to exist on the local system.
The worm combines using the local execution ability of showHelp with a cache bypass vulnerability. Normally, when HTML e-mail is received, inline files such as images (<IMG SRC=inline.gif>) are automatically saved to the Temporary Internet Files folder. By design, this cache directory is treated as the Internet zone and is off limits to remote scripts. Thus even if W32/Blebla were able to save a local CHM file to this directory, showHelp would not have permission to load files from that directory.
Using a cache bypass vulnerability in Microsoft Outlook, the worm was able to save a CHM file to the known location of C:\Windows\Temp, bypassing the requirement to utilize the restricted Temporary Internet Files directory.
After the malicious file is saved to C:\Windows\Temp, the worm launches the malicious CHM file using the showHelp method.
This demonstrates a dual-vulnerability attack. The showHelp method itself is not vulnerable. However, by combining the showHelp method with another vulnerability, an attacker can remotely execute arbitrary code on the local system.
Microsoft fixed the cache bypass vulnerability in Security Bulletin MS00-046.
If the HHCtrl ActiveX control is simply marked not safe for scripting, then this type of attack cannot take placewhether or not another vulnerability exists. Unfortunately today, the HHCtrl is still marked safe for scripting and thus can be used remotely to launch local files.