DEFInIt: An Analysis Of Exposed Android Init Routines

Transcription

DEFInit: An Analysis of Exposed Android Init RoutinesYuede Ji, University of North Texas; Mohamed Elsabagh, Ryan Johnson, andAngelos Stavrou, security21/presentation/jiThis paper is included in the Proceedings of the30th USENIX Security Symposium.August 11–13, 2021978-1-939133-24-3Open access to the Proceedings of the30th USENIX Security Symposiumis sponsored by USENIX.

DEFI NIT: An Analysis of Exposed Android Init RoutinesYuede Ji Mohamed ElsabaghRyan JohnsonUniversity of North tavrou@kryptowire.comAbstractDuring the booting process of an Android device, a specialdaemon called Init is launched by the kernel as the first userspace process. Android allows vendors to extend the behaviorof Init by introducing custom routines in .rc files. These Initroutines can also be triggered by privileged pre-installed appsin a certain manner to accomplish privileged functionalities.However, as these pre-installed apps may fail to properly protect access to code sites triggering these Init routines, thecapabilities of these routines may leak to unprivileged apps,resulting in crossing security boundaries set by the system.To this end, this study aims at investigating the prevalenceof these Init routines and their security impact. We presentDEFI NIT as a tool to help automate the process of identifyingInit routines exposed by pre-installed apps and estimatingtheir potential security impact. Our findings are alarming. Wefound that custom Init routines added by vendors were substantial and had significant security impact. On a data set of259 firmware from the top 21 vendors worldwide, we identified 1, 947 exposed custom Init routines in 101 firmwarefrom 13 vendors. Of these routines, 515 performed at leastone sensitive action. We verified 89 instances spanning 30firmware from 6 vendors, allowing unprivileged apps to perform sensitive functionalities without user interaction, including disabling SELinux enforcement, sniffing network traffic,reading system logs, among others.1IntroductionAndroid is open source and freely available to vendors tocustomize and port to different platforms. Owing to its opensource nature, Android has dominated the global smartphonemarket, holding more than 72% of the market share as of December 2020 [1]. The Android ecosystem is vast and versatilein supporting various platforms such as TVs, wearables, infotainment systems, and IoT devices. Android is built on top of aAngelos Stavroumodified Linux with several changes at the kernel and user levels. Perhaps the most substantial of those is Android’s processisolation and permission model: Android apps run in isolatedprocesses, receive private storage spaces on the filesystem,can communicate using Android-specific secure inter-processcommunication (IPC) mechanisms, and require permissionto access OS resources. This has also been the most studiedaspect of Android from a security perspective [2–12].Less studied are Android changes to Linux that are not necessarily visible to app developers and users. Few prior workshave looked at the security risks stemming from Android customizations to boot loaders [13], kernel drivers [14], memorymanagement [15], and SELinux policies [13, 16]. Other areas,such as changes to user-space daemons, received little to noattention. Of particular interest to us are changes made tothe “Init” process, the first user-space process launched bythe kernel after booting. Similar to Linux, Init on Androidinitializes the user space by mounting filesystems, initializing hardware, setting security policies, and loading essentialsystem components. Different from Linux though, Init on Android is also the system property store where it keeps globalsystem properties (in the form of key-value pairs) set by Inititself and other privileged Android processes.More importantly, Android Init can execute custom routines in response to changing system properties. Android vendors can introduce privileged apps and executables to supportcertain vendor-specific hardware (e.g., sensors and custompartitions) and introduce value-added software services (e.g.,custom pin-locked storage for vendor apps). These customInit routines are defined in .rc files in the form of what Android Init calls “actions” and “services” using the AndroidInit Language [17] and execute with higher privileges thanavailable to regular processes.1Our work focuses on these vendor modifications to Initand attempts to assess their prevalence and potential securityimpact. Specifically, we are interested in studying securitythreats stemming from privileged apps exposing access to1 Unless Thiswork was done while the first author was interning at Kryptowire.USENIX Associationotherwise stated, in the rest of this document we use the term“Init routine” to collectively refer to Init actions and services.30th USENIX Security Symposium3685

