Introduction: The Fortress of Android SELinux
Android’s security model heavily relies on Security-Enhanced Linux (SELinux), a mandatory access control (MAC) system that dictates what processes can access what resources. Far beyond traditional Unix discretionary access control (DAC), SELinux enforces fine-grained permissions based on security contexts assigned to every process and file. While designed to contain threats and enforce the principle of least privilege, SELinux policies are complex and can sometimes harbor misconfigurations or unintended permissions, creating pathways for privilege escalation. This article delves into the art of reverse engineering Android SELinux policies to identify such vulnerabilities.
Deconstructing Android’s Security Policy
Understanding SELinux Fundamentals
At its core, SELinux operates on a ‘default deny’ principle, meaning any action not explicitly allowed by the policy is forbidden. Key concepts include:
- Subjects (Domains): Processes running with a specific SELinux type, often referred to as a domain (e.g.,
untrusted_app,system_app,init). - Objects: Resources being accessed, such as files, directories, sockets, devices, or IPC mechanisms.
- Types: Labels assigned to objects and subjects (e.g.,
app_data_file,system_server_domain). - Classes: Categories of objects (e.g.,
file,dir,socket,process). - Rules (AVCs): Allowances or denials defined in the policy (e.g.,
allow source_domain target_type:class { permissions };). - Type Transition: Rules that automatically assign a new type to a newly created object based on the creating process’s domain and the parent directory’s type. This is a critical area for exploitation.
Acquiring and Dissecting the Policy
The Android SELinux policy is compiled into a binary format and typically resides within the boot.img or as /sys/fs/selinux/policy on a running device. To begin our analysis, we first need to extract this policy:
- Root the device or unlock the bootloader: Access to
boot.imgor root privileges is usually required. - Pull the policy from a running device (requires root):
adb root adb disable-verity adb reboot adb pull /sys/fs/selinux/policy policy_raw - Extract from
boot.img(for AOSP or custom ROMs): If you have theboot.img, you can use tools likeAOSP bootimg toolsorMagisk's unpackbootimgto extract the SELinux policy. Once extracted, you’ll have a raw policy file. - Convert to a human-readable format: Use tools like
sepolicy-analyze(part of AOSP) orapol(from setools) to decompile the binary policy into a more readable text format or query it.# Using sepolicy-analyze (from AOSP source/prebuilt) sepolicy-analyze -p policy_raw policy.conf # Or using apol from setools # apol -P policy_raw -o policy.txt
The resulting policy.conf or policy.txt file contains thousands of rules. This is where the real reverse engineering begins.
Unearthing Policy Vulnerabilities: A Methodical Approach
Type Transition Weaknesses: The Trojan Horse Gambit
type_transition rules are often overlooked during policy audits, but they can be a goldmine for privilege escalation. A vulnerable type_transition allows a less privileged domain to create a file or directory that automatically receives a more privileged type, provided it’s created in a specific location by a specific source domain. If a privileged service later interacts with this mislabeled file, an attacker can leverage it.
Look for rules where an unprivileged source domain (e.g., untrusted_app) can create an object that transitions to a privileged target type (e.g., system_file, shell_exec, service_data_file). Example of a potentially vulnerable rule snippet:
type_transition untrusted_app system_tmp_dir:{ file } system_file;
If untrusted_app has write access to system_tmp_dir, and a privileged service (e.g., system_server) executes files labeled system_file from that directory, an attacker could plant a malicious executable.
Use sepolicy-analyze to query for type transitions:
sepolicy-analyze type_transition -s untrusted_app -t system_file -c file
sepolicy-analyze type_transition -s untrusted_app -t shell_exec -c file
Permission Oversights and Over-Privileged Domains
Broad permissions granted to specific domains or types can also introduce vulnerabilities. Pay close attention to permissions like:
execmod: Allows execution of code and modification of its text, potentially leading to dynamic code loading exploits.ioctl: Can allow unprivileged processes to perform arbitrary kernel operations if not properly constrained.setattr: If a process can change the attributes (including type) of a file, it can potentially elevate its context.entrypointandexec_type: Crucial for understanding how a process can execute files with a different SELinux context. A policy might allow a low-privilege domain to execute a file type that becomes privileged upon execution.
Query specific domain permissions:
sepolicy-analyze perm_summary -d untrusted_app
sepolicy-analyze perm_summary -d system_app
sepolicy-analyze allows -s untrusted_app -t system_server_domain -c process
Analyzing Privileged Daemon Interactions
Many critical Android services run as highly privileged SELinux domains (e.g., system_server, zygote, various native daemons). Understanding how these services interact with less privileged domains, especially regarding file creation and execution, is vital. Look for scenarios where:
- A privileged daemon creates files in world-writable directories that could then be overwritten or influenced by an unprivileged app.
- A privileged daemon attempts to open or execute files based on user input or data from an unprivileged source, without proper type checking or validation.
Crafting an Exploit: From Policy Weakness to Privilege Escalation
Scenario: Escalating from an Untrusted App
Let’s consider a hypothetical exploit chain:
- An unprivileged application (
untrusted_appdomain) identifies a writable directory (e.g.,/data/local/tmpor a specific service’s cache directory) where atype_transitionrule exists. - The rule states:
type_transition privileged_daemon_domain some_writable_dir_type:{ file } shell_exec; - The
untrusted_appcreates a malicious shell script or binary insome_writable_dir_type. Because theprivileged_daemon_domainis the one creating the file (or some interaction leads to it), the file now gets theshell_exectype. - A different, more privileged service (or the same
privileged_daemon_domainitself) is found to execute any file labeledshell_execin that directory, perhaps as part of a cleanup routine or a processing task. - The malicious payload is executed with the privileges of the executing service, leading to privilege escalation.
Illustrative Code Example (Conceptual Payload)
A simple C payload compiled for ARM/ARM64 that attempts to gain root and execute a shell command:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
printf("[*] Malicious payload started.n");
// Attempt to drop privileges if running as root initially
// or verify effective UID/GID
uid_t euid = geteuid();
gid_t egid = getegid();
printf("[*] Current Effective UID: %d, Effective GID: %dn", euid, egid);
if (euid != 0 || egid != 0) {
printf("[!] Not running as root. This exploit might not succeed.n");
// If we want to try to elevate to root from a privileged context
// setuid(0) and setgid(0) can be called if capabilities allow.
}
// Execute a privileged command, e.g., remounting /system writable
// In a real scenario, this would be more complex, e.g., spawning a reverse shell.
printf("[*] Attempting to execute privileged command.n");
if (execl("/system/bin/sh", "sh", "-c", "/system/bin/mount -o remount,rw /system && echo 'PWNED BY SELINUX' > /system/pwned.txt", (char *)NULL) == -1) {
perror("execl failed");
return 1;
}
return 0;
}
This C code would be compiled into an executable and placed in the vulnerable directory, relying on the SELinux policy’s weakness to be executed with higher privileges.
Mitigation and Prevention Strategies
For policy developers, the key is strict adherence to the principle of least privilege. Regular audits of the compiled policy, especially for type_transition rules and broad permissions, are crucial. Using neverallow rules strategically can prevent certain unintended permissions from ever being granted. Automation with tools like audit2allow should be used cautiously and validated manually to prevent introducing new vulnerabilities.
Conclusion
Android SELinux is a powerful security mechanism, but its complexity opens avenues for subtle misconfigurations. By understanding the policy structure, leveraging specialized analysis tools, and methodically searching for type transition weaknesses and permission oversights, security researchers can uncover critical privilege escalation vulnerabilities. Reverse engineering SELinux policy is an essential skill for anyone serious about Android security research and exploitation.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →