Network Defense by Rik Farrow A 'new' attack takes advantage of programming errors that could easily be avoided During last summer's Black Hat conference, several security practitioners moaned that there was nothing new happening in security. Not that nothing was happening, just that it was more of the same old stuff--stuff that should have been fixed years ago, but has not. One particular attack has become almost ubiquitous in the last several years, the buffer overflow attack. While the attack requires particularly arcane and detailed knowledge of both assembler and, in the case of Windows, operating system interface details, once someone has coded an exploit and published it, anyone can use it. The results of these exploits provide interactive command shells on UNIX systems, and the ability to upload and execute arbitrary programs on Windows systems. The sad thing about buffer overflow exploits is that good programming practices could wipe out even potential exploits, but that simply hasn't happened. Operating system developers, including Microsoft, continue to use the same dangerous subroutine calls that open their software to exploitation. No wonder the security experts are getting bored. Your own defense revolves around controlling access to sensitive systems, installing software updates that replace exploitable software, and being aware of what a buffer overflow attack looks like when your system is the intended victim. If you are a programmer, you could make some security person's day, and avoid making the same old mistakes. An Infamous Worm In November 1988, the Internet Worm shutdown over 6,000 systems, just about cutting off all traffic on the Internet. One of the methods used to gain access to systems was a buffer overflow exploit of a UNIX service called finger. When you fingered a user, the finger service would return information about the user, for example, the user's real name and phone number. But the buffer overflow attack on finger replaced the server program with a UNIX command interpreter, or shell. This shell was then used to copy across a program that uploaded, linked, and then executed, a new copy of the Worm. Buffer overflow attacks remained relatively unheard of for many years following the Worm. One known example came in November of 1994, when one of the first commercial Web servers, running HP-UX (Hewlett-Packard UNIX), was successfully breached using a buffer overflow attack against the NCSA 1.3 Web server. As this Web server sat on the target's internal network and could be connected to through the firewall, the attackers had unfettered access to the victim's internal network. The attackers, calling themselves the Internet Liberation Front, had a field day. But the event that really fueled the frequency of attacks was the publication of a paper entitled "Smashing the Stack for Fun and Profit", based on a paper written by Mudge of the L0pht. Published by Phrack in November of 1996, Aleph One's paper explains in detail how a programmer would go about writing a buffer overflow exploit against a UNIX system program. While somewhat mindbending, the paper is good reading, especially if you have tried to understand how buffer overflow exploits work from reading the source to exploits. In 1997 and 1998, buffer overflow exploits became extremely common, mainly targetting UNIX systems, and in particular the Open Source versions. While the Open Source organizations, like the various Linux distributers or FreeBSD, were quick to release patches, the number of exploits was astounding. To get a feel for the scope of this problem, you can visit www.rootshell.com, and search on "buffer overflow". You could also visit other sites, such as the L0PHT (that is a zero, not an 'o'), and search for exploits. You will see that exploits continue right into 1999, and begin to include Microsoft products, such as IIS, as well. One reason why Microsoft products were not initially attacked is that the techniques required are somewhat different. But Dildog's paper helped raise the awareness level of eager hackers of Microsoft everwhere. Anatomy of a Buffer Overflow Buffer overflows occur mainly because of the C language, and partially because of poor programming practices. The C language is not going to change, but poor programming practices just might undergo improvement. The C programming language appeared in the early seventies as a tool for porting the UNIX oeprating systems and utilities to multiple architectures. While UNIX was initially written in assembler, porting it to C made UNIX the first really portable operating system. C was designed to be tight and fast, just a step above assembler. The C language is also a structured programming language. Object oriented languages, like Java, are organized around data. Structured programming languages use the function call as their unit of organization. The designers of C made a great leap forward (they were not the first, as ALGOL was also structured). They also created the framework for the buffer overflows. Each time a function is called, arguments to the function get copied to an area of memory called the stack. In assembler, you store things on the stack by pushing them, and retrieve them by popping them off the stack. All CPU architectures currently in use support the notion of a stack and have a special register (the stack pointer) and operations for pushing and popping. There is also an operator that takes an address off the stack and copies it into the program counter, the register that determines the address of the next instruction to execute. Calling a function always pushes the return address onto the stack. The problem with this design shows up within the called function. Any variables defined within this function are also stored in space allocated on the stack. For example, if a string, such as the name of a file to open, or a password, needs to be defined in the function, a number of bytes will be allocated on the stack. The function can then use this memory, but it will automatically be unallocated after the function returns--quite a neat design. But, C does no bounds checking when data is stored in this area, opening a narrow window for an attacker. C subroutine calls that copy data, but do no bounds checking, are the culprits (as well as the programmers who use these calls). The strcat(), strcpy(), sprintf(), vsprintf(), bcopy(), gets() and scanf() calls can be exploited because none of these functions checks to see if the buffer, allocated on the stack, will be large enough for the data copied into the buffer. It is up to the programmer to either use a version that makes the check (such as strncpy()) or to count the bytes of data before copying them onto the stack. Given that there is a list of commonly abused subroutine calls, you might think it reasonable that all uses of these calls would be checked, and the problem fixed forever, right? Actually, it is not quite as easy as that, and there are other ways of making similar, and as exploitable, mistakes (for example, appending characters in a loop). Now, the stage is set. But understanding this is the easy part. The attacker must also understand enough assembler, the byte codes used by processors like the Intel Pentium, to code the exploit itself. In a buffer overflow exploit, code gets written on the stack, beyond the return address and function call arguments, and the return address gets modified so that it will point to the beginning (approximately) of the code. Then, when the function call returns, the attacker's code gets executed instead of normal program execution. (Figure 1) Getting the return address to point to the right location is part of the 'art' of buffer overflow exploits. Commonly, an offset argument allows the attacker to try different locations merely by applying different values to adjust the position of the modified return address. The other part of the art covers the code itself. On UNIX systems, this is relatively simple. A short program, that can be copied outright from existing exploits, executes a local program, most commonly the UNIX command interpreter, /bin/sh. If the attacker already had interactive access, the result often is an interpreter run with superuser privileges. If the attacker is remote, the TCP connection used to initiate the attack is now connected to the shell program, and often has superuser access. Doing Windows The code is more complex for Windows exploits. Under Windows, doing anything requires more code then a similar UNIX exploit. But attackers can take advantage of knowledge gained in writing viruses. For example, Dildog describes how to discover the address of useful library routines that can be used to download the executable program of the attacker's choice, save it as a file, then execute it, all in less than 500 bytes of code. In this case, the buffer overflow is more like the Internet Worm, in that the overflow code itself serves to download the real exploit (which might be Back Orifice, or any number of the hundred or so trojans for Windows). So, while writing the Windows exploit is more difficult than the UNIX version, the result is that an HTTP request is used to download any application the attacker desires. Victims of buffer overflow attacks may or may not notice anything amiss. On UNIX systems, failed attacks often leave behind core files, the result of program that crashed if the offset value was not correct. On Windows boxes, successful attacks might result in registry changes, a window that appears briefly, or the invocation of a browser when the user was not expecting it. Virus checking software will pick up many of the trojans that might be installed during such an attack. Firewalls may or may not protect against these attacks. The most common firewalls used today, stateful inspection firewalls, will pass most buffer overflow expoits. Application gateway firewalls will often prevent buffer overflow exploits, but only if the application gateway was written correctly, and the firewall was configured to actually use the application gateway (it is optional on most firewalls, and sometimes disabled because application gateways are slower and may block traffic that only resembles an attack). Perhaps buffer overflow attacks are only fascinating to gearheads, but they are becoming boring to security professionals, who know that they can be prevented. Run the minimum number of services, disable automatic execution of software upon the receipt of email, do not blindly start up software, such as NetMeeting, and install security patches as soon as possible. And perhaps you will be lucky enough to be bored about security as well. Resources: Phrack, Smashing the stack, issue 49, article 14 of Phrack by Aleph One (Elias Levy): http://www.phrack.com/search.phtml?view&article=p49-14 The article by Dr. Mudge that got the ball rolling: http://www.L0pht.com/advisories/bufero.html Cult of the dead cow, by Dildog ('R' rated language), The Tao of Windows Buffer Overflows: http://www.cultdeadcow.com/cDc_files/cDc-351/ Rootshell, a site you can safely visit and search for buffer overflow exploits: http://www.rootshell.com/ L0PHT also includes a search facility, as well as some anti-hacking tools: http://www.l0pht.com/ Figure 1 caption: This illustration depicts a stack with some details left out to make it simpler. At point a, arguments have been pushed on the stack prior to calling the function. In b, the function has been called, pushing the Return Address on the stack. Normally, when the function completes, execution resumes at the Return Address. In c, space for the local array name[256] has been allocated on the stack. In d, the attack has written past the end of the stack, replacing the Return Address and writing the exploit code onto the stack. The new Return Address points to the start of the exploit code.