Init routines that perform sensitive functionality. To this end,we propose an analysis system called DEFI NIT to help ussystematically analyze Android firmware images, map outthe behaviors of custom Init routines, identify their necessarytrigger conditions, analyze the privileged apps triggering them,and highlight sensitive routines exposed by privileged apps.2We applied DEFI NIT to 259 Android firmware from thetop 21 vendors worldwide containing a total of 64,632 preinstalled apps and identified 1, 947 exposed Init routines, all ofwhich were added by vendors. Of these routines, 515 performat least one sensitive action, impacting 101 firmware from13 vendors. We further identified and verified 89 instancesspanning 30 firmware from 6 vendors, allowing unprivilegedapps to perform sensitive functionalities, such as disablingSELinux enforcement, capturing network traffic, reading system logs, recording the device screen, among others. Ourfindings highlight the significant security risks posed by vendor customizations to the Init process that are visible at theapplication layer, an area that has been previously unexplored.To summarize, we make the following contributions: Novel System. We propose DEFI NIT, an automatedpractical system to process Android firmware imagesand identify Init routines, estimate their behavior, identify routines exposed by privileged apps, and highlightinteresting routines that potentially pose a security risk. Systematic Study. We present the first comprehensivestudy on vendor customization to Android Init routinestriggerable from privileged apps using a corpus of 259firmware covering Android versions 8 to 11 from the top21 vendors worldwide. New Findings. We provide new insights into the prevalence and security impact of customized Init routines andhighlight multiple concrete exploitable instances withsevere security and privacy impact to end users.2Background2.1Android Firmware CustomizationWe use the term Android firmware to refer to Android OSimages that can be flashed to a device. An Android firmwarecontains all files necessary for the device to operate, andtypically includes a bootloader, kernel, boot files, securitypolicies, OS files, and pre-installed apps bundled by the devicevendor. These files are packed as a set of partition blocks, andthe firmware itself is delivered as a compressed archive (theexact file structure differs among vendors).Android vendors customize their devices by including additional hardware and software to differentiate themselves byproviding a unique, branded experience. The vendors take theofficial version of Android from the Android Open Source2 DEFI NIT3686stands for Detecting Exposed Functionalities from Init.30th USENIX Security SymposiumProject (AOSP) to make modifications and integrate theircode. These modifications often touch many parts of the system, including boot files and OS components. It is possibleto identify the base AOSP version a firmware image wasforked from by inspecting the /build.prop file in a firmwareroot filesystem. Once identified, one can identify vendor customization by diffing files from a vendor firmware with theircounterparts, if present, in the firmware base AOSP image.Vendors also often include apps from their partners, hardware manufacturers, and carriers. Android apps are classified by type and an app’s type limits the actions the appcan perform on the device. A third-party app is an app thatdoes not originate from the device vendor and is generallydirectly installed by the user through an app marketplace. Apre-installed app is an app that the vendor has included inthe device firmware. Pre-installed apps are often necessaryfor proper system functionality. Pre-installed apps, by theirnature of being selected by the vendor, can obtain permissionsand capabilities that are not available to third-party apps. Apre-installed app in this regard is considered privileged versusthird-party apps installed from the market.2.2Android InitLike all Unix-like systems, Android has a special daemonprocess named Init (short for initialization) that executes firstin user space once the kernel has finished booting. The Initprocess runs as root and acts as the progenitor to all other userspace processes. The Init process is responsible for startingup the system, setting up directories and their permissions,mounting partitions, initializing peripherals, and setting upvarious system settings. On Android, the Init binary is locatedat /init at the root filesystem.Android Init, however, diverges from traditional Unix-likesystems in multiple ways. For instance, Init implements thesystem property store where it provides global read access tosystem properties to other processes (e.g., via the getpropcommand) and provides privileged processes with write access to system properties (e.g., via the setprop command).Android Init also acts as the device event handler (e.g., whenthe device is connected to USB).Most importantly, device vendors can configure and extendthe behavior of Android Init by defining custom Init routinesin Init Resource Files (.rc files for short) that Init loads at boottime. These .rc files can be stored on different partitions, suchas /system and /vendor. Init starts by loading the /init.rcfile which further imports other .rc files. Init routines areimplemented in the form of “actions” and “services” writtenin the Android Init Language [17]. An Android Init action isa named sequence of internal Init commands. An Init servicespecifies an external program for Init to launch, and potentiallyrestart, with different runtime settings and security contexts.Android Init can execute an Init routine at any point whilethe system is running when its corresponding “trigger” isUSENIX Association

