As the cost of phones and Android-capable hardware goes down, using Android in embedded applications is becoming more and more practical. Even if you’re not using the cell features of the phone, an Android-based handset makes an affordable and capable touchscreen interface while also providing Bluetooth, Wifi, and USB interfaces.

The transition from traditional embedded development environments to Android is not trivial. At the very least, you’ll have to get up to speed with Android app development, and depending on the customization your system requires, you may need to develop a custom Board Support Package (BSP) for your hardware.

Learning to develop Android applications is a well documented process. There are a plethora of books and websites available to guide you through the stages of design and development. What’s not as well documented is how to secure your embedded Android application and harden the system for deployment. In this article, we’ll share some of the common pitfalls that befall unwary developers and provide tips for mitigating potential security risks.

Kiosk Breakout Attacks

Example Kiosk Mode App

You are probably familiar with the concept of a “kiosk” application, where the user interface for a physical display is entirely controlled by a single application that prevents access to the underlying operating system. Android provides a way to configure Apps to act as a Device Policy Controller (DPC). A DPC app controls local device policies and system applications on devices. This is an effective way to deploy a single-use App in a “kiosk mode” configuration on Android, which is inherently a general purpose Mobile Operating System. The general recipe for adding DPC support can be found in various articles.

Ideally, DPC would provide a completely sandboxed world that would constrain the end user (and attackers) to only using the Activities and Views that the App authors intended. But what would the consequences be if a malicious user with unlimited physical access was able to “break out” of this kiosk mode? If this is achieved, the display unit is now a regular Android device that is subject to attacks such as:

  • Managing Settings on the device
  • Enabling ADB access from an attacker-controlled host system
  • Installation of malicious Apps
  • Escalating privileges via execution of hacking tools pushed using ADB
  • Unintended Erasure/Factory Reset of the device

This list of attacks is not exhaustive but demonstrates that once the “kiosk mode” boundary is broken, the integrity of the system is significantly compromised. The sections below outline a few common oversights when using Android kiosk modes like DPC.

Safe Mode Reboot Attack

Android provides an option to boot into a state called “Safe Mode”. When started in this mode, the phone will only start apps that originally came with the system. Because your App is not “original” (i.e. it is installed as part of your own provisioning procedures), Android will not start it, which gives the attacker the ability to interact with the display to perform malicious actions.

This attack is also non-destructive – the attacker simply has to reboot the display normally and the original system operation is restored. Here’s how the attack works:

  1. Open your application
  2. Hold down the physical power button. A menu giving choices “Power Off” and “Restart” are displayed
  3. Long press the “Power Off” entry. A pop-up prompting the user to restart in Safe Mode will be presented.
  4. The attacker clicks “Yes” to the Safe Mode pop-up. The display will start in Safe Mode.
  5. Insert the USB cable connected to the attacker Host computer.
  6. Accept the prompt to add ADB Key, and voila, the attacker now has complete control.

This issue can easily be mitigated by updating the App’s source code to invoke the DISALLOW_SAFE_BOOT option during Application startup. Because the App is acting as a Device Policy Controller, it will be able to prevent the user from restarting the device in Safe mode. The following code snippet demonstrates how to set this option:

// Set the activity as the preferred option for the device.
ComponentName activity = new ComponentName(context, KioskModeActivity.class);
DevicePolicyManager dpm =
    (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);

// If the system is running in lock task mode, set the user restrictions // for a kiosk after launching the activity.
String[] restrictions = {
    UserManager.DISALLOW_SAFE_BOOT,
    // Add others... 
};

for (String restriction: restrictions)
    dpm.addUserRestriction(adminName, restriction);

We highly recommend fully reviewing the Dedicated Devices Cookbook and adding any additional user restrictions. The entire list of user restrictions is available here .

Quick Draw Attack

Another common technique for breaking out of kiosk mode is something we call the “Quick Draw” attack. When the display is booting up, kiosk mode is not active yet and the normal square “Switch Tasks” button is visible on the Android bottom bar if not configured properly. Clicking on this UI button repeatedly and rapidly has a high probability of causing a crash in the App prior to the crash handler being installed. This prevents the App from starting normally leading to a Kiosk Breakout. Here’s how to mount this attack:

  1. Reboot the display using the physical power button on the device.
  2. When the display says “Android is Starting”, quickly tap on the square Switch Tasks button.  If quick enough, the App will fail to start and a crash will be displayed.  The display will be stuck in the “Switch Tasks” View.  If the App starts, reboot the device to try the attack again.
  3. Click on the Settings Task.
  4. Insert the USB cable connected to the attacker Host computer.
  5. Accept the prompt to add ADB Key.

This Exception is usually the result trying to invoke the startLockTask() function within the initialization of the Presenter Activity while the Android system is in an invalid state.  According to Google’s guidance , “an app starts its own activities in lock task mode by calling Activity::startLockTask(). To call this method, the activity must be running in the foreground (see Activity-lifecycle concepts)”.  Careful attention should be given to remediating and testing a fix for this issue, as it involves a critical phase of display startup in which the kiosk is being established.  Also keep in mind that on some Android devices this may be a system-level issue that cannot be fixed from the App itself.

Sim Card Pop-Up Attack

