LogoLogo
HomeArticlesCommunity ProductsPremium ProductsGitHubTalsec Website
  • Introduction
  • articles
    • Simple Root Detection: Implementation and verification
    • OWASP Top 10 For Flutter - M5: Insecure Communication for Flutter and Dart
    • OWASP Top 10 For Flutter – M4: Insufficient Input/Output Validation in Flutter
    • OWASP Top 10 For Flutter – M3: Insecure Authentication and Authorization in Flutter
    • OWASP Top 10 For Flutter – M2: Inadequate Supply Chain Security in Flutter
    • OWASP Top 10 For Flutter - M1: Mastering Credential Security in Flutter
    • Hook, Hack, Defend: Frida’s Impact on Mobile Security & How to Fight Back
    • Emulators in Gaming: Threats and Detections
    • Exclusive Research: Unlocking Reliable Crash Tracking with PLCrashReporter for iOS SDKs
    • 🚀A Developer’s Guide to Implement End-to-End Encryption in Mobile Apps 🛡️
    • How to Block Screenshots, Screen Recording, and Remote Access Tools in Android and iOS Apps
    • Flutter Security 101: Restricting Installs to Protect Your App from Unofficial Sources
    • How to test a RASP? OWASP MAS: RASP Techniques Not Implemented [MASWE-0103]
    • How to implement Secure Storage in Flutter?
    • User Authentication Risks Coverage in Flutter Mobile Apps | TALSEE
    • Fact about the origin of the Talsec name
    • React Native Secure Boilerplate 2024: Ignite with freeRASP
    • Flutter CTO Report 2024: Flutter App Security Trends
    • Mobile API Anti-abuse Protection with AppiCrypt®: A New Play Integrity and DeviceCheck Alternative
    • Hacking and protection of Mobile Apps and backend APIs | 2024 Talsec Threat Modeling Exercise
    • Detect system VPNs with freeRASP
    • Introducing Talsec’s advanced malware protection!
    • Fraud-Proofing an Android App: Choosing the Best Device ID for Promo Abuse Prevention
    • Enhancing Capacitor App Security with freeRASP: Your Shield Against Threats 🛡️
    • Safeguarding Your Data in React Native: Secure Storage Solutions
    • Secure Storage: What Flutter can do, what Flutter could do
    • 🔒 Flutter Plugin Attack: Mechanics and Prevention
    • Protecting Your API from App Impersonation: Token Hijacking Guide and Mitigation of JWT Theft
    • Build secure apps in React Native
    • How to Hack & Protect Flutter Apps — Simple and Actionable Guide (Pt. 1/3)
    • How to Hack & Protect Flutter Apps — OWASP MAS and RASP. (Pt. 2/3)
    • How to Hack & Protect Flutter Apps — Steal Firebase Auth token and attack the API. (Pt. 3/3)
    • freeRASP meets Cordova
    • Philosophizing security in a mobile-first world
    • 5 Things John Learned Fighting Hackers of His App — A must-read for PM’s and CISO’s
    • Missing Hero of Flutter World
Powered by GitBook
LogoLogo

Company

  • General Terms and Conditions

Stay Connected

  • LinkedIn
  • X
  • YouTube
On this page
  • Introduction
  • Rooting: What It Is And How It Affects Mobile Security
  • Shut the Mouse Hole: Stop Attackers with Root Detection
  • Common Root Detection Techniques
  • File-based detection
  • Static Analysis for Root Traces
  • Dynamic Detection of Root Access
  • A Hands-On Demo

Was this helpful?

  1. articles

Simple Root Detection: Implementation and verification

PreviousIntroductionNextOWASP Top 10 For Flutter - M5: Insecure Communication for Flutter and Dart

Last updated 7 hours ago

Was this helpful?

Introduction

In the area of mobile application security, the development of new root detection techniques is a constant game of cat and mouse. Developers work tirelessly to implement new methods of identifying and mitigating the risks associated with rooted devices, while attackers continually develop more sophisticated tools to bypass these safeguards. In this article, we will explore what rooting is, outline basic root detection techniques, and show you how to effectively test your implementation.

Check out and for industry leading root detection

Rooting: What It Is And How It Affects Mobile Security

Rooting is the process of gaining privileged control over an Android device, allowing users to bypass system restrictions and obtain full administrative privileges. This process enables the installation of specialized apps, modification of system settings, or running commands that are otherwise restricted in the default user environment.

On the one hand, these capabilities enhance control over the device, providing users with greater flexibility and customization options. On the other hand, rooting introduces significant security risks, as it can expose the system to unauthorized access, malicious activity, or potential vulnerabilities.

In the context of root detection testing, rooting presents a serious challenge as it disables or bypasses many of the operating system’s built-in mechanisms. Therefore, detecting signs of rooting is crucial for maintaining device integrity and preventing vulnerabilities that could be exploited by attackers.

Shut the Mouse Hole: Stop Attackers with Root Detection

As outlined in the previous section, Android devices with root access are significantly more exposed to mobile malware, privilege escalation exploits, and persistent system compromise due to the lack of enforced security boundaries. These rooted environments often become attractive entry points for attackers, allowing them to gain unauthorized access not only to sensitive user data but also for other components of the mobile ecosystem.

Given these significant risks, it is essential to implement reliable root access detection that can effectively identify compromised devices before serious security breaches occur. Root detection is the first line of defense against these threats and should be an integral part of the security architecture of any application.

However, root detection is not a simple task. As rooting techniques evolve, so do the methods for bypassing detection. Root bypass techniques have become increasingly sophisticated, making it necessary to implement multi-layered root detection mechanisms. Relying on a single method of detection is not sufficient, as attackers often find ways to circumvent basic checks.

Common Root Detection Techniques

There are several common techniques used to identify rooted devices, each targeting a specific characteristic or modification that typically occurs when a device is compromised. By combining these techniques, it is possible to create a more robust method for detecting rooted devices, helping to reduce the security risks posed by compromised devices.

File-based detection

Rooting often leaves behind characteristic traces in the file system, and by probing for these artifacts, it’s possible to identify compromised devices. These artifacts may include binaries, configuration files, or directories commonly associated with rooting tools.

One of the most recognizable indicators is the presence of the su binary, which is typically used to grant superuser privileges to applications. This binary may be located in several paths depending on the rooting method or tool used, such as:

  • /system/bin/su

  • /system/xbin/su

  • /sbin/su

  • /data/local/su

  • /data/local/xbin/su

Some devices may also contain utility binaries like busybox, which provides a suite of Unix tools often included in rooted environments:

  • /system/xbin/busybox

  • /system/bin/busybox

Root management apps, such as SuperSU or Magisk, may install APKs and daemon scripts to maintain root access. These files can also be detected at known locations:

  • /system/app/Superuser.apk

  • /system/etc/init.d/99SuperSUDaemon

  • /system/xbin/daemonsu

  • /dev/com.koushikdutta.superuser.daemon/

Implementing a scan for these files can serve as a basic yet effective first layer of root detection.

Process-based detection

Some root detection techniques focus on monitoring and interacting with processes that are commonly associated with root access. Root management tools typically rely on binaries like su to elevate privileges, or sh to execute scripts with root permissions. By attempting to invoke these processes at runtime, it’s possible to detect their presence and functionality, even if their files are hidden or obfuscated.

Package-based detection

Rooted devices often rely on specialized apps to manage superuser access and maintain elevated privileges. These root management tools, such as Magisk, SuperSU, or older solutions like Superuser.

Android provides APIs to query all installed packages using the PackageManager. By comparing package names against a known list of popular root-related apps, it’s possible to detect the presence of these tools on the device.

Some commonly used package names include:

  • eu.chainfire.supersu

  • com.noshufou.android.su

  • com.koushikdutta.superuser

  • com.zachspong.temprootremovejb

  • com.ramdroid.appquarantine

  • com.topjohnwu.magisk

System property-based detection

Custom or tampered Android builds often leave identifiable traces in system properties. These builds may originate from user-modified ROMs or developer-compiled firmware images and frequently bypass standard security mechanisms. Detecting such modifications can help determine if a device is running a non-standard and potentially insecure, operating system.