matched. A trigger is a conditional statement that starts with“on” followed by a strictly conjunctive expression over Initevent names (called Event Triggers) or system property values(called Property Triggers). Property Triggers use the word"property:" followed by a property name and expected value(e.g., "on property:service.adb.root 1"). Once the conditions for a trigger are satisfied at runtime, the associatedactions for the trigger are executed.Init recognizes several special property prefixes, including "ro.*" for read-only properties, "persist.*" for properties that survive reboots, and "sys.usb.*" for deviceUSB attachment settings, among others. Init also recognizes two custom properties, "ctl.start service " and"ctl.stop service ", that can be set by privileged apps todirectly start and stop Init services without necessarily needing to satisfy their triggers.Init property triggers use global system properties thatcan only be set by privileged apps and processes using internal Android commands and APIs, such as android.os.SystemProperties.set and setprop, that are not availableto third-party apps. In this regard, privileged apps can bethought of as deputies as they can act on the request of anunprivileged app and invoke an Init routine by setting systemproperties. This can allow an unprivileged app to indirectlylaunch a sensitive Init routine through an open interface in aprivileged app, resulting in crossing the security boundariesset by the system as the capabilities of the exposed Init routineare effectively leaked to the unprivileged app.3Threat Model and AssumptionsOur threat model assumes the users have installed an attackercontrolled, third-party app on their devices. This attack appwill attempt to escalate its privileges by interacting with privileged pre-installed apps that have the capability to modifysystem properties that start Init routines.We assume the attack app will interact with a privilegedapp by sending crafted inter-process communication (IPC)messages to exported (i.e., callable by other apps) componentsin the privileged app. This is a standard threat model specificto the Android ecosystem where apps are sandboxed and preinstalled apps can be granted permissions and capabilitiesnot available to third-party apps [2–8, 10–12]. These methodsallow the attack app to indirectly invoke code sites withina privileged app that cause the setting of system properties,launching an Init routine that performs a functionality that athird-party cannot perform given its limited privileges.We only consider pre-installed apps as the access vector toInit routines. Analyzing other potential access vectors introduced by vendors is outside the scope of this work. Finally, weconsider only Android versions 8.0 and higher since versionsprior to 8.0 no longer receive system updates nor securitypatches as of this writing.USENIX AssociationAttack App (unprivileged)Initvoid exploit() {v0 new Intent(“wifitest”);sendBroadcast(v0);.1}on property:a 1 && property:b true.start wifitest.3service wifitest /bin/wifitest.shuser root.System App (privileged)void onReceive(.) {.setprop(“a”, “1”);setprop(“b”, “true”);.}42/bin/wifitest.sh.setenforce 0.Figure 1: A simplified example based on a real-world case identifiedby DEFI NIT for disabling SELinux enforcement through an Initservice exposed by a pre-installed app.44.1OverviewMotivating ExampleA real-world example of an exposed sensitive Init routinedetected by DEFI NIT is shown in Figure 1, where an unprivileged app disables SELinux policy enforcement on the devicefor all processes by exploiting a privileged app invoking asensitive Init service. The figure shows the interactions between the attack third-party app, the privileged system app,the Init process, and the shell script invoked by a custom Initservice to disable SELinux. A third-party attack app broadcasts a message (called an Intent in the context of Android)with an action of "wifitest" in step 1 . The Intent is receivedby an exported component in a privileged system app thatregistered to receive that action. Once received, the privileged app sets the system properties "a" to "1" and "b" to"true" in step 2 . These two system properties trigger an Initaction (i.e., satisfy its conditions) that starts the wifitest service in step 3 . The wifitest service in turn executes a shellscript /bin/wifitest.sh, in step 4 , as the root user. Finally,the script executes the setenforce 0 command that disablesthe system-wide enforcement of Mandatory Access Control(MAC) SELinux policies (the main defense mechanism Android systems depend on to establish mandatory privilegeboundaries among processes).4.2Challenges and Key InsightsThis study aims to identify potential security weaknessesstemming from Init routines exposed to unprivileged apps.We propose DEFI NIT as a system that helps automaticallyhighlight these potential issues for an analyst. DEFI NIT hasto handle multiple challenges concerned with processing Initfiles, understanding the behaviors of Init routines and theirpotential security impact, capturing dependencies and triggerconditions, identifying privileged apps invoking these routines, and detecting sensitive routines exposed to unprivilegedactors. We discuss these challenges in the following.30th USENIX Security Symposium3687

