Earlier, installing Chainfire’s SuperSU was one of the common ways of rooting Android devices. With time, security researchers have found newer and better ways of rooting Android devices which makes root detection harder. One such method is the “systemless” root.
In this method, modifications are stored in the boot partition instead of modifying OS files. This way, it is easier to bypass root checks. Hence, there is a strong need of making root detection checks more comprehensive.
Overview
The goal of comprehensive root detection is to make running the application on a rooted device more difficult. Detecting rooted devices alone are not sufficient, but implementing various checks scattered throughout the app can improve the effectiveness of overall implementation and improve the security of Android apps.
Recommended Implementation Flow
In the following section, we have listed common root detection methods as well as reference steps for implementation. We recommend implementing all checks. If any of the checks (from Step 1 to Step 5) fails, then don’t allow the end-user to continue further.
- Implement emulator detection
- Implement Google SafetyNet Attestation API – ctsProfile and basicIntegrity
- Implement root detection checks
- Implement Frida detection
- Implement Magisk detection
- Code obfuscation
1. Implementing emulator detection
Many security researchers and penetration testers use virtual devices for testing the security of Android applications. Having emulator detection gives one layer of additional projection to your application against runtime manipulation.
Reference Implementation
Android Anti-Emulator is one such GitHub repo that uses various ways of detecting an emulated Android environment and determines if the application is running on a virtual device or not.
Link of presentation: https://github.com/strazzere/anti-emulator/tree/master/slides
Link of source code: https://github.com/strazzere/anti-emulator
2. Google SafetyNet Attestation API – ctsProfile and basicIntegrity
SafetyNet is an Android API that creates profiles of devices according to software and hardware information and then compares it to a list of whitelisted device models, which have cleared Android compatibility testing.
How exactly SafetyNet works is not well documented. However, we have included reference links to the documentation. When this API is called, SafetyNet will download a package containing device validation code which is provided by Google. Then, the code is dynamically executed on the device to check the integrity of the device.
basicIntegrity check
basicIntegrity gives a signal about the general integrity of the device and its API. Many rooted devices emulators, virtual devices, and devices with signs of tampering (such as API hooks) fail basicIntegrity checks.
ctsProfileMatch
Unmodified devices certified by Google can pass ctsProfileMatch check. These types of devices will generally fail ctsProfileMatch check:
- Devices that fail basicIntegrity
- Devices with an unlocked bootloader
- Devices with a custom system image (custom ROM)
- Devices for which the manufacturer did not apply for, or pass, Google certification
- Devices with a system image built directly from the AOSP (Android Open Source Program) source files
- Devices with an operating system image, which was distributed as part of a beta or developer preview program (including the Android Beta Program)
To use the API, an app may call the SafetyNetApi.attest method (which returns a JWS message with a result). The application can check the following fields from the returned result.
A sample attestation result:
Explanation
nonces: To match the response to its request.
timestampMs: To check how much time has passed since the request was made and the response was received. A delayed response may suggest suspicious activity.
apkPackageName, apkCertificateDigestSha256, apkDigestSha256: Gives information about APK file to verify the identity of the app. These parameters are absent if the API is unable to determine APK information.
ctsProfileMatch: Returns “true” if device profile matches one of Google’s listed devices.
basicIntegrity: Returns “true” if the device likely hasn’t been tampered with.
Potential integrity verdicts
The table contains an explanation of how device status could affect the values of basicIntegrity and ctsProfileMatch
Implementing Google SafetyNet Attestation API
Google calls this API “an anti-abuse API” that allows app developers to check if the user’s Android device meets certain requirements and determine whether application servers are interacting with a genuine app. This API should be used as an abuse detection check.
The API provides checks the device’s integrity and provides a cryptographically signed attestation. To create “attestation”, API will examine the device’s software and hardware environment. Besides, it will perform a check for integrity issues, and compare it with reference data of approved Android devices. The attestation also contains a timestamp.
SafetyNet Attestation API workflow
- API receives a call from the Android app. This call includes a nonce.
- The attestation service will evaluate the runtime environment and will request a signed attestation of the assessment results from servers of Google.
- The server of Google will send the signed attestation to the SafetyNet Attestation service on the device.
- Attestation service returns this signed attestation to the Android application.
- The mobile app forwards the signed attestation to the mobile app’s server.
- The mobile app’s server validates the response and uses it for anti-abuse decisions.
Code Snippets
Making a SafetyNet attestation request (Java code):
SafetyNet attestation response on your server:
Link of documentation: https://developer.android.com/training/safetynet/attestation#java
Reference Implementation
The reference implementation includes the following details:
- Sample Android App: Android application written in Java that uses Google Play Services for the SafetyNet API on a device.
- Server-side Java code: Two examples showing how to verify a SafetyNet API response on a server written in Java, including offline and online using the Android Device Verification API.
- Server-side code C# code: Two samples showing how to verify a SafetyNet API response on a server written in C#, including offline and online via using Android Device Verification API.
Link of reference implementation: https://github.com/googlesamples/android-play-safetynet
Important points about SafetyNet:
- Don’t use this API as a stand-alone app-security mechanism. Use it in combination with the other mobile application security. Google also recommends implementing these things.
- The API doesn’t work when the device is offline. API returns an error when the device isn’t having internet access.
- Don’t use or interpret responses directly in the calling app. Shifting all anti-abuse decision logic to a server that is controlled by the application team is recommended.
- The API only offers Boolean values that express different levels of system integrity.
- It doesn’t implement or replace any DRM checks.
3. Implementing various root detection checks
With the invention of RootCloak, RootCloak Plus, “system-less” root, Magisk Hide, Frida root bypass scripts, bypassing root detection checks has become easier. Hence, there is no single check that detects all types of rooting methods. Hence, implementing multiple checks will ensure a higher detection rate.
Root Inspector
Most detection apps simply try to run su or perform basic checks. Root Inspector uses multiple methods of root detection. There are 15 root checks via SDK (Java) and 13 checks via NDK (Native Code). This can be an effective detection mechanism against RootCloak or RootCloak Plus as well.
Root detection results using Root Inspector
To test the effectiveness of Root Inspector’s detection mechanisms, we ran an unmodified version of the app on the rooted virtual device and the rooted physical device. To represent a real-life scenario, we implemented various root detection bypass techniques and then checked the results.
Results achieved on a virtual device after implementing common bypass techniques.
Result 1
Memu Emulator (Running Android 7), Xposed Framework, RootCloak (with native root detection bypass enabled) were used during this test.
Result 2:
Memu Emulator (Running Android 7), Xposed Framework, Frida Objection, Frida root bypass script and RootCloak were used during this test.
Results achieved on a physical device after implementing common detection bypasses.
Result 1:
Xiaomi Mi Pad 1 (Running Lineage OS 14 with Android 7.1.2 ), Magisk Core, Magisk Hide (with Magisk Hide enabled on Root Inspector), and Frida root detection bypass scripts were used during testing.
RootBeer Sample and RootBeer Fresh
RootBeer library is an open-source project that checks if the Android device is rooted or not. RootBeer Fresh is based on the original RootBeer project but implements some new and different techniques to detect rooted devices including basic checks to detect Magisk.
Results achieved on a virtual device after implementing common root detection bypass
Result 1:
Memu Emulator (Running Android 7), Xposed Framework, RootCloak, and Frida Objection were used during this test.
Results achieved on a physical device after implementing common detection bypasses.
Result 1:
Xiaomi Mi Pad 1 (Running Lineage OS 14 with Android 7.1.2 ), Magisk Core, Magisk Hide (with Magisk Hide enabled on RootBeer Sample and RootBeer Fresh) were used during testing.
Source Code and Reference Implementation:
Root Inspector:
- Source Code: https://github.com/devadvance/rootinspector
- Implementation: https://github.com/devadvance/rootinspector/releases
RootBeer Fresh:
- Source Code: https://github.com/KimChangYoun/rootbeerFresh
- Implementation: https://play.google.com/store/apps/details?id=com.kimchangyoun.rootbeerFresh.sample
RootBeer:
- Source Code: https://github.com/scottyab/rootbeer
- Implementation: https://play.google.com/store/apps/details?id=com.scottyab.rootbeer.sample
4.Implementing Frida detection
Mobile application penetration testers commonly use Frida for root detection bypass and SSL pinning bypass. Hence, it’s crucial to implement Frida hooking detection to prevent Frida from hooking into your application.
DetectFrida
DetectFrida is one GitHub project that used three 3 ways to detecting frida hooking:
- Detect through named pipes used by Frida
- Detect through frida specific named thread
- Compare text section in memory with text section in disk
Source Code: https://github.com/darvincisec/DetectFrida
Reference Implementation with obfuscated APK: https://github.com/darvincisec/DetectFrida/blob/master/obfuscated-app-release.apk
AntiFrida
AntiFrida is another GitHub repo for detecting Frida instrumentation within a process. AntiFrida performs a scan for all local TCP ports, sends a D-Bus message to each port to identify frida-server. It also scans text sections to search for a specific string found inside frida-gadget*.so or frida-agent*.so files.
Source Code: https://github.com/b-mueller/frida-detection-demo
5. Implementing Magisk detection
When a device is rooted via “systemless root” method, modifications are stored in the boot partition. Due to this, basic root detection checks are bypassed easily. Magisk is a way to root an Android device “systemlesss” way. Via Magisk Manager app, modules and other features can be configured.
Magisk Manager can be hidden by changing its package name to a random name. Another notable feature Magisk Hide prevents applications from detecting the presence of roots. Hence, it’s crucial to implement Magisk detection to prevent the Android app from running on a rooted device.
MagiskDetector
Magisk Detector performs various checks to detect Magisk. It also checks if Magisk Hide is enabled or not, resulting in an effective detection mechanism.
Source Code: https://github.com/vvb2060/MagiskDetector
Reference Implementation: https://github.com/vvb2060/MagiskDetector/releases
DetectMagiskHide
DetectMagiskHide is another GitHub repo for detecting the presence of Magisk and to check if Magisk Hide is enabled on the application or not. Compared to DetectMagiskHide, MagiskDetector has more effective checks.
Source Code: https://github.com/darvincisec/DetectMagiskHide
6. Implementing source-code obfuscation
Application reverse engineering is a common technique used by application penetration tester to understand an application’s technical details. Analyzing application source code helps them gain a deeper understanding of the application. There are some techniques and open-source projects to obfuscate some parts of the code.
IBM ProGuard
You can use the Android ProGuard tool to obfuscate, shrink, and optimize your code. ProGuard works by renaming classes, fields, and methods with obscure names. As ProGuard also removes unused code and performs other optimizations, it can also be used for reducing the size of the APK file.
Example
Code Snippet and Documentation
To shrink, obfuscate, and optimize the Android application, implement the following code in project-level build.gradle file.
minifyEnabled: Enables code shrinking, obfuscation, and optimization for only your project’s release build type.
shrinkResources: Enables shrinking of resources, performed by the Android Gradle plugin.
proguardFiles getDefaultProguardFile: Includes the default ProGuard rules files that are packaged with the Android Gradle plugin.
Link of documentation:
https://developer.android.com/studio/build/shrink-code#obfuscate
StringCare
StringCare is an Android Library and Android Studio Gradle Plugin for obfuscating strings (strings inside the code) at compilation time. StringCare manages strings and assets files in your Android application. Developers can specify what strings and assets should be obfuscated in the XML files.
Code Snippet and Reference Implementation:
Plugin setup:
StringCare plugin looks for string resource
s for obfuscating at compilation time. Include the following in your project-level build.gradle file to make it work globally in the project.
Library setup:
The library is used for revealing the obfuscated string resources generated by the plugin. The obfuscated strings are revealed at runtime. Include the following in-app module’s build.gradle file.
Source Code
Android Library: https://github.com/StringCare/AndroidLibrary
Gradle Plugin for Java: https://github.com/StringCare/GradlePlugin
Gradle Plugin for Kotlin: https://github.com/StringCare/KotlinGradlePlugin
Documentation:
Link: https://github.com/StringCare/AndroidLibrary/wiki
Reference Implementation:
Link: https://github.com/StringCare/KotlinSample
Want to improve the security of your Android app?
Are you looking for ways to improve the security of your Android application? With our in-depth Android application security testing techniques, we can identify security vulnerabilities in your Android application and help you to fix those. Besides, we also provide comprehensive web application security scanning to protect your web applications. Connect with us to discuss improving the security of your internet-facing applications. We would love to help you.