In recent years, Android phone becomes widely
used in the world. At the same time, security becomes more and more important.
One of the most important interfaces in security which would expose to outside
world is root. In the more and more vehement completion, three kinds of
directions in root also becomes stronger. They are mitigation, bypass
mitigation mechanism and exploit tricks. Perhaps we should think it over, it is
very different from the other techniques which would be out of date. Root
security skills can be closed related all the time. In other words, some
innovative techniques can revive older skills and will be very convenient to
use. Geek also demonstrate brilliant techniques with every-time-period
mechanism. I'll elaborate that as well. Therefore I will write it with four parts. One is android security mitigation, the second is to bypass mitigation,
the third one is exploit tricks and last one is detecting exploits. They are
the most important features we must know as a security researcher. It is
noticeable that I mainly focus on the kernel security and introduce a small
amount of knowledge on user mode as we know complete root in android phones
cannot live without kernel and user-mode root has a few privileges. I have
summarized some latest mitigation in general.
We know android is based on linux kernel. So
the kernel security is also based on linux kernel security. Briefly, DAC allows
the owner of an object (such as a file) to set the security policy for that
object—which is why it’s called a discretionary scheme. As a user, you can, for example, create a new
file in your home directory and decide who else may read or write the file. This policy is implemented as permission bits
attached to the file’s inode, which may be set by the owner of the file. Permissions for accessing the file, such as
read and write, may be set separately for the owner, a specific group, and
other (i.e. everyone else). This is a relatively simple form of access control
lists (ACLs). There is also a superuser—an all-powerful entity which bypasses
Unix DAC policy for the purpose of managing the system. Running a program as
the superuser provides that program with all rights on the system. That is
fatal weakness for the DAC mechnism.
At the same time, accesses to sources are also not coverd such as
network packet flows. So here comes POSIX Capabilities, the aim of the feature
is to break up the power of the superuser, so that an application requiring
some privilege does not get all privileges. It's possible to reduce the number
of setuid applicatons on the system by assigning specific capabilities to them,
however, some capabilities are very coarse-grained and effectively provide a
great deal of privilege.
Security Enhanced Linux (SELinux) is an
implementation of fine-grained Mandatory Access Control (MAC) designed to meet
a wide range of security requirements, from general purpose use, through to
government and military systems which manage classified information. MAC security differs from DAC in that the
security policy is administered centrally, and users do not administer policy
for their own resources. This helps
contain attacks which exploit userland software bugs and misconfiguration.
In SELinux, all objects on the system,
such as files and processes, are assigned security labels. All security-relevant interactions between
entities on the system are hooked by LSM and passed to the SELinux module,
which consults its security policy to determine whether the operation should
continue. The SELinux security policy is
loaded from userland, and may be modified to meet a range of different security
goals. Many previous MAC schemes had
fixed policies, which limited their application to general purpose computing.
In android 6.0, it even opens Mutil-Level Security option which makes
segmentation of a single object more granular. It is important that it even
prevents normal user from accessing the device directories.
Secure computing mode (seccomp) is a
mechanism which restricts access to system calls by processes. The idea is to reduce the attack surface of
the kernel by preventing applications from entering system calls they don’t
need. The system call API is a wide
gateway to the kernel, and as with all code, there have and are likely to be
bugs present somewhere. Given the
privileged nature of the kernel, bugs in system calls are potential avenues of attack. If an application only needs to use a limited
number of system calls, then restricting it to only being able to invoke those
calls reduces the overall risk of a successful attack.
The original seccomp code, also known as
“mode 1”, provided access to only four system calls: read, write, exit, and
sigreturn. These are the minimum
required for a useful application, and this was intended to be used to run
untrusted code on otherwise idle systems.
A recent update to the code allows for
arbitrary specification of which system calls are permitted for a process, and
integration with audit logging. This
“mode 2” seccomp was developed for use as part of the Google Chrome OS.
PXN is set by changing the bit attribute.
PXN bit on every permission template for user-space, as well as UXN/PXN bits
for non-executable pages. This makes sure that every user-space page is mapped
with PXN bit set, which mitigates ret2usr attack. Ret2usr (return-to-user)
attack exploits the truth that the code in kernel mode (high memory) can
execute the code in user mode (low memory). PXN is kernel protection against
ret2usr attack. If the CPU is running in the kernel mode, executing a code in
user space will trigger a page fault. It is just similar to SMEP in linux. Keep
in mind that PXN bits are set in user virtual address space, which means they
are dynamically allocated, so unlike kernel ones, you will need a good kernel
read bug to locate the entry to be manipulated. Usually with both read and
write, there is really no need of code execution. So this is not very
practicable in real exploit.
PAN means it prevents privileged kernel
access to user data. There's software-based PAN implementation for armv7 with
config CPU_SW_DOMAIN_PAN and arm64 with config CONFIG_ARM64_SW_TTBR0_PAN. It
increases kernel security by ensuring that normal kernel accesses are unable to
access userspace addresses. This can
help prevent use-after-free bugs becoming an exploitable privilege escalation
by ensuring that magic values (such as LIST_POISON) will always fault when
dereferenced. It is the part of ARMv8.1 Extensions that adds bit PSR_PAN_BIT to
control with the config ARM64_PAN. As we see software-based PAN implementation
is common in arm64, so we have detailed description about it.
Register ttbr0_el1 stores the user page
table. In __uaccess_ttbr0_disable, if we disable the ttbr0_el1, then we cannot
access the usermod and the right usermod page table base addr is stored in
thread_info. On the other hand, uaccess_ttbr0_enable can recover ttbr0_el1 from
thread_info, then we can access user page table from kernel state, PAN is
disabled. Only open the attribute in copy_from_user and copy_to_user,
kernel_entry and kernel_exit. It is sure that ttbr0_el1 is also stored in
Address Space Layout Randomization (KASLR)
Android has included support for Address
Space Layout Randomization (ASLR) for years. Randomizing memory layout makes
code reuse attacks probabilistic and therefore more difficult for an attacker
to exploit, especially remotely. Android 8.0 brings this feature to the kernel.
While Linux has supported KASLR on x86 since version 3.14, KASLR for ARM64 has
only been available upstream since Linux 4.6. Android 8.0 makes KASLR available
in Android kernels 4.4 and newer.
KASLR helps mitigate kernel
vulnerabilities by randomizing the location where kernel code is loaded on each
boot. On ARM64, for example, it adds 13–25 bits of entropy depending on the
memory configuration of the device, which makes code reuse attacks more
Usercopy functions are used by the kernel
to transfer data from user space to kernel space memory and back again. Since
2014, missing or invalid bounds checking has caused about 45% of Android's
kernel vulnerabilities. Hardened usercopy adds bounds checking to usercopy
functions, which helps developers spot misuse and fix bugs in their code. Also,
if obscure driver bugs slip through, hardening these functions prevents the
exploitation of such bugs.
The final hardening feature extends
existing memory protections in the kernel by creating a memory region that's
marked read-only after the kernel has been initialized. This makes it possible
for developers to improve protection on data that needs to be writable during
initialization, but shouldn't be modified after that. Having less writable
memory reduces the internal attack surface of the kernel, making exploitation
Post-init read-only memory was introduced
in upstream kernel version 4.6 and we have backported it to Android kernels
3.18 and newer. While we have applied these protections to some data structures
in the core kernel, this feature is extremely useful for developers working on
kernel drivers. This measure is just a mitigation of dirtycow bug which
exploits the android device using the way of vdso.
A simpler approach to integrity
management is the dm-verity module. This
is a device mapper target which manages file integrity at the block level. It’s intended to be used as part of a
verified boot process, where an appropriately authorized caller brings a device
online, say, a trusted partition containing kernel modules to be loaded
later. The integrity of those modules
will be transparently verified block by block as they are read from disk.
Linux kernel has utilized many features
to harden the kernel. One of them is protecting critical memory zones like
kernel text and non-volatile data. Recent Linux mainline kernel has this
feature implemented through CONFIG_DEBUG_RODATA. It invokes mark_rodata_ro
function to allocate a section page ahead of kernel text Kernel text starts
relatively at a fixed location for most of the devices, so we can predict the
beginning of this critical kernel data structure.
KNOX Active Protection
TrustZone is an extension to ARM cores,
which creates two “worlds”. Although few
restrictions are there that how vendor can utilize Trustzone, usually the
feature-rich OS, aka Android, in our case, is going to run in the normal world.
The secure world will be hosting trustlets on a light-weight OS.
As a secure world running in parallel
with the normal world, compromising the kernel in normal world shall not affect
the secure world if the implementation was properly done, as their
communication is handled by the privileged monitor mode code usually loaded by
low-level bootrom. However, there are cases seen that secure world can also be
compromised due to its own bug or bugs in monitor mode.
Samsung has implemented TIMA(Trust Zone
based Integrity Measurement Architecture) which can provide complete protection
in real time. Even though an attack can compromise kernel and gain arbitrary
read/write, he/she can still not load any kernel module for convenient kernel
also introduces TIMA protected page table and read-only kernel text/data into
the kernel in Galaxy S5. In Galaxy S6/S6 Edge since 5.1.1, Samsung did not only
protect the page table, but also put crucial kernel objects like credentials
into consideration such as kmem_cache. If it is readable, this helps mitigating
conventional DKOM exploit.
Real-time kernel protection (RKP) is
implemented in Trust Zone or hypervisor, depends on device model with the
config CONFIG_TIMA_RKP CONFIG_RKP_DKP. It completely prevents kernel data
running unauthorized privileged code and prevents kernel data from being
directly accessed by user processes and monitors some critical kernel data
structures to verify that they are not exploited by attacks.
Its main function is rkp_call() which is
called by many critical kernel functions than manages slab allocation and
de-allocation, page table operations and copy/override/commit creds. It
provides kernel data protection based on read only pages. Important data can be
set read only after initialized. In previous version s6, just call rkp_override_creds()
to override current process's credentials. This is not working in s7 and adds
In knox 2.6, it adds verifying in
rkp_override_creds() such as part of data flow integrity and uid chek. Uid_checking is unprivileged
process(uid>1000) cannot override the credential with high privilege (uid
0~1000). DFI(data flow integrity) with config CONFIG_RKP_DKP will check whether
credential and task_struct is of own. We cannot manipulate credentials and
security object in kernel mode, point current credential to init_cred and call
rkp_override_creds function to ask secure world to help overide credential with
uid(0~1000). It also has enhanced selinux. The init process and u:rinit:s0 is
still limited by selinux and almost do nothing. It disables CONFIG_SECURITY_SELINUX_DEVELOP
thus cannot disable selinux by override selinux_enforcing. Selnux is statically
enforcing with removing the flag AVD_FLAGS_PERMISSIVE all the time. Init
process cannot reload seliux poliy after system initialized and permissive domain
is not allowed.
In knox 2.8, there is more security than
knox 2.6. The load_elf_binary function checks if uid<1000 and the bin file
is not from / and /system directory, the kernel will panic. For the reason, we
cannot invoke __orderly_poweroff function to start a kernel thread.
Besides these “high-end” mitigations,
Samsung also customized some syscalls to restrict post-exploit activities.
Taking fork/execve as an example. These two are basically the underlying
syscalls behind “system”, a very common routine an exploit will utilize after
privilege escalation. So Samsung added some additional check in execve
For any root process, sec_restrict_fork()
will check if it is originated from /data. In general, this directory is the
only place that an user application can start in. Samsung is hoping that this
can stop rooting applications from spawning new process, in most cases a daemon
running as root. But since they failed to protect some critical data structures
being used inside sec_restrict_fork(), bypassing this check if far easiler than
bypassing a tz/hyp assisted protection.
pipe read and write
YunOS, a customized Android ROM from
Alibaba, one of the generic route we take for rooting is by modifying the
addr_limit of current task’s thread_info structure. This allows the kernel to
take the whole virtual address space (or sometimes just enough virtual address
space) as “USER_DS” so read/write operation in the address range won’t be
restricted for certain syscalls, like pipe_read and pipe_write. In the kernel
of YunOS, YunOS added one additional ext_security_pre_check before actually
heading into the syscall. The first basic block extracts addr_limit from
current context and check against the standard USER_DS value, 0x8000000000 (1
<< 39). If it is not the desired value, it will enforce a SIGKILL to the
calling process. Since SIGKILL can’t be masked, the calling process will be
forcibly killed (which may lead to a panic if in the middle of exploit).
Google's Pixel 3 will be the first
Android device to ship with LLVM's forward-edge Control Flow Integrity (CFI)
enforcement in the kernel, and they have made CFI support available in Android
kernel versions 4.9 and 4.14.
A huge number of overwriting a function
pointer to complete their exploit exists in mobile devices.CFI attempts to
mitigate these attacks by adding additional checks to confirm that the kernel's
control flow stays within a precomputed graph which just makes exploiting such
a bug more difficult in practice.
LLVM's CFI implementation adds a check
before each indirect branch to confirm that the target address points to a
valid function with a correct signature. This prevents an indirect branch from
jumping to an arbitrary code location and even limits the functions that can be
In order to support loadable modules,
they have implemented LLVM's cross-DSO CFI support in the kernel, including a
CFI shadow that speeds up cross-module look-ups. When compiled with cross-DSO
support, each kernel module contains information about valid local branch
targets, and the kernel looks up information from the correct module based on
the target address and the modules' memory layout.
The following kernel configuration
options are needed to enable kernel CFI:
Using CONFIG_CFI_PERMISSIVE=y may also
prove helpful when debugging a CFI violation or during device bring-up. This
option turns a violation into a warning instead of a kernel panic. However, it
hasn't adopted measures to protect function return addresses.
In some devices like samsung, huawei, they
just implemented simple KCFI. In every function end, they put a fixed value.
When jumping to another function, firstly it jumps a fixed code and check
whether the last code is at the start of a function. If yes, then jump into the
right function in case that attacks abuse function pointer to exploit the