Advanced OS Customizations & Bootloaders

Hands-On Lab: Building an AppArmor Sandbox for a Vulnerable Android Native Service

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: AppArmor on Android

Android’s security model is robust, but specific vulnerabilities in native services can still pose risks. AppArmor, a Mandatory Access Control (MAC) system, offers a powerful way to enhance security by confining programs to a limited set of resources. This hands-on lab will guide you through building and enforcing an AppArmor sandbox for a hypothetical vulnerable Android native service, demonstrating how to mitigate potential exploits by restricting its capabilities.

Prerequisites and Environment Setup

To follow this lab, you’ll need:

  • A rooted Android device or emulator running a custom kernel with AppArmor enabled.
  • An AOSP (Android Open Source Project) build environment.
  • Basic knowledge of C/C++ and shell scripting.
  • Linux host machine with aa-genprof and aa-logprof (or equivalent manual tools) if you plan on profiling services interactively. For Android, manual profile crafting and dmesg analysis are often more practical.

Enabling AppArmor in the Android kernel involves configuring CONFIG_SECURITY_APPARMOR=y and other related options during kernel compilation. For a production environment, you’d integrate profile loading into the device’s init scripts.

The Vulnerable Service: A Hypothetical Scenario

Let’s imagine a simple Android native service, /vendor/bin/vulnerable_service, which performs some legitimate operations but also has a bug that allows an attacker to force it to attempt reading sensitive files outside its intended scope. For this lab, we’ll simulate this by having our service try to read /data/local/tmp/secret.txt, which it should *not* have access to.

Hypothetical Vulnerable Service (C Code)