C1: Processing Init files. While the Android Init Languageis documented at [17], Init itself loads and processes .rc filesdynamically in the presence of extra sources of information,such as system properties preloaded at boot time. Init .rc filescan also reference Init sections defined in other files (using animport statement) and service definitions are polymorphic(i.e., a service can override its parent definition by using anoverride modifier). Since DEFI NIT is static, we needed toimplement a parser for .rc files that closely mimics the dynamism of Init. By studying the source code of Init, we foundthat we can start parsing at the root /init.rc file and nest intoincluded files in depth-first order to mimic the behavior ofInit. We discuss this in more detail in §5.1.C2: Determining action and service behaviors. Init routines can execute programs represented as Init commands,ELF binaries, and shell scripts. DEFI NIT needs to be able todetermine the behavior of these programs to identify whichroutines perform security-relevant functionalities and the sensitivity of these functionalities. While the behaviors of individual commands and standard Android APIs are documented,the problem of automatically determining the behavior of arbitrary programs is undecidable as it can be reduced to thehalting problem [18]. Nevertheless, the behavior of a program can be estimated based on information present in theexecutable program file that could indicate its behavior.In DEFI NIT, we estimate the behavior of routines by extracting code traces (including hardcoded parameter values)from their binaries and estimating the behavior of these tracesusing a compiled list of behaviors of potentially sensitivecommands and standard Android APIs. This process maps aroutine to a vector of behavioral categories, allowing an analyst to get an idea of its estimated general behavior. We alsoused static rules in our evaluation to help highlight specificbehaviors by looking in the traces for certain call patterns. Forexample, if an Init service calls a system command to dumpsystem logs to a file followed by a command that moves filesto external storage, then it can be estimated that the serviceleaks the system logs to external storage. The specifics ofbehavior estimation vary depending on the kind of programexecutable being analyzed, which we detail out in §5.2.C3: Modeling trigger conditions. There exist multiple interdependencies between Init actions, services, and Androidcommands and APIs. For instance, an action could start aservice that runs a program that itself starts another Init service by setting an Init property to which a property triggeris registered. Actions could also start multiple services andcommands. Trigger conditions can be composed of properties set by disjoint routines, making it difficult to identify thenecessary trigger sequences to get Init to launch a certainroutine. Therefore, DEFI NIT needs to capture these dependencies (including transitive ones) to be able to reason about368830th USENIX Security Symposiumthe conditions necessary to trigger a certain behavior via Initroutines. To capture these interdependencies, DEFI NIT buildsa graph that we refer to as Init Dependency Graph (IDG). Inan IDG, nodes represent Init triggers, Init services, and executables called by routines. (An executable here can be anInit command, a shell command, or an ELF binary.) Edges inan IDG represent call edges between triggers, services, andexecutables; and conjunction relationships between conjunctsin a composite property trigger. Using an IDG, we can efficiently extract relationships between Init actions, services,triggers, and the conditions necessary to execute a certain Initaction or service. We discuss IDGs in §5.3.C4: Identifying exposed routines and behaviors. OnAndroid, Init properties can only be set by privilegedapps (including privileged native binaries) by using internal APIs, such as android.os.SystemProperties.set andsystem property set, that are not available to thirdparty apps. Privileged apps here can be thought of as deputiesthat control access to Init routines. Privileged apps that invokesensitive Init routines based on requests from unprivilegedapps can be subject to confused-deputy attacks where thecapabilities performed by Init leak to the unprivileged apps.Identifying privileged apps invoking Init routines requiresidentifying app call sites that invoke APIs setting system properties, and resolving the parameter values of these APIs toidentify the properties being set and their corresponding values at each call site. In DEFI NIT, we developed a technique toidentify the property keys being read/written and the mappingbetween each property key and its corresponding value in acontext- (i.e., taking the callee stacks at each relevant API callsite into consideration) and flow-sensitive (i.e., taking statements execution order into consideration) manner. We thenidentify exposed routines by looking for control-flow pathsfrom exported app entry points to relevant code sites. We tuneour analysis to avoid false positives from dynamic bytecodeconstructs (e.g., virtual calls) at the expense of soundness (i.e.,missing some valid flows). More details are provided in §5.4.5Detailed DesignFigure 2 shows the workflow of DEFI NIT. Given an Android firmware image as input, DEFI NIT unpacks it to extractneeded files. It then processes Init .rc files to identify customInit routines. We consider a service to be custom, i.e., a result of vendor modification, if it references an executable thatwas not present in the base AOSP image of the firmware. Weconsider an action as custom if its trigger is not found in thebase AOSP image. DEFI NIT then estimates the behavior ofthese routines and assesses their security impact. Followingthat, DEFI NIT identifies privileged apps exposing access tothese sensitive routines and generates a report containing alisting of the exposed routines, their estimated behaviors andUSENIX Association

