tack signature generation component that creates a filtering rule for front-end firewall or intrusion prevention system to block the detected attack and its variants, ...
Automatic Patch Generation for Buffer Overflow Attacks Alexey Smirnov
Tzi-cker Chiueh
Computer Science Department Stony Brook University
Abstract
is augmented with additional checks to protect the program against certain anomalies at run time. Even though these dynamic checking systems could effectively detect and prevent control-hijacking attacks, they lack a post-attack response functionality that can effectively prevent recurrence of the same attack and its variants. An ideal post-attack response system should be able to generate a characterizing signature for a detected attack that the front-end firewall or intrusion prevention system (IPS) can use to block it, and to generate a patch that can seal the security hole that the detected attack exploits. Currently, attack signature generation is mostly manual, and therefore cannot effectively thwart fast-spreading worms. As for patch development, it takes 54 days on average to release a patch for a new vulnerability, according to Symantec’s Internet Security Threat Report [19]. Therefore, the ability to facilitate or even automate patch development is essential to shrinking the window of vulnerability. This paper describes the design, implementation, and evaluation of a program transformation and execution trace analysis system called PASAN, which automatically instruments a network service program in such a way that the resulting binary can detect control hijacking attack and leave enough run-time information for PASAN to generate the corresponding patch. Therefore, PASAN offers a powerful defense against fast spreading polymorphic worms because it reduces the development time for firewall filter rules and software patches for detected attacks by at least an order of magnitude. PASAN instruments a network application’s source code with additional instructions to detect control hijacking and to record enough run-time information from which it can derive an attack signature and patch for the detected attack. PASAN uses the recorded run-time information to reconstruct the data and control dependencies that actually take place at run time. Because all known control-hijacking attacks corrupt the target address of a certain indirect branch instruction, after detecting a control-hijacking attack, PASAN’s repair-time component first finds the corrupted target address. Next it identifies the basic block in which the compromised target address is last modified, then the data structure that is being overflowed and its size. Finally it creates a bound check to prevent the overflowed data structure from being overflowed in the future. The resulting bound check and where it should be inserted constitute a software patch that permanently fixes
Control-hijacking attacks exploit vulnerabilities in network services to take control of them and eventually their underlying machines. Although much work has been done on detection and prevention of control-hijacking attacks, most of them did not address the problem of repairing the attacked network services so as to prevent the same attacks from recurring. Ideally, post-attack repair should consist of an attack signature generation component that creates a filtering rule for front-end firewall or intrusion prevention system to block the detected attack and its variants, and a patch generation component that creates a fix to permanently eliminate the vulnerabilities that the detected attack exploits. This paper describes the design, implementation and evaluation of a program transformation and execution trace analysis system called PASAN that can automatically instrument the source code of network service programs in such a way that it can detect control-hijacking attacks and automatically generate patches to seal the vulnerability being exploited by the detected attack. We have implemented the first PASAN prototype as a GNU C compiler extension that aims at stackbased buffer overflow attacks but could be easily generalized to accommodate other control-hijacking attacks. Testing this prototype with seven network daemon programs with known vulnerabilities show that the automatically generated patches can successfully fix the vulnerability. In addition, these patches are similar in their structure to those that are manually created. The run-time performance overhead of application programs instrumented by PASAN is between 10% and 23%, except two programs, whose CPU consumption is low.
1
Introduction
Control-hijacking attacks typically compromise a controlsensitive data structure in a vulnerable application such as a return address or a function pointer and eventually usurp the control of the application and the underlying machine. Examples of control-hijacking attacks are buffer overflow attacks, integer overflow attacks, and format string attacks. There have been numerous methods proposed for attack detection and prevention. Many of them [11, 10, 13] take a dynamic checking approach in which the original program 1
the vulnerability. The current PASAN prototype is implemented as an extension to GNU C compiler 3.4.1 and focuses only on stackbased buffer overflow attacks, which overflow a function’s local array/buffer and eventually modify that function’s return address. However, the same techniques could be easily applied to other types of buffer overflow attacks and format string attacks. Although PASAN assumes the availability of source code, we believe its techniques can be applied to Win32 binaries directly by leveraging such Win32 binary analysis and instrumentation infrastructure as BIRD [14].
2
ments that express constraints on network packet bytes and therefore cannot be used by existing firewalls and intrusion prevention systems. In addition, these constraints do not capture length constraints. Second, Vigilante does not attempt to produce patches as PASAN does. On the other hand, Vigilante supports a sophisticated alert distribution mechanism that guarantees the security of alerts that are exchanged among its distributed sensors.
3
Execution State Logging
PASAN instruments a network application so that it can record enough information to reconstruct all the data and control dependencies that take place at run time, and the size information associated with static arrays and dynamically allocated buffers. A buffer overflow attack uses one or multiple network packets to overrun a buffer in the victim program and eventually overwrite the target address of some indirect branch instruction. Therefore, the corrupted target address must be data- or control-dependent on attack packets. Memory updates logging is designed to track these control and data dependencies. A data dependency from variable X to variable Y is created when variable X is assigned an expression that includes variable Y. Tracking control dependencies allows PASAN to identify the ”context” that sets up the final attack packet. For example, the Blackmoon FTP server attack [3] uses a CWD command with an excessively long argument. The FTP server requires user authentication before processing user commands. The authentication state is saved in variable is auth checked each time the program receives a command. If the authentication is not completed the command is dropped. After the authentication is completed, subsequent variable assignments are control-dependent on is auth. However, no data dependencies exist between is auth and subsequent variables. Using data dependencies alone will not be able to identify the authentication packets. To capture data dependencies, PASAN instruments network applications to produce a memory updates log. Each memory updates log record contains the following fields: read addr, write addr, len, data, file id, and lineno. For an assignment statement X = Y, where X and Y are directly referenced variables, array references (e.g., a[i]) or de-referenced variables (e.g., *(a+1)), PASAN logs its effects into a log record, where the read addr field contains the address of the right-hand-side variable of the assignment operation, in this case Y’s address, the write addr field holds the address of the left-hand-side variable being modified, in this case X’s address, the len field is the size of the modified variable, size of X in this case. The data field is not used for this type of statements. The file id and lineno fields identify the source code location of the statement for which the log record was generated. If Y is a complex expression containing a number of variables or a function call, PASAN traverses the expression recursively and identifies all variables in it. A log record with the same
Related Work
FLIPS [12] uses instruction set randomization to detect a code-injection attack and create its attack signature. FLIPS uses STEM [17], an x86 selective transactional emulator that executes the vulnerable functions in a sand-boxed environment, and unrolls the memory updates that the attacker performed, thus allowing program to continue as if attack packets were never received. The signatures that include the last packet are prone to both false positives and false negatives. Context-aware signatures augment a single packet with the attack state by providing all packets relevant to a particular attack. Nemean [21] and Polygraph [15] analyze a large pool of malicious network traffic to derive multi-packet context-aware attack signatures. Autograph [8], EarlyBird [18], and Honeycomb [9] are also based on analyzing network data but they are capable of producing single-packet signatures only. PADS [20] generates signatures as a probabilistic distribution of characters for each byte of attack packets. D. Brumley et al. [2] present a theoretical foundation of vulnerability-based signature generation and introduce three types of signatures: a Turing-machine signature, a symbolic constraint, and a regular expression. They describe algorithms to generate a signature of each type and present the implementation results. Their prototype was able to generate signatures of several one-packet attacks. Sidiroglou and Keromytis [16] addressed the problem of automatic patch generation.The idea is to re-allocate the compromised local array as a global array and sandwich it in a pair of write-protected pages. The instrumented program installs a special page-fault signal handler that processes write attempts to the protected buffer. Although generated completely automatically, the resulting fixes are not similar to those that human programmers write. Human effort is required to merge the automatically generated patches with the source code. These patches are designed as temporal fixes rather then persistent patches. On the commercial side, Determina [7] generates memory patches from source-code patches that allow patching a vulnerable program on the go without restarting it. Vigilante [6] took a similar approach as PASAN in that it also employs dynamic data/control flow analysis techniques to derive attack signatures. However, there are two differences between PASAN and Vigilante. First, the attack signatures that Vigilante creates are in the form of program seg2
write address of the left-hand side variable but a different read address is created for each identified variable. Similarly, a log record is created for each argument of the function call on the right-hand side of the expression. In case of a function call dependencies are created between the arguments of the caller and the parameters of the callee. When a function returns a value, a dependency is created between the value and the destination variable of the caller function that the value is assigned to. To minimize the number of functions being compiled, standard library function calls that modify the calling program’s memory state such as memcpy() are proxied with a call to a proxy function from PASAN’s library. PASAN proxies the following classes of libc functions: network and file input functions, socket functions, memory copying/concatenation functions, and format string functions. Whenever a proxy function is called, it produces a memory updates log record that describes the side effects of the original library function. The fields of the memory updates log entry are set differently for different functions. For a string copying function, for example strcpy(a, b) the read address field is set to b, the write address is set to a, and the length equals strlen(b)+1. The data field is not used in this case. For a network read function such as recv() the read address is set to tag value RECV TAG, the write address is set to the address of the destination buffer, the length is set to the number of bytes read, and data field stores the data read from the network which is used for patch generation. To capture control dependencies, PASAN logs the address of the first instruction in each basic block into a control transfer log. The information in the control transfer log, coupled with the control flow graph, allows an accurate reconstruction of the complete execution trace at run time.
4
Source code:
Patched source code:
len=strlen(src); for (i=0; i=dest+sizeof(dest) ? dest+sizeof(dest)-1:len].
5.2
Patch Testing
After PASAN creates a patch, it applies the patch to the vulnerable program, recompiles the program with boundschecking detection instead of return address detection, and exercises the same attack packets against the new program. If during this testing PASAN detects an attack, it generates another patch and repeats the same testing cycle. If no attack is detected, the patched program is not vulnerable to the original attack packets and the patch generation is completed. PASAN’s patch testing stage checks whether the produced patch indeed stops the original attack and whether the original attack exploits any other vulnerabilities.
6
Evaluation of PASAN
We used seven network daemons with known vulnerabilities to evaluate the effectiveness of PASAN’s attack signature generation and patch creation algorithms. The following programs were used in our experiments: • • • •
ghttpd 1.4 — a web server; named 8.1 — a DNS server; drcatd 0.5.0 — a remote cat program; ntlm auth — an authentication program from squid 2.5-stable1, a web caching daemon; • passlogd 0.1c — a remote logging program; • htdigest 2.0.52 — Apache user authentication up-
date program; • samba-2.2.7 — SMB file sharing. We used the exploit code from Bugtraq [4] and Securiteam [1] to attack these test programs. We measured the compilation time of the PASAN compiler and the size of the binaries it produces as compared with the standard GNU C compiler. All program in the test suite were compiled with -g -O flags. To evaluate the throughput penalty due to PASAN’s instrumentation, we sent a series of requests to each of the test programs and measured the total elapsed time it takes to process these requests. Results of these tests are presented in Table 1. The average throughput penalty of the tested programs is between 10% and 23% 4
while (*p!=’\0’) *dst++=*p++; *dst=*p;
while (1) { if (*p==’\0’) { *dst=’\0’; break; } *dst++=*p++;}
while (*p!=’\0’) *dst++=*p++; *dst=’\0’;
Figure 2. Program examples with terminating characters (left and middle) and with no terminating characters (right). The code on the left has a terminating character because of a data dependency, the code in the middle has a control-dependent terminating character. Program ghttpd drcatd named passlogd ntlm auth htdigest samba
Compilation time (sec) GCC PASAN Difference
0.516 1.176 7.721 1.72 1.86 2.62 163
3.35 1.416 31.0 2.05 2.14 3.21 388
549 20 302 19 15 22 138
GCC
Binary size (bytes) PASAN Difference
39234 37548 776664 21828 99347 6228 3986284
154901 135789 1955742 206637 314383 6374 11470997
294% 261% 151% 847% 216% 23% 188%
Throughput (requests per second) GCC PASAN Difference
219 187 1512 300 385 9,800 156.1
200 152 1325 120 342 4,600 133.3
10% 23% 14% 150% 13% 113% 17%
Table 1. Compilation time, binary size, and run-time overheads of PASAN for the test programs. for all programs except two, which we believe are anomalies and will discuss in more detail next. The main reason that the throughput penalty for passlogd and htdigest is substantially higher than others is because neither program requires much CPU processing. Passlogd essentially receives a packet from the network interface, scans it in a loop once, and prints it to the system log. Saving memory updates log records in each iteration takes much longer than processing one byte of the received packet. The same processing pattern occurs in htdigest. To evaluate the quality of the patches that PASAN generates, we measured the number of iterations and amount of time required in patch generation, and examined the internal workings of each patch. These results are summarized in Table 2. We describe our experiences with each of these test programs below. ghttpd attack: At its first iteration PASAN’s patch generation algorithm patches the vsprintf() function in function Log(). The patch testing algorithm then detects an offby-N bug in the sprintf() function. Only a full array bound checking compiler such as BCC can detect this offby-N bug because it does not overwrite the return address. drcatd attack: A call to sprintf() inside the logging function logit() overruns a local buffer. In this case, patch generation takes only one iteration and replaces sprintf() with snprintf(). The second iteration makes sure that the program is not vulnerable to the same attack. named attack: The memcpy() function in the query processing function req iquery() overwrites a local buffer using the size of the source array as the length argument of memcpy(). PASAN replaces the size argument with the size of the destination buffer. squid ntlm auth attack: This vulnerability is similar to the previous one. PASAN’s patch generation algorithm replaces the size argument of the vulnerable memcpy() call
with the size of the destination buffer. Patch testing then finds an off-by-one error when a NULL character is written into array pass: pass[25]=’\0’; and the array is declared as char pass[25]. PASAN replaced the index with sizeof(pass)-1. This off-by-one error does not exist in the latest release of squid. passlogd attack: The original attack does not exploit the vulnerability in the second while() loop. Increasing attack packet’s length enables the resulting attack to exploit both vulnerabilities at the same time. To fix the vulnerabilities, it takes PASAN two iterations to add a length check in front of each while loop. On the third iteration, PASAN found an offby-one error that is similar to the error found in squid. This error was found in the latest release of passlogd. We sent the description of this off-by-one bug to passlogd’s developers, who confirmed the existence of this bug. htdigest attack: Function sprintf() is the cause of the vulnerability. The automatically generated patch uses snprintf() instead of sprintf(). The second iteration of the patch generation algorithm finds no new vulnerabilities. samba attack: Samba uses its own implementations of standard library functions, for example, StrCpy() instead of strcpy(), etc., which are proxied together with standard library functions. At this point, PASAN cannot automatically identify replacement implementations of standard library functions and require explicit annotations from the programmers. The patch generation algorithms fix Samba’s vulnerability by replacing these functions with their safe counterparts. The exercise of applying PASAN to generate patches for the test programs leads to the following findings: • Patch testing is a valuable addition to patch generation because it can help identify the inadequacy of the first 5
Program ghttpd drcatd named passlogd ntlm auth htdigest samba
Attack packets 1 3 1 1 2 1 6
Patch type vsprintf, sprintf sprintf memcpy two while loops memcpy sprintf strncpy
# of iterations 3 2 2 3 4 2 2
heap overflow attacks as soon as it uses a different attack detection method than RAD. As for integer overflow attacks and format string attacks, PASAN’s dynamic slicing mechanism is still useful in identifying the first data structures that are being overflowed and therefore generating the attack signatures, but it needs to use different bounds checking techniques for patch creation, e.g., integer range check for integer overflow attacks and input argument list bound check for format string attacks. We plan to cover all control-hijacking attacks in the next release of PASAN.
Table 2. Number of identified attack packets, patch type and number of iterations of patch generation algorithm.
References
patch and other vulnerabilities that the original attack does not exploit. PASAN found two such bugs (both of them are off-by-one error), one of which was already fixed and the other was brand new. • To our surprise, this exercise also revealed that programmers make mistakes even in memcpy() that includes a length argument and is therefore similar to strncpy(), a safe analog strcpy(). PASAN found two memcpy() errors in addition to errors in string processing functions such as strcpy() and sprintf(). • Comparison between automatically generated patches and vendor-released patches showed that they are quite similar in structure. The main difference is that vendor patches are more complete in that they perform cleanup and exit the function with an error code when bounds checking finds an illegal modification, whereas patches that PASAN generates simply continue the program execution. • Patch generation and testing was fully automated for all programs in the test suite except passlogd, which required attack packets to be sent from a remote computer. This result suggests that it is indeed possible to use PASAN to generate patches for previously unknown control-hijacking attacks as soon as they are detected without human participation.
[1] Beyond Security’s SecuriTeam. http://www.securiteam. com. [2] D. Brumley, J. Newsome, D. Song, H. Wang, and S. Jha. Towards automatic generation of vulnerability-based signatures. In Proc. of IEEE Symposium on Security and Privacy, 2006. [3] Bugtraq. Blackmoon ftp server buffer overflow vulnerability. http: //www.securityfocus.com/bid/3884/info. [4] Bugtraq. Bugtraq mailing list. http://www.securityfocus. com. [5] T-C. Chiueh and F-H. Hsu. RAD: A compile-time solution to buffer overflow attacks. In Proc. of 21st Intl. Conf. on Distributed Computing Systems, 2001. [6] M. Costa, J. Crowcroft, M. Castro, L. Zhou A. Rowstron, L. Zhang, and P. Barham. Vigilante: End-to-End Containment of Internet Worms. In Proceedings of ACM Symposium on Operating Systems Principles, 2005. [7] Determina. How the memory firewall works. http://www. determina.com/whitepapers/index.html. [8] H.-A. Kim and B. Karp. Autograph: Toward automated, distributed worm signature detection. In Proceedings of USENIX Security Symposium, 2004. [9] C. Kreibich and J. Crowcroft. Honeycomb — creating intrusion detection signatures using honeypots. In Proc. of the Second Workshop on Hot Topics in Networks (Hotnets II), 2003. [10] L-C. Lam and T-C. Chiueh. Automatic extraction of accurate application-specific sandboxing policy. In Proc. of Seventh International Symposium on Recent Advances in Intrusion Detection, 2004. [11] L.-C. Lam and T.-C. Chiueh. Checking array bound violation using segmentation hardware. In Proc. of the International Conference on Dependable Systems and Networks, 2005. [12] M. Locasto, K. Wang an A. Keromytis, and S. Stolfo. FLIPS: Hybrid adaptive intrusion prevention. In Proc. of RAID 2005, 2005. [13] S. Nanda and T-C. Chiueh. Foreign code detection for Windows/X86 binaries. ECSL Technical report TR-190, Computer Science Department, Stony Brook University, 2005. [14] Susanta Nanda, Wei Li, Lap chung Lam, and Tzi cker Chiueh. Bird: Binary interpretation using runtime disassembly. In Proceedings of the 4th IEEE/ACM Conference on Code Generation and Optimization (CGO’06), March 2006. [15] J. Newsome, B. Karp, and D. Song. Polygraph: automatically generating signatures for polymorphic worms. In Proc. of the IEEE Symposium on Security and Privacy, 2005. [16] S. Sidiroglou and A. D. Keromytis. Countering network worms through automatic patch generation. IEEE Security and Privacy, 3(6), 2005. [17] S. Sidiroglou, M. E. Locasto, S. W. Boyd, and A. D. Keromytis. Building a reactive immune system for software services. In Proc. of 2005 USENIX Annual Technical Conference, 2005. [18] S. Singh, C. Estan, G. Varghese, and S. Savage. Automated worm fingerprinting. In Proc. of the 6th ACM/USENIX Symposium on Operating System Design and Implementation, 2004. [19] Symantec. Symantec internet security threat report. http://www. symantec.com/, September 2005. [20] Y. Tang and S. Chen. Defending against internet worms: a signaturebased approach. In Proc. of INFOCOM 2005, 2005. [21] V. Yegneswaran, J. Giffin, P. Barford, and S. Jha. An architecture for generating semantics-aware signatures. In Proc. of the USENIX Security Symposium, 2005.
7 Conclusions Although there have been numerous efforts on thwarting control-hijacking attacks, most of them focused mainly on detection and prevention, but failed to provide an adequate post-attack response. This paper presents the design, implementation, and evaluation of a system called PASAN that can automatically detect any stack-based buffer overflow attack, and generate a bounds checking patch to permanently seal the security vulnerability the attack exploits. The basic idea of PASAN is to record an execution state log at run time so that the repair-time component can track the dynamic control and data dependencies and derive the patch for a detected attack in a way completely transparent to to the application developers and users. Although the current PASAN prototype works only for stack-based overflow attacks, it can be directly applied to 6