Android Hacking, Sandboxing, & Security Exploits

Reverse Engineering Android SELinux: Unearthing Policy Flaws for Exploitation

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to Android SELinux and its Role

Android’s security architecture relies heavily on Security-Enhanced Linux (SELinux), a mandatory access control (MAC) system that operates alongside traditional discretionary access control (DAC). Introduced in Android 4.3, SELinux enforces fine-grained permissions on processes, files, and other system resources, effectively compartmentalizing the operating system and limiting the damage potential of compromised applications. Every process runs within a specific SELinux domain, and every file or resource has an associated SELinux type. Interactions between domains and types are governed by a strict policy, which is loaded at boot time.

While SELinux significantly enhances Android’s security posture, its complexity means that policy misconfigurations or overly broad rules can introduce vulnerabilities. Reverse engineering these policies is a critical skill for security researchers and penetration testers aiming to uncover privilege escalation paths, sandbox escapes, and other exploitation opportunities.

Understanding Android SELinux Policy Structure

An Android SELinux policy is a collection of rules written in a specialized language, defining types, attributes, domains, and the allowed interactions between them. Key components include:

  • Types: Labels applied to objects (files, processes, sockets) representing their security context.
  • Domains: Special types applied to processes, defining their execution context.
  • Classes: Categories of system objects (e.g., file, process, socket, service).
  • Permissions: Actions that can be performed on objects (e.g., read, write, execute, bind).
  • Rules: Directives like allow, neverallow, type_transition, role, defining the policy logic.

Our goal in reverse engineering is to analyze these rules to find weaknesses that a malicious actor or a compromised application could exploit to gain unauthorized access or elevate privileges beyond its intended scope.

Extracting the SELinux Policy from an Android Device

Before analysis, we need the policy file itself. There are two primary methods to obtain it:

1. From a Running Device (Root Required)

If you have root access to an Android device, the active policy can be pulled directly from the `/sys` filesystem.

adb shellsu -c 'cat /sys/fs/selinux/policy > /sdcard/sepolicy'adb pull /sdcard/sepolicy .

This method provides the currently loaded policy, which might include dynamic modifications made at runtime, although these are typically minimal.

2. From the Boot Image

For a more comprehensive analysis, extracting the policy from the boot image is often preferred, as it represents the policy shipped with the device firmware. The SELinux policy is typically located within the ramdisk portion of the boot.img or vendor_boot.img (for devices using A/B partitions).

  1. Obtain the Boot Image: Download the factory image for your device, or extract boot.img/vendor_boot.img from a device backup.
  2. Unpack the Boot Image: Use tools like magiskboot or AOSP bootimg tools to unpack the image.
  3. magiskboot unpack boot.img
  4. Locate the Policy: The policy file is usually named sepolicy and found in the root of the extracted ramdisk (e.g., ramdisk/sepolicy).

Analyzing the SELinux Policy for Flaws

Once you have the sepolicy file, specialized tools are essential for parsing and querying its complex structure.

1. Using sepolicy-analyze (from AOSP/SETools)

sepolicy-analyze is a powerful command-line utility from the AOSP source or the setools suite. It allows detailed queries about policy rules, domains, and types.

  • List all domains:
    sepolicy-analyze -s sepolicy_file domain_list
  • List all types:
    sepolicy-analyze -s sepolicy_file type_list
  • Query permissions for a specific domain and target type:

    This is crucial for understanding what a process (domain) can do to a resource (type). For example, checking what untrusted_app (a common domain for installed apps) can do to zygote:

    sepolicy-analyze -s sepolicy_file query perms untrusted_app zygote
  • Find all `allow` rules involving a specific type:

    Useful for understanding how a particular resource is accessed.

    sepolicy-analyze -s sepolicy_file query rules type_name

2. Using apol (SETools)

apol provides a graphical interface for exploring SELinux policies, which can be very helpful for visualizing relationships between types, domains, and rules.

apol sepolicy_file

Within apol, you can navigate through domains, types, and rules, inspect their attributes, and view the graph of allowed transitions and accesses. This visual representation often helps in spotting unusual or overly broad permissions quickly.

Identifying Common Policy Flaws for Exploitation

When analyzing policies, look for patterns that could be exploited:

1. Overly Permissive allow Rules

An allow rule that grants excessive permissions can be a direct path to exploitation. For example, an application’s domain (e.g., untrusted_app) should not have write access to critical system files or executable memory.

# Example of a potentially dangerous allow ruleallow untrusted_app system_file:file { execute execute_no_trans read write entrypoint };

If untrusted_app can write to a system_file and then execute it, it can achieve arbitrary code execution as the system_server (if system_file is actually accessible to untrusted_app and writable in a way that allows a type transition or direct execution).