When the display device is started with a valid SIM card inserted into the SIM/SDCard tray, a modal dialog may be displayed between the handoff from the Android System startup and your App startup.  This creates an opportunity to gain ADB access to the display. Here’s how to launch this attack:

  1. Power Off the display.
  2. Use a plastic spudger tool to remove the tray from the slot on the display.
  3. Insert a SIM card (in a “Micro” size SIM adapter) into the tray and re insert the tray into the slot. 
  4. Power on the display.  The pop-up shown at right is displayed.
  5. Insert the USB cable connected to the attacker Host computer.
  6. Accept the prompt to add ADB Key.

The root of this problem is that handsets that only support 1x SIM devices are sometimes configured to manage 2x SIMs.  According to the code path in AOSP::SimSelectNotification.java if there is only 1x SIM this notification will never be shown.  This problem is out of the scope of the app, and it may be the result of a misconfiguration by the vendor. Vendors should configure the system appropriately to only have 1x SIM to avoid the notification while still allowing the ability to use the Mobile interface in the future (e.g. for OTA field updates).

USB HID Device Attachment Attack

When connecting and disconnecting various USB Human Interface Device (HID) peripherals to the display using a USB OTG dongle, a brief context switch in the Android System can cause an Exception to occur within the App.  If the attacker is quick enough when this happens, she can interact with the Switch Task button and breakout of the kiosk. Below are the steps to perform this attack:

  1. Open your App
  2. Obtain a USB peripheral that presents itself as a HID device (e.g. keyboard, mouse). 
  3. Attach the USB device to the Type-A end of the USB OTG dongle.
  4. Attach the Micro USB end of the USB OTG dongle to the display’s Micro USB port.  At the same time, start clicking the square Switch Tasks button area rapidly. If the App crash occurs, the Switch Tasks button will be briefly enabled and the attacker can interrupt the App restart process.
  5. If needed repeat (4) by detaching and reattaching until the hard crash occurs.
  6. Insert the USB cable connected to the attacker Host computer.
  7. Accept the prompt to add ADB Key.

To mitigate this attack, ensure there are no race conditions or edge cases in your USB event handling code.

Enable SELinux

Security Enhanced Linux (SELinux), is a mandatory access control (MAC) system for the Linux operating system. As a MAC system, it differs from Linux’s familiar discretionary access control (DAC) system. In a DAC system, a concept of ownership exists, whereby an owner of a particular resource controls access permissions associated with it. This is generally coarse-grained and subject to unintended privilege escalation.  

SELinux is arguably the most useful security feature provided in the baseline Android system.  When it is properly deployed with correct policies, it provides a strong sandboxing policy.  Verify that SELinux is enabled and not in Permissive mode.  In Permissive mode, permission denials are logged but not enforced.  This is the mode that Android System developers use to verify policies are correct and troubleshoot permission denials.  However, none of the actions are denied, allowing an attacker to completely bypass it.  SELinux should be set to run in Enforcing mode (the default since Android 5.0). This issue does not present a concrete, immediate vulnerability to the system, but it may allow a dedicated attacker to develop a privilege escalation exploit with less effort.

Setting the SELinux mode is normally done by early Android system initialization code and requires the ability to compile and flash the System image. Enabling SELinux on your device may require coordination with the OEM vendor.

Disable Unused Interfaces

You may not realize that your device has enabled interfaces which are not needed for a production system. A general approach for improving the security of a system is to reduce its attack surface as much as possible. That means removing all unneeded interfaces and entry points to eliminate the possible vectors an attacker could use to exploit your system. Go through the Android Settings with a fine-toothed comb to identify all the interfaces that are enabled, and disable all those that are not needed for your system. Use an ADB batch script as part of your provisioning procedure to disable all unnecessary services and interfaces on the device.

Employ Obfuscation

Be sure to employ obfuscation for your app. Without this option, it is trivial for an attacker to reverse engineer the your APK to reveal a considerable amount of functionality.  This could result in a loss of IP and discovery of methods to enter privileged modes.  The attacker would also be free to alter  the App’s code and repackage the APK.  If she has established ADB access through one of the Kiosk Breakout methods presented earlier, the App could then be replaced with this altered copy.

One form of obfuscation can be enabled by setting the build.gradle file’s minifyEnabled option to true.  Be advised that code obfuscation only makes it more difficult and more expensive for an attacker to reverse engineer your App, but it will always be subject to some level of reverse engineering.  This fundamental idea must be taken into account when making the decision to include any “backdoor” functionality into the App itself (i.e. some privileged “Operator Mode”).

defaultConfig {
	applicationId "com.example.foo"
	minSdkVersion 25
	targetSdkVersion 28
	versionCode appVersionCode
	versionName appDisplayedVersion
	testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
	setProperty("archivesBaseName", "$appDisplayedVersion")
}

buildTypes {
	release {
		minifyEnabled true
		propguardFiles getDefaultPropguardFile("propguard-android.txt"), "propguard-rules.pro"
	}
	debug {
		minifyEnabled false
	}
}

productFlavors {
}

Conclusion

These are just a small subset of the types of the security issues we commonly come across when performing vulnerability analysis of Android-based systems. If your team is deploying an Android-based embedded system for the first time, consider having an independent third-party perform a security audit of your system to help you identify the risks and develop mitigations. Contact Us today to get a quote for assistance in securing your application.

 

About the Author: Anthony DeRosa

Anthony DeRosa is a software security researcher with 20 years of experience in static and dynamic reverse engineering. He holds a Masters degree in Electrical and Computer Engineering from Johns Hopkins University. He is the founder of Syscall 7, a software development and analysis firm based in Baltimore, MD. He serves as an expert witness in technology-related litigation and currently leads a team of engineers supporting patent infringement litigation through source code analysis, software reverse engineering, and runtime testing.