Consider the following simplified C code for our vulnerable_service:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>int main(int argc, char *argv[]) {    printf("vulnerable_service: Starting up...n");    // Legitimate operation: create a log file    FILE *log_file = fopen("/data/local/tmp/vulnerable_service.log", "a");    if (log_file) {        fprintf(log_file, "Service started at %ldn", time(NULL));        fclose(log_file);        printf("vulnerable_service: Logged legitimate activity.n");    } else {        perror("vulnerable_service: Failed to open log file");    }    // SIMULATED VULNERABILITY: Attempt to read a forbidden file    // In a real exploit, this path might be controlled by attacker input.    const char *sensitive_file = "/data/local/tmp/secret.txt";    char buffer[256];    ssize_t bytes_read;    int fd = open(sensitive_file, O_RDONLY);    if (fd != -1) {        printf("vulnerable_service: Successfully opened sensitive file! (This should be blocked)n");        bytes_read = read(fd, buffer, sizeof(buffer) - 1);        if (bytes_read > 0) {            buffer[bytes_read] = '';            printf("vulnerable_service: Content of %s: %sn", sensitive_file, buffer);        }        close(fd);    } else {        perror("vulnerable_service: Failed to open sensitive file (Expected behavior if sandboxed)");    }    printf("vulnerable_service: Exiting.n");    return 0;}

Compile this code within your AOSP environment or cross-compile it for ARM/ARM64. Push it to the device:

# On your host machineaarch64-linux-android-gcc vulnerable_service.c -o vulnerable_serviceadb push vulnerable_service /data/local/tmp/adb shell "chmod 755 /data/local/tmp/vulnerable_service"# Create a dummy sensitive fileadb shell "echo 'TOP_SECRET_DATA' > /data/local/tmp/secret.txt"

Run it without AppArmor enforcement first to see the vulnerable behavior:

adb shell "/data/local/tmp/vulnerable_service"# You should see: "vulnerable_service: Successfully opened sensitive file!"# and "vulnerable_service: Content of /data/local/tmp/secret.txt: TOP_SECRET_DATA"

AppArmor Profile Creation Workflow

The goal is to create an AppArmor profile that allows vulnerable_service to perform its legitimate logging but denies access to /data/local/tmp/secret.txt.

Step 1: Initial Profile Definition (Manual)

For Android native services, an initial profile is often crafted manually based on expected behavior. We’ll start with a very restrictive profile for /data/local/tmp/vulnerable_service. Create a file named vulnerable_service.profile:

#include <tunables/global>profile vulnerable_service /data/local/tmp/vulnerable_service {  #include <abstractions/base>  #include <abstractions/consoles>  # Deny all file writes by default  file,  # Allow execution of itself  /data/local/tmp/vulnerable_service rUx,  # Allow reading and appending to its specific log file  /data/local/tmp/vulnerable_service.log rwa,  # Deny access to the sensitive file  deny /data/local/tmp/secret.txt rw,  # Capabilities  # Add other necessary rules here as they emerge from logging}

This profile does a few things:

  • #include <tunables/global> and #include <abstractions/base>, <abstractions/consoles>: Includes common AppArmor rules.
  • file,: This line is crucial. When placed at the top level of the profile (before more specific rules), it sets the default file access policy for the confined process to “deny all”. Subsequent rules then explicitly allow specific accesses.
  • /data/local/tmp/vulnerable_service rUx,: Allows the service to read, execute, and transition into its own profile.
  • /data/local/tmp/vulnerable_service.log rwa,: Explicitly permits reading, writing, and appending to its designated log file.
  • deny /data/local/tmp/secret.txt rw,: Explicitly denies read/write access to the sensitive file. This rule takes precedence over broader allowances.

Step 2: Load and Enforce the Profile

Push the profile to your Android device and load it into the kernel:

# On your host machineadb push vulnerable_service.profile /data/local/tmp/adb shell "aa-load /data/local/tmp/vulnerable_service.profile"

Verify that the profile is loaded. You might check /sys/kernel/security/apparmor/profiles (requires root):

adb shell "cat /sys/kernel/security/apparmor/profiles"# Look for 'vulnerable_service (enforce)'

Now, run the service again under AppArmor enforcement:

adb shell "/data/local/tmp/vulnerable_service"

Step 3: Analyze Audit Logs (dmesg)

After running the service, check the kernel’s audit log for AppArmor denials. This is typically found in dmesg on Android:

adb shell "dmesg | grep 'apparmor="DENIED"'"

You should see output similar to this:

[  123.456789] audit: type=1400 audit(1234567890.123:1): apparmor="DENIED" operation="open" profile="vulnerable_service" name="/data/local/tmp/secret.txt" pid=1234 comm="vulnerable_servic" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

This log clearly shows that vulnerable_service was denied read access (requested_mask="r") to /data/local/tmp/secret.txt. This confirms our sandbox is working as intended!

You might also notice other denials if the service attempts operations not covered by the abstractions or our specific rules (e.g., accessing /dev/urandom or other system paths). Each denial provides valuable information for further refining the profile.

Step 4: Iterative Refinement (If Needed)

If the service was unable to perform its *legitimate* operations, you would iterate:

  1. Identify the denied operation from dmesg.
  2. Add a rule to vulnerable_service.profile to explicitly allow that specific operation.
  3. Reload the profile: adb shell "aa-load /data/local/tmp/vulnerable_service.profile"
  4. Re-run the service and check dmesg again.

For example, if the service needed to access /dev/urandom, you might add /dev/urandom r, to the profile.

Deployment Considerations

In a production Android environment, AppArmor profiles are usually compiled into the kernel or loaded via init.rc scripts during boot. They are typically located in system/etc/apparmor.d/ or vendor/etc/apparmor.d/ and loaded by a service like apparmor_init. The profiles are often compiled into a binary format (using aa-complain and aa-enforce equivalent commands) for faster loading.

Conclusion

This lab demonstrated the process of creating and enforcing an AppArmor sandbox for a vulnerable Android native service. By starting with a restrictive profile and iteratively refining it based on audit logs, you can effectively minimize the attack surface of services, even in the presence of software vulnerabilities. AppArmor acts as a crucial layer of defense, ensuring that even if an attacker exploits a bug, the service is confined and cannot perform actions outside its defined policy, thus protecting the integrity and confidentiality of the system.

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