One common technique involves inspecting the Build.TAGS property. Another indicator is the absence of Google’s Over-The-Air (OTA) update certificates.

Static Analysis for Root Traces

One approach to determine whether an application includes root detection mechanisms is to perform a static analysis. This technique enables the examination of the application’s code without requiring execution. It also helps identify embedded root detection logic, such as techniques outlined in the section Common Root Detection Techniques.

This method is particularly useful during security assessments, where it’s important to understand how thoroughly an application protects itself against rooted environments. The following steps outline how to perform static analysis to detect common root detection implementations:

  1. Check for root detection indicators

  • Apps may check for the presence of files commonly associated with rooted devices (e.g., /system/xbin/su, /data/data/com.superuser.android.id) or for root management apps (e.g., SuperSU, Magisk).

  • Run a static analysis tool such as MobSF or Apktool on the app binary to look for common root detection checks.

  1. Non-standard system behavior detection

  • Check if the app monitors processes that shouldn't normally be running, such as su or sh, which are typically associated with root management tools.

  • Reviewing the app's smali or assembler code can reveal whether the app checks for or interacts with such processes.

  1. System properties modification detection

  • Apps may monitor system properties (e.g., ro.debuggable, ro.secure) for changes, adding another layer to the root detection process.

  1. Critical system directories modification detection

  • Check if the app attempts to modify files or settings in critical system directories, such as /data or /system, which should remain immutable on unrooted devices.

As a result of this analysis, one should be able to observe whether the application contains any of these typical root detection patterns. If such mechanisms are present and clearly target known indicators of root access, it can be concluded that the app implements root detection properly.

It’s important to note that static analysis has limitations and may not reveal all root detection logic, especially if it is obfuscated or implemented using unconventional techniques.

Dynamic Detection of Root Access

Another approach to determine whether an application includes root detection mechanisms is to perform dynamic analysis. This technique involves observing the application’s behaviour at runtime while it operates on a potentially rooted device. It allows testers to understand how the application interacts with the system and whether it performs any real-time checks for root-related indicators.

This method is particularly useful during runtime security assessments, where the goal is to verify if the application can detect and respond to signs of root access under realistic conditions. The following steps outline how to perform dynamic analysis to detect runtime root detection behaviour:

  1. Monitor Application Behaviour

  • Use tools like strace or similar utilities to trace how the app checks for root access. Look for interactions with the system, such as attempts to open su, check running processes, or read root-specific files. This analysis helps uncover how the app performs root detection and may reveal potential weaknesses.

  1. Bypassing Root Detection Mechanisms

  • Run a dynamic analysis tool such as Objection to attempt automated root detection bypass. Use commands to manipulate root checks and observe whether the app still correctly detects root access or if its security mechanisms can be bypassed.

As a result of this analysis, one should be able to determine whether the application performs root detection at runtime and how resilient it is to bypass techniques. If the application actively checks for root indicators and responds appropriately, even under attempts to tamper with its logic, it can be considered to have a well-implemented runtime root detection mechanism. However, if no signs of root detection are observed, or if the application’s checks are easily bypassed, it suggests that the implementation may be incomplete or ineffective.

A Hands-On Demo

This section demonstrates how root detection logic present in an Android application can be verified via static analysis using Semgrep. The test is performed on a class containing basic root detection techniques.

In real-world scenarios, security engineers often work with Android applications where the original source code is not available. However, static analysis can still be performed by reconstructing the code using reverse engineering tools like jadx or apktool. These tools allow analysts to obtain Java or Smali code from an APK file.

For the purposes of this hands-on example, we’ll assume the source code is already available (or has been successfully reconstructed) and focus on the static analysis part.

Let’s take a look at a simple class that contains basic root detection logic:

[RootDetection.kt]

class RootDetection(private val context: Context) {

    companion object {
        private const val TAG = "RootCheck"
    }

    fun mastgTest(): String {
        return when {
            checkRootFiles() || checkSuperUserApk() || checkSuCommand() || checkDangerousProperties() -> {
                "Device is rooted"
            }
            else -> {
                "Device is not rooted"
            }
        }
    }

