New Exploit Protections in Android 4.1

Author: 

Shawn Webb

There have been a few articles about the new exploit protections in Android 4.1. The Duo Security blog has a great write-up on all the security Google added. This is a great step in the right direction for Android as a whole. The majority of the protections are related to ASLR and ELF. I would like to talk about defeating one protection in particular: RELRO + BIND_NOW. I will discuss how BIND_NOW also has the potential to make the ELF protection slightly weaker.

RELRO - Forcing the Global Offset Table to be read-only

The Global Offset Table (GOT) is an array that contains pointers to dynamically-linked symbols. The GOT allows your program to call functions in shared objects, no matter where the shared object lies in memory. This makes it so that the shared object can be loaded anywhere in memory, and your program can still use its dynamically exported functions (and dynamically exported global variables). Using a technique publicized by Silvio Ceasare, you can hijack dynamically-exported functions. For example, if your program calls printf, the address of where printf resides in memory will be located in the GOT. Your program will reference the GOT entry to find out where to call printf. By changing the GOT entry, you change where your program thinks printf is.

NYU Poly has a great blog article on securing the GOT and preventing hijacking of dynamically-exported functions. It forces the GOT to be read-only after the program has finished loading. Though the idea presented in that article does provide some level of protection, I want to discuss how it can still be abused.

Enter PTrace, The Debugging God, and libhijack, the Dynamic Function Hijacking God

PTrace (Process Trace) is the debugging facility in Linux (and most other POSIX OSes). PTrace allows you to become a god of sorts. You are able to change the CPU registers (even EIP). You can read and write from/to arbitrary memory locations. You can even write to memory locations marked read-only. That last bit is the key. We can get around RELRO by using a helper program that will use PTrace to modify the target process' GOT. Android (and Linux in general), by default, leaves PTrace enabled on production devices.

Libhijack, a tool I authored, makes hijacking GOT entries extremely easy. It allows a security analyst to hijack dynamically-loaded functions in relatively few lines of code. It can be seen as a proof-of-concept against RELRO. It works on 64bit FreeBSD and 32bit and 64bit Linux. It, however, does not currently work on ARM. Work is currently being done to port it to ARM, which would enable Android as a target.

BIND_NOW Weaknesses

RELRO only works if BIND_NOW is used. That forces the dynamic linker (the code that is responsible for loading your program in memory, its dependencies, etc) to resolve all dynamically-loaded symbols your program uses. BIND_NOW gives us two benefits: added security via RELRO and increased performance. It increases the time it takes to load a program, but nullifies the time it takes to do symbol resolution later during program execution. Tools like libhijack only work if the symbol has been resolved. If the symbol hasn't been resolved (it hasn't been referenced in the program), then libhijack cannot do its magic. By using BIND_NOW, all symbols are resolved from the start and libhijack will be guaranteed to work, lessening the overall security of RELRO+BIND_NOW.

Conclusion

The protections included in Android are a great start. I've long encouraged disabling such invasive debugging facilities like PTrace, especially in production. If a debugger is needed in production, a facility like DTrace ought to be used. PTrace can be used maliciously and can easily circumvent what would otherwise be great security measures. RELRO and BIND_NOW overall is helpful for both security and performance. The security of RELRO can be defeated easily by using PTrace.

 Update #1 (2012-07-19 11:37 EST)

As Jon Oberheide pointed out, you don't necessarily need PTrace to regain write access to the GOT. Since Android does not implement grsec, you can simply call mprotect() on the page in which the GOT resides to regain write access. On systems which implement grsec, you would need to use PTrace to regain write access. Since this article deals mainly about RELRO on Android, the solution of using PTrace to side-step RELRO isn't the most efficient solution.

Tags: 

4 Comments

ptrace

This is quite incorrect. You can only ptrace a process of the same uid. Therefore, your Android app won't be able to ptrace any target process and "bypass" RELRO.

More generally, if you already have the privilege to ptrace attach to a process, there's no gain in capabilities by overwriting the GOT, since you already inherently have full read/write access to that process address space.

Goal or RELRO

Hi,

it would be useful if you introduced why you think RELRO was introduced and what it is supposed to protect against. Your way to defeat it suggest a very different understanding from mine.

RELRO was primarily introduced to protect execution flow hijacking using .got overwrites. I.e. it was common to use for instance a write-4 primitive to change a .got address and get control over EIP that way. And making it read-only after a quick bind phase serves that goal. Your suggestion do not break this protection as far as I can tell.