.{.sh}of the trigger at runtime). The output of this step is an enumeration of the effective set of Init routines (i.e., the subset ofroutines that are live at runtime) and their associated triggers,SELinux modifiers, and other attributes as defined in t InitRoutinesdisable selinux:- setenforce 0]capture traffic:- tcpdump- mv * /sdcard/*.Collect Traces& EstimateBehaviorsr1: [(/bin/wifitest.sh, root, a 1&b true), .]r2: [(/vendor/bin/x.sh, root, c 10), .].r1: wifitest.sh: [setprop x y; setenforce 0; .](perms, disable selinux, .)r2: x.sh: [tcpdump a; mv a /sdcard; .]],(dump, capture traffic, .).BehavioralRulesModel TriggerConditions &DependenciesIdentify AppsExposing InitRoutinesa 1 &&b truea 1b truer1wifitest.shapp1: (r1: perms, disables selinux, .), .app2: (r2: dump, captures traffic, .), .Figure 2: Workflow of DEFI NIT.security impact, privileged apps exposing them and how, andInit trigger conditions needed to invoke these routines. Wediscuss the details of these steps in the following.35.1Extracting Init Routine DefinitionsDEFI NIT processes .rc files to extract Init routines and thecommands or executables they invoke. Parsing occurs according to the syntax of the Android Init Language [17] ina way that mimics the runtime behavior of Init. Specifically,DEFI NIT parses each .rc file line by line, starting at the root/init.rc file, then nests into imported files in depth-first order following the same import rules in [17, sec. imports].Variables encountered during parsing of static Init constructs(e.g., import paths) are substituted with their correspondingdefault values from .prop and boot environment files.Since an Init service definition can override a previous definition associated with the same service name, DEFI NIT onlykeeps the most-specialized service definition that uses theoverride modifier (i.e., the last encountered definition in Init.rc parsing order that sets the override modifier). For a trigger that is declared multiple times, DEFI NIT appends all itsactions under the first-encountered trigger (this is equivalentto Init sequentially invoking the actions of each declaration3 We omit the details of the firmware unpacking process as we employstandard unpacking tools and techniques. Interested readers can refer to priorwork (e.g., [9, 11]) for information on unpacking techniques.USENIX Association5.2Estimating Behaviors of Init RoutinesThe goal of this step is to estimate the behavior of executablesinvoked by Init when a trigger is fired. Init can invoke threekinds of executables in response to a trigger: Init actions, shellscripts, and ELF binaries. We discuss how we collect codetraces from each executable kind in the following.Init actions are defined by the Init Language [17] asa named sequence of predefined Init commands, thereforeDEFI NIT extracts Init action traces from the action definitionsin .rc files as a list of Init commands, substituting hardcodedproperty values as needed from .prop files.For shell scripts, DEFI NIT employs a custom shell tracerthat dry-runs shell scripts inside a sandbox built on top ofBash trace mode (see bash -x option at [19]) to collect theircommand traces. Since these scripts are executed in a foreign environment, it is expected that they would incur runtimeerrors due to missing dependencies from their execution environment. Therefore, DEFI NIT needs to carefully controltheir execution to maximize coverage. Specifically, DEFI NITtaints environment and command-line arguments availableat a shell script invocation site in an .rc file, and evaluatesonly conditional statements in the script that depend on (i.e.,directly uses or derived from) these arguments. Additionally,DEFI NIT ignores “sleep” statements and masks return codesof invoked shell commands to avoid prematurely exiting thescript due to missing commands.For ELF binaries, DEFI NIT collects static traces of calledAPIs by traversing simple paths in the binary inter-proceduralcontrol-flow graph (ICFG) in depth-first order, starting atthe binary entry point function and ignoring control flowsthrough basic blocks not calling any APIs. For relevant APIswith potentially sensitive arguments, DEFI NIT performs interprocedural Def-Use analysis to propagate constant characterstrings and numerical definitions to API call sites to identifyarguments at each call site of interest. In addition, DEFI NITextracts strings from the binary that resemble system commands by matching the first token of strings to executablefile names and paths available in the input ROM. This wholeprocess is done recursively through the ELF executable andits dynamically linked functions.DEFI NIT then uses the traces for each Init routine to annotate the routine with behavioral categories based on thecurated list of behaviors of security-sensitive APIs and commands shown in Table 1. We collected these by, first, automatically enumerating all the commands in AOSP images andthe APIs in Bionic libc. This resulted in 473 commands and4, 259 APIs. Then, we filtered out the obviously non-securityrelevant ones, leaving us with 137 commands and 64 APIs.30th USENIX Security Symposium3689

Table 1: Security-sensitive APIs and commands used by DEFI NITfor highlighting security-sensitive Init routines.CategoryDevice SettingsSensitive DataAPIs/Commandshid ime locksettings settings svcatrace bugreport content diag klogdiag mdlog diag socket logdiag uart log dumpstate dumpsyslogcat ramdump record stream newscreencap screenrecord tcpdumpNetworkingdnsmasq ifconfig iptables telecom sendsendfile sendfile64socket local server bindPackage Management applypatch pm dpm insmod patchoatPermission Control keystore appops setsid load policysetenforcePower Management thermal engine rebootandroid reboot rebootProcess Management cmd killall killpg ptrace serviceUI Interactionvirtual touchpad am input sendeventmonkey uiautomatorTotalCount515Pre-installed Appwifitestconj.callconj.b Figure 3: Simplified Init Dependency Graph (IDG) for the runningexample in Figure 1.45653Modeling Trigger Conditions30th USENIX Security Symposiumcallinvokea 1To capture trigger conditions of Init routines, we propose adirected heterogeneous graph structure that we refer to asInit Dependency Graph (IDG) in which we encode triggerconditions and transitive dependencies between Init triggers,actions, services, and executed programs. Figure 3 shows theIDG for the running example in Figure 1. DEFI NIT uses anIDG to identify what functionality Init performs in responseto properties set by privileged apps and binaries.There are three types of nodes in the IDG: trigger, service,and executable nodes. A trigger node represents a single Inittrigger condition. For example, the trigger node "a 1" denotes that the property key "a" needs to equal "1" at that statein the IDG. We split composite triggers (boolean conjunctionsof trigger conditions) into multiple nodes, one for each trig-3690a 1 &&b truesetFinally, we consulted the documentations of these commandsand APIs and shortlisted the potentially sensitive ones.For each Init routine, DEFI NIT annotates it with the countsof security-sensitive commands and APIs it executes. Thiscategorization gives an analyst a basic understanding of theoverall behavior of a service and its potential security impact.DEFI NIT then uses pattern-matching rules to identify callsequences in the traces that indicate more specific interestingbehaviors. For example, a common source of vulnerabilitiesin Android is leaking sensitive data to external (shared) storage, which DEFI NIT can identify by looking in the traces forcalls to a command from the Sensitive Data category followedby calls to commands that move files to a path on externalstorage. We developed 116 rules to

we propose an analysis system called DEFINIT to help us systematically analyze Android firmware images, map out the behaviors of custom Init routines, identify their necessary triggerconditions,analyze the privilegedapps triggering them, and highlight sensitive routines exposed by privileged apps.2 We applied DEFINIT to 259 Android firmware .