    private fun checkRootFiles(): Boolean {
        val rootPaths = setOf(
            "/system/app/Superuser.apk",
            "/system/xbin/su",
            "/system/bin/su",
            "/sbin/su",
            "/system/sd/xbin/su",
            "/system/bin/.ext/.su",
            "/system/usr/we-need-root/su-backup",
            "/system/xbin/mu"
        )
        rootPaths.forEach { path ->
            if (File(path).exists()) {
                Log.d(TAG, "Found root file: $path")
            }
        }
        return rootPaths.any { path -> File(path).exists() }
    }

    private fun checkSuperUserApk(): Boolean {
        val superUserApk = File("/system/app/Superuser.apk")
        val exists = superUserApk.exists()
        if (exists) {
            Log.d(TAG, "Found Superuser.apk")
        }
        return exists
    }

    private fun checkSuCommand(): Boolean {
        return try {
            val process = Runtime.getRuntime().exec(arrayOf("which", "su"))
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            val result = reader.readLine()
            if (result != null) {
                Log.d(TAG, "su command found at: $result")
                true
            } else {
                Log.d(TAG, "su command not found")
                false
            }
        } catch (e: IOException) {
            Log.e(TAG, "Error checking su command: ${e.message}", e)
            false
        }
    }

    private fun checkDangerousProperties(): Boolean {
        val dangerousProps = arrayOf("ro.debuggable", "ro.secure", "ro.build.tags")
        dangerousProps.forEach { prop ->
            val value = getSystemProperty(prop)
            if (value != null) {
                Log.d(TAG, "Dangerous property $prop: $value")
                if (value.contains("debug")) {
                    return true
                }
            }
        }
        return false
    }

    private fun getSystemProperty(prop: String): String? {
        return try {
            val process = Runtime.getRuntime().exec(arrayOf("getprop", prop))
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            reader.readLine()
        } catch (e: IOException) {
            Log.e(TAG, "Error checking system property $prop: ${e.message}", e)
            null
        }
    }
}

Before we begin, make sure you have Semgrep installed. You can install it using:

pip install semgrep

or , if you’re using macOS:

brew install semgrep

If you’re working with code reconstructed from APK (e.g., RootDetection_reversed.java obtained via jadx), you can run Semgrep on the reversed Java file instead of the original Kotlin source.

Here’s an example of a Semgrep rule that identifies common patterns used in root checks:

[root-detection.yml]

rules:
 - id: root-detection
   languages: [java, kotlin]
   severity: INFO
   message: Root detection mechanisms have been identified in this application.
   patterns:
     - pattern-either:
         - pattern: File("/system/app/Superuser.apk").exists()
         - pattern: File("/system/xbin/su").exists()
         - pattern: File("/system/bin/su").exists()
         - pattern: File("/sbin/su").exists()
         - pattern: File("/system/sd/xbin/su").exists()
         - pattern: File("/system/bin/.ext/.su").exists()
         - pattern: File("/system/usr/we-need-root/su-backup").exists()
         - pattern: File("/system/xbin/mu").exists()
         - pattern: Runtime.getRuntime().exec("which su")
         - pattern: Runtime.getRuntime().exec(arrayOf("which", "su"))
         - pattern: Runtime.getRuntime().exec(arrayOf("getprop", "ro.debuggable"))
         - pattern: Runtime.getRuntime().exec(arrayOf("getprop", "ro.secure"))
         - pattern: Runtime.getRuntime().exec(arrayOf("getprop", "ro.build.tags"))
         - pattern: Runtime.getRuntime().exec($_)
     - pattern-not: |
         try {
           Runtime.getRuntime().exec($_);
         } catch (Exception e) {
           $_;
         }

You can run Semgrep with this rule:

semgrep -c root-detection.yml RootDetection_reversed.java

When executed, Semgrep will provide results similar to this:

┌────────────────┐
│ 1 Code Finding │
└────────────────┘
                           
    RootDetection_reversed.java
     ❱ rules.root-detection
          Root detection mechanisms have been identified in this application.
                                                                             
           65┆ Process process = Runtime.getRuntime().exec(new String[]{"which", "su"});

Martin Žigrai - OWASP MAS contributor, Talsec Mobile Security Engineer

freeRASP
RASP+