Offset2lib: bypassing full ASLR on 64bit Linux

Introduction

Offset2lib is a security weakness on the implementation of the ASLR in GNU/Linux when the executable is PIE compiled.

A PoC attack is described to illustrate how the weakness can be exploited. The attack bypasses the three most widely adopted and effective protection techniques: No-eXecutable bit (NX), address space layout randomization (ASLR) and stack smashing protector (SSP). The exploit obtaines a remote shell in less than one second.

ASLR weakness

It is specific to GNU/Linux and does not affect Windows or Mac OS. It is not a programming error on the code that implements the ASLR, but a weakness on the design. Fortunately, it can be easily fixed on 64 bit systems.

The problem appears when an application is PIE compiled. The executable image is handled as if it were a shared library, that is, it is loaded at a random location in memory. The GNU/Linux algorithm for loading ASLR objects works as follows:

The first shared object is loaded at a random position (the application). The next shared objects are loaded one after the other.



Memory map of PIE compiled apps. Memory map of PIE compiled apps.

The offset2lib value it is a constant value which may be slightly different on each system, depending on the library version and the order how the libraries have been loaded.

Offset2lib attack

The goal of the attack is to obtain an address which belongs to the application code. The following attack exploits a standard stack buffer overflow to obtain the saved-IP address (of the app. code) stored in the stack.

We have structured the attack in 5 steps. Our attack starts by doing an off-line analysis of the target application and its execution environment. The unknown information (hidden by the ASLR) is obtained via brute force, thanks to the forking server architecture of the target. Once we have the full address of the application, the base address of the application is calculated. The last step is to have the memory map of all the libraries, which depends on the GNU/Linux distribution of the target. With the obtained information it is easy to arm a ROP program to get a remote shell. The complete on-line attack may take no more than one second.

The steps to build the attack are:

Extract static information Bruteforce a part of saved-IP Calculate the base application address Calculate the offset2lib constants Obtain mmapped areas

Step Description Result 1a Set the highest 24 bits. These bytes are known off-line. 1b Set the 12 lower bits. Due to page alignment. 2 The remaining 28 random bits of the saved-IP are obtained by using the byte-for-byte attack against the saved-IP. Note that only three and a half bytes need to be bruteforced. 3 Using the saved-IP calculated in the previous step we can obtain the executable base address as follow: App_base = (savedIP & 0xFFF)-(CALLER_PAGE_OFFSET << 12) 0x00007F36C6fEB000 4 Calculate the offset2lib for the target libraries. These values are different depending on the system, but quite similar among them. A fast way to obtain these offset2lib values are by executing the application (on local) and print the offsets. The offset2lib does not depends on the application itself. We have calculated the offset2lib for some Linux distributions. Distribution Libc ver. Offset2lib CentOS 6.5 2.12 0x5b6000 Debian 7.1 2.13 0x5ac000 Ubuntu 12.04 2.15 0x5e4000 Ubuntu 12.10 2.15 0x5e4000 5 The base address of any library can be calculated by just subtracting the offset2lib. For example to obtain the libc base address we simply do: Libc_base = App_base - offset2lib 0x00007f36c6a07000

The attack presented here is only a demonstrative example of how to exploit the ASLR offset2lib weakness. We believe that the way to take of advantage of this weakness is only limited by attackers creativity.

Proof of Concept

Small program to obtain the offset2lib value for the libc: get_offset2lib.tgz The vulnerable server: vuln-server-64bit.tgz The python script to make the attack: exploit-offset2lib-ubuntu-14.04.1-LTS.py

$ tar xvf vuln-server-64bit.tgz server_64_PIE_SSP.c Makefile $ make rm -f server_64_PIE_SSP gcc server_64_PIE_SSP.c -o server_64_PIE_SSP -m64 -Wall -fPIE -pie -fstack-protector-all $ sudo /etc/init.d/apport stop # avoid creating cores on crashes (speed-up) $ sudo ./server_64_PIE_SSP Starting server on port [9999]

$ tar xvf get_offset2lib.tgz Makefile.offset2lib get_offset2lib.c $ make -f Makefile.offset2lib $ ./get_offset2lib -------------------------------------=======--------------------------------------- ------------------------------=======================------------------------------ Authors: Hector Marco-Gisbert <hmarco@hmarco.org> Ismael Ripoll Ripoll <iripoll@upv.es> Comment: Script to obtain the offset2lib value of this machine. Attack: http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html ------------------------------=======================------------------------------ -------------------------------------=======--------------------------------------- Offset2lib (libc): 0x5f0000

$ sed -i 's/\(OFFSET_TO_LIBC\).*=.*/\1= 0x5f0000 /' \ exploit-offset2lib-ubuntu-14.04.1-LTS.py $ objdump -d server_64_PIE_SSP| grep -A1 vulnerable_function\>$ 1149: e8 82 fc ff ff callq dd0 <vulnerable_function> 1 14e : 48 8d 45 c0 lea -0x40(%rbp),%rax $ sed -i 's/\(PAGE_NUMBER_OF_NEXT_INSTRUCTION_TO_CALL\).*=.*/\1= 0x1 /' \ exploit-offset2lib-ubuntu-14.04.1-LTS.py $ sed -i 's/\(OFFSET_SAVED_RIP\).*=.*/\1= 0x14e /' \ exploit-offset2lib-ubuntu-14.04.1-LTS.py

$ chmod a+x exploit-offset2lib-ubuntu-14.04.1-LTS.py $ ./exploit-offset2lib-ubuntu-14.04.1-LTS.py exploit-server_64_PIE.py -s <server> -p <port> $ ./exploit-offset2lib-ubuntu-14.04.1-LTS.py -s localhost -p 9999 [+] Exploit ASLR 64 bit systems [+] Trying to find out the canary offset [+] Offset is 56 bytes [+] Brute forcing stack canary [+] SSP value is 0x60e0792a523eb900 [+] Brute forcing RBP [+] EBP value is 0x00007fff0e79d160 [+] Brute forcing Saved RIP [+] RIP value is 0x00007f44a19b114e [+] Text Base at 0x00007f44a19b0000 [+] Libc Base at 0x00007f44a13c0000 [+] Getting shell ... root@test:/home/test # id id uid=0(root) gid=0(root) groups=0(root) root@test:/home/test #

5 Recommendations

There are several solutions and workarounds to this weakness.

5.1 Prevent the weakness with PaX patch

Among many other security improvements, the PaX (from GRSecurity) patch places the executable at random positions with respect to the other shared objects. PaX defines four zones:

delta_exec : code, data, bss.

: code, data, bss. brk : heap.

: heap. delta_mmap : libraries, mapped files, thread stack, shared memory, ...

: libraries, mapped files, thread stack, shared memory, ... delta_stack : user stack.

5.2 Fixing Offset2lib weakness

In order to remove the offset2lib weakness, the executable shall be located at a different zone than libraries. We have created a small patch for the current Linux kernel (3.18-rc7) which implements four different zones, so that the executable is not in the same zone than libraries.

More information about this solution at: Fixing Offset2lib weakness

5.3 Prevent the exploitation with RenewSSP

To successfully bypass the ASLR by exploiting the stack buffer overflow presented in the PoC, the attacker needs to bypass first the stack smashing protector (SSP). If the SSP protection can not be bypassed, then the PoC would fail.

A new technique, called renewSSP can be used to prevent brute force attacks against the SSP. This technique is a variant of the classic SSP where the value of the secret canary is renewed dynamically at key places in the program. This way, the secret is refreshed more often. The SSP value can be renewed per-process preventing the ``byte-for-byte'' attack. The technique is not intrusive, and can be applied by just pre-loading a shared library. The overhead is almost negligible.

A demonstrative video which shows how the attack can be prevented using the RenewSSP can be seen at http://www.renewssp.com.