2. Type Confusion and Mislabeling

Sometimes, system resources are mislabeled with an incorrect SELinux type, granting a less privileged domain access it shouldn’t have. For instance, if a critical system daemon’s configuration file is accidentally labeled as app_data_file instead of a more restrictive type, an untrusted app might gain read/write access.

# Scenario: Configuration file for a privileged service is mislabeled.adb shell ls -Z /data/misc/service_configs/critical_service.confu:object_r:app_data_file:s0 /data/misc/service_configs/critical_service.conf

An untrusted_app often has broad permissions over app_data_file, potentially leading to configuration tampering for a privileged service.

3. Improper Domain Transitions and Privileged Executables

The type_transition rule allows a process to transition to a new SELinux domain when executing a specific file. If a low-privileged domain can execute a file that transitions it to a high-privileged domain, this is a clear path to privilege escalation.

# Example of a dangerous type_transition rule.type_transition untrusted_app system_file:process system_server;

This rule would allow an untrusted_app to transition to the system_server domain merely by executing a system_file labeled as a process, effectively granting it root-like privileges.

Another common flaw involves the execmem permission. If a domain (especially a low-privileged one) has execmem, it can execute code from memory segments it has allocated and written to. This is a powerful primitive for arbitrary code execution.

# Query for execmem permissions for untrusted_appsepolicy-analyze -s sepolicy query perms untrusted_app self:process execmem

If this returns an allow rule, it means untrusted_app can use mmap with PROT_EXEC, which is often a critical step in traditional memory corruption exploits to gain code execution.

A Practical Scenario: Exploiting a Permissive `execmem`

Scenario Setup

Imagine we have an untrusted_app that discovers a policy flaw: its domain, untrusted_app, is explicitly allowed execmem permission.

allow untrusted_app self:process { execmem };

Policy Discovery

Using sepolicy-analyze, we confirm the permission:

sepolicy-analyze -s sepolicy_file query perms untrusted_app self:process execmemFound rule:allow untrusted_app untrusted_app:process { execmem };

Exploitation Steps (Conceptual)

  1. Develop Shellcode: Create a small piece of ARM/ARM64 shellcode that performs a desired action, such as spawning a shell or writing to a protected file.
  2. /* Example: ARM64 shellcode to execute /system/bin/id */char shellcode[] = "x01x00x00x00"  // ADRP X0, #offset_to_string"xe2x03x00x91"  // ADD X0, X0, #offset_in_page"x00x00x00x00"  // STR X0, [SP, #0] (placeholder for syscall args)"x01x01x00xd4"  // SVC #0x80 (syscall number for execve)"x00x00x00x00"  // .string "/system/bin/id" (placeholder, adjust for actual string)
  3. Allocate Executable Memory: The Android application (running as untrusted_app) uses mmap to allocate a memory region with PROT_READ | PROT_WRITE | PROT_EXEC permissions. The execmem permission allows this.
  4. #include <sys/mman.h>void *mem = mmap(NULL, sizeof(shellcode), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (mem == MAP_FAILED) {    // Handle error}memcpy(mem, shellcode, sizeof(shellcode));((void (*)())mem)(); // Execute the shellcode
  5. Execute Shellcode: The application then casts the memory region to a function pointer and executes it. Because execmem is allowed, this operation succeeds, and the shellcode runs within the context of the untrusted_app domain. Depending on what the shellcode does, this can lead to further exploitation, such as gaining access to resources the untrusted_app should not control (e.g., if shellcode targets a vulnerable service or a mislabeled file).

Mitigation and Best Practices

For developers and system integrators, understanding these attack vectors is key to building more secure Android systems:

  • Principle of Least Privilege: Grant only the absolute minimum permissions required for a domain to function.
  • Strict Type Labeling: Ensure all files and resources are correctly labeled with the most restrictive types possible.
  • Avoid `execmem`: Explicitly deny execmem for all but the most trusted and critical components.
  • Regular Policy Audits: Periodically review and audit SELinux policies, especially after updates or custom modifications.
  • Automated Analysis Tools: Integrate tools like sepolicy-analyze into the CI/CD pipeline to catch overly permissive rules early.

Conclusion

Reverse engineering Android SELinux policies is a challenging but rewarding endeavor for security professionals. By systematically extracting and analyzing the policy rules, and understanding common misconfigurations, it’s possible to identify significant security vulnerabilities. The ability to unearth and understand these policy flaws provides crucial insights into the real-world security posture of Android devices and helps in developing more robust and resilient systems against sophisticated attacks.

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 →
Google AdSense Inline Placement - Content Footer banner