diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 91c0f338a63..9b08154b88d 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -436,6 +436,7 @@ - [Android Anti Instrumentation And Ssl Pinning Bypass](mobile-pentesting/android-app-pentesting/android-anti-instrumentation-and-ssl-pinning-bypass.md) - [Android Application Level Virtualization](mobile-pentesting/android-app-pentesting/android-application-level-virtualization.md) - [Android Applications Basics](mobile-pentesting/android-app-pentesting/android-applications-basics.md) + - [Android SELinux Policy Analysis](mobile-pentesting/android-app-pentesting/android-selinux-policy-analysis.md) - [Android Enterprise Work Profile Bypass](mobile-pentesting/android-app-pentesting/android-enterprise-work-profile-bypass.md) - [Android Hce Nfc Emv Relay Attacks](mobile-pentesting/android-app-pentesting/android-hce-nfc-emv-relay-attacks.md) - [Android Physical Attacks](mobile-pentesting/android-app-pentesting/android-physical-attacks.md) diff --git a/src/mobile-pentesting/android-app-pentesting/README.md b/src/mobile-pentesting/android-app-pentesting/README.md index 4336bcabbff..8949835395d 100644 --- a/src/mobile-pentesting/android-app-pentesting/README.md +++ b/src/mobile-pentesting/android-app-pentesting/README.md @@ -31,6 +31,7 @@ Sometimes it is useful to **modify application code** to access **hidden informa ## Other interesting tricks +- [Android SELinux live-policy analysis and attack-path mapping](android-selinux-policy-analysis.md) - [Spoofing your location in Play Store](spoofing-your-location-in-play-store.md) - [Play Integrity attestation spoofing (SafetyNet replacement)](play-integrity-attestation-bypass.md)[[1]](#references) - [Android app-level virtualization / app cloning abuse & detection](android-application-level-virtualization.md) diff --git a/src/mobile-pentesting/android-app-pentesting/android-selinux-policy-analysis.md b/src/mobile-pentesting/android-app-pentesting/android-selinux-policy-analysis.md new file mode 100644 index 00000000000..2b9aa0f23cf --- /dev/null +++ b/src/mobile-pentesting/android-app-pentesting/android-selinux-policy-analysis.md @@ -0,0 +1,163 @@ +# Android SELinux Policy Analysis + +{{#include ../../banners/hacktricks-training.md}} + +Android 8+ builds SELinux from independently maintained platform, `system_ext`, product, vendor and ODM components. `init` either validates and loads a precompiled policy or compiles the CIL inputs before loading one binary policy into the kernel. Consequently, `/sys/fs/selinux/policy` is the authoritative target for access-path analysis; reviewing only AOSP `.te` files or one partition's CIL can miss compatibility mappings, OEM rules and runtime injection.[[1]](#references)[[8]](#references) + +## Treble policy layout + +The most useful on-device artifacts are distributed across these locations.[[1]](#references)[[2]](#references) + +```text +/system/etc/selinux/ # platform policy, contexts, mappings, bug_map +/system_ext/etc/selinux/ # system_ext policy and contexts +/product/etc/selinux/ # product policy and contexts +/vendor/etc/selinux/ # OEM policy, contexts and precompiled policy +/odm/etc/selinux/ # device-specific policy and contexts +/sys/fs/selinux/policy # live merged binary policy +``` + +Platform **public** types form the API that vendor policy may reference; platform-private types are implementation details. Compatibility CIL maps old versioned public attributes to current concrete types, allowing a newer platform to run with an older vendor partition. Vendor-defined types should normally use the `vendor_` namespace.[[1]](#references)[[2]](#references) + +## Extract the effective policy + +[`setools-android`](https://github.com/xmikos/setools-android) provides Android builds of `seinfo` and `sesearch`. Reading the live policy normally requires root; perform collection on an authorized research device and remember that Magisk, KernelSU, APatch or a policy-injection module may already have added domains and rules. Record both a live dump and the partition inputs so the effective policy can be compared with a stock image.[[3]](#references)[[8]](#references) + +```bash +adb shell getenforce +adb shell id -Z +adb shell su -c 'cat /sys/fs/selinux/policy' > live-policy +adb pull /system/etc/selinux/ system-selinux/ +adb pull /system_ext/etc/selinux/ system-ext-selinux/ +adb pull /product/etc/selinux/ product-selinux/ +adb pull /vendor/etc/selinux/ vendor-selinux/ +adb pull /odm/etc/selinux/ odm-selinux/ +``` + +If `adb pull` skips a context or XML file, stream it through the privileged shell instead. Establish a baseline and identify permissive domains before interpreting individual rules.[[8]](#references) + +```bash +adb exec-out su -c 'cat /system/etc/selinux/plat_seapp_contexts' > plat_seapp_contexts +adb shell su -c '/data/local/tmp/seinfo --stats /sys/fs/selinux/policy' +adb shell su -c '/data/local/tmp/seinfo --permissive /sys/fs/selinux/policy' +``` + +An unexpected permissive domain on a production image is important because denials for that domain are audited but not enforced. However, a permissive domain or unusually high rule count on a rooted test phone may belong to the rooting framework rather than the stock firmware. Older Android SETools ports can also report incomplete statistics for modern policy features, so confirm surprising counts with `sesearch` results or a second parser rather than interpreting them as absence.[[8]](#references) + +### `neverallow` is a build assertion + +AOSP `neverallow` rules constrain policy compilation and compatibility testing; they are not decision rules preserved in the loaded binary policy. Therefore, an empty `Neverallow` count in `seinfo --stats` is expected, and a live-policy injector can add an `allow` that the original source policy would have rejected. Treat a stock-to-live policy diff—not the source `neverallow` set—as the runtime integrity check.[[4]](#references)[[8]](#references) + +## Query from the compromised domain + +Start with the exact domain obtained from `id -Z` or `/proc//attr/current`; Android has multiple app domains, including target-SDK-specific `untrusted_app_*`, isolated-app and SDK-sandbox domains, so blindly querying `untrusted_app` can omit the real path. The following examples use `untrusted_app`; replace it with the target domain.[[5]](#references)[[8]](#references) + +```bash +P=/sys/fs/selinux/policy +S=/data/local/tmp/sesearch +adb shell su -c "$S -A -s untrusted_app $P" +adb shell su -c "$S -A -s untrusted_app -c file $P" +adb shell su -c "$S -A -s untrusted_app -c chr_file $P" +adb shell su -c "$S -A -s untrusted_app -c process $P" +adb shell su -c "$S -A -s untrusted_app -c process -p transition $P" +``` + +Enumerate cross-domain IPC separately because it often supplies the first edge from an app to a privileged parser or service.[[8]](#references) + +```bash +adb shell su -c "$S -A -s untrusted_app -c binder $P" +adb shell su -c "$S -A -s untrusted_app -c unix_stream_socket -p connectto $P" +adb shell su -c "$S -A -s untrusted_app $P" | grep vendor_ +``` + +Then search globally for permissions which make a compromised target domain valuable. These results are leads, not vulnerabilities: correlate every rule with an object label and a reachable code path.[[8]](#references) + +```bash +for cap in sys_admin sys_rawio sys_ptrace sys_module; do + adb shell su -c "$S -A -c capability -p $cap $P" +done +adb shell su -c "$S -A -c file -p execute $P" | grep -E 'data_file|tmpfs' +adb shell su -c "$S -A -t proc_mem $P" | grep vendor_ +adb shell su -c "$S -A -t kmsg_device $P" | grep vendor_ +``` + +High-value findings include broad `chr_file` access with `ioctl`, privileged Binder or Unix-socket targets, executable writable storage, sensitive persistent files and powerful Linux capabilities. An `allow` does not create code execution by itself; its offensive significance is that code execution in the source domain inherits that authority and may reach a second vulnerable driver, HAL or service.[[8]](#references) + +## Convert rules into an attack-path graph + +Model **domains and labeled objects as nodes** and permitted operations as directed edges. This avoids treating a large list of allows as equally important and exposes multi-stage chains.[[8]](#references) + +1. Fix the starting domain and confirm its real context on the target process. +2. Enumerate files, device nodes, Binder services, Unix sockets and allowed transitions reachable from it. +3. Resolve each result to the concrete object (`ls -Z`, `service list`, context files and device-node major/minor values); attributes returned by `sesearch` can represent many concrete types. +4. Check whether attacker-controlled data reaches code in the target service or driver. +5. Repeat the policy query with each reachable service/HAL domain as the new source. +6. Prioritize chains ending in driver IOCTLs, process-memory access, module loading, raw hardware access, sensitive writes or a domain transition. + +Vendor policy deserves special attention because OEM HALs and proprietary daemons frequently connect application-reachable IPC to device-specific kernel drivers. The useful finding is the conjunction **reachable entry point + exploitable implementation + policy permission to reach the next target**, not an overbroad rule in isolation.[[2]](#references)[[8]](#references) + +## Android 16 portability checks + +Starting with vendor API level `202504`, `genfscon` labels can be selected by `BOARD_GENFS_LABELS_VERSION`, stored in `/vendor/etc/selinux/genfs_labels_version.txt`. Before assuming the type of a `/proc` or `/sys` path, read that value and inspect the matching `plat_sepolicy_genfs_.cil`; the same path can receive a different type when paired with another vendor partition.[[2]](#references)[[8]](#references) + +```bash +adb shell cat /vendor/etc/selinux/genfs_labels_version.txt +adb shell ls '/system/etc/selinux/plat_sepolicy_genfs_*' +adb shell cat /vendor/etc/selinux/plat_sepolicy_vers.txt +adb shell cat /sys/fs/selinux/policyvers +``` + +`/sys/fs/selinux/policyvers` reports the maximum binary-policy version supported by the kernel; it is not the Treble platform-policy identifier and does not necessarily equal the version of the loaded binary. Resolve versioned types through `/system/etc/selinux/mapping/*.cil` rather than stripping their suffixes by assumption.[[2]](#references)[[8]](#references) + +Android 16 also adds policy selectors for `sdk_sandbox_next`, `sdk_sandbox_audit` and the privileged virtualization terminal domain `vmlauncher_app`. Query these as distinct domains: `sdk_sandbox_audit` uses `auditallow` telemetry to measure currently permitted accesses, while `vmlauncher_app` has virtualization-service and VM-management reachability. Such rules identify review targets and release-portability risks, but do not prove exploitation.[[6]](#references)[[7]](#references)[[8]](#references) + +```bash +grep -E 'sdk_sandbox_(next|audit)|vmlauncher_app' plat_seapp_contexts +P=/sys/fs/selinux/policy +S=/data/local/tmp/sesearch +adb shell su -c "$S -A -s sdk_sandbox_audit $P" +adb shell su -c "$S -A -s vmlauncher_app $P" +adb shell cat /system/etc/selinux/bug_map +adb shell cat /vendor/etc/selinux/selinux_denial_metadata +``` + +`bug_map` and `selinux_denial_metadata` map denial tuples to issue identifiers. Use them to prioritize components with known policy friction, not as evidence that the denied operation is allowed or that the target is exploitable.[[8]](#references) + +## Kernel primitive versus SELinux data-only bypass + +Modern Android kernel mitigations mostly obstruct the conversion of memory corruption into stable kernel read/write: MTE/hardware-tagged KASAN can detect stale or incorrectly tagged accesses, SCS and PAC protect return-oriented control flow, and kCFI validates indirect-call target types. They do not independently validate every legitimate-looking write after an attacker already owns an arbitrary kernel read/write primitive.[[8]](#references) + +This distinction matters when evaluating a proposed final action.[[8]](#references)[[9]](#references)[[10]](#references) + +| Final action after kernel R/W | Relevant constraint | +| --- | --- | +| Unlink an SELinux LSM hook | Data-only list mutation; no invalid indirect call is made, so kCFI is not directly triggered. | +| Replace an LSM hook pointer | The destination must satisfy the call site's kCFI type check and may also be subject to pointer authentication. | +| Change policy/AVC data | Control-flow mitigations do not directly protect the semantic integrity of these data structures. | +| Change the enforcing field | Build-specific: GKI exposes `selinux_state`, but when `CONFIG_SECURITY_SELINUX_DEVELOP` is disabled, `enforcing_enabled()` returns `true` regardless of that field. | + +Do not hard-code an obsolete `selinux_enforcing` global or structure offset. Recover symbols and layouts from the exact kernel build, account for KASLR and `__randomize_layout`, and check the SELinux configuration before selecting a target. The AVC implementation uses 512 buckets in the reviewed kernel, but occupancy and chain length are dynamic and can be inspected through `/sys/fs/selinux/avc/hash_stats`.[[9]](#references)[[11]](#references)[[8]](#references) + +```bash +adb shell zcat /proc/config.gz | grep -E 'SELINUX_DEVELOP|CFI_CLANG|PTR_AUTH|MTE|KASAN_HW_TAGS|SHADOW_CALL_STACK' +adb shell cat /sys/fs/selinux/avc/hash_stats +adb shell grep -E ' selinux_state$| selinux_enforcing' /proc/kallsyms +``` + +Zeroed `/proc/kallsyms` addresses are normal on production-like builds, so a real chain needs an independent disclosure, KASLR bypass or other symbol-location technique. Hypervisor-backed integrity mechanisms are qualitatively different from compiler and allocator hardening because they can restrict modifications to protected kernel data even after the guest kernel is compromised.[[8]](#references) + +## References + +- [1] [AOSP - Build SELinux policy](https://source.android.com/docs/security/features/selinux/build) +- [2] [AOSP - SELinux policy compatibility](https://source.android.com/docs/security/features/selinux/compatibility) +- [3] [setools-android](https://github.com/xmikos/setools-android) +- [4] [AOSP - Customize SELinux and neverallow rules](https://source.android.com/docs/security/features/selinux/customize) +- [5] [AOSP system/sepolicy - Application domain overview](https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/main/README.apps.md) +- [6] [AOSP system/sepolicy - vmlauncher_app policy](https://android.googlesource.com/platform/system/sepolicy/+/08f7c99456097c9214fd8270ff2e61a60b724934/private/vmlauncher_app.te) +- [7] [AOSP system/sepolicy - SDK sandbox audit policy](https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/main/private/sdk_sandbox_audit.te) +- [8] [8kSec - Android SELinux Internals Part IV: Policy Analysis, Kernel Mitigations, and Android 16 Changes](https://8ksec.io/android-selinux-internals-part-iv) +- [9] [Android GKI 6.6 - SELinux state and enforcing helpers](https://android.googlesource.com/kernel/common/+/refs/heads/android15-6.6-lts/security/selinux/include/security.h) +- [10] [Android GKI 6.6 - LSM hook dispatch](https://android.googlesource.com/kernel/common/+/refs/heads/android15-6.6-lts/security/security.c) +- [11] [Android GKI 6.6 - SELinux AVC implementation](https://android.googlesource.com/kernel/common/+/refs/heads/android15-6.6-lts/security/selinux/avc.c) + +{{#include ../../banners/hacktricks-training.md}}