android-rasp

Introduction: RASP (Runtime Application Self-Protection) solution for protecting Android apps against being run on vulnerable devices.
More: Author   ReportBugs   OfficialWebsite   
Tags:

CI Kotlin Android Android Gradle Maven

An open-source RASP (Runtime Application Self-Protection) solution for protecting Android apps against being run on vulnerable devices.

[!NOTE]
Android RASP is still in development, meaning that some breaking changes are likely to be introduced in future releases. See Versioning section for more information.

Motivation

In the current threat-rich environment, it is crucial to protect the apps against exploitation of a wide range of vulnerabilities and reverse engineering techniques. Hooking frameworks, man-in-the-middle attacks, app repackaging, rooted devices, just to name a few common threats applicable to mobile applications.

While there are existing solutions for guarding against aforementioned threats, almost all of them are paid. This library is one attempt to provide a robust solution for "regular" teams and devs that cannot afford spending hundreds of dollars per month to defend against this kind of threats, allowing to take control over application execution, security threat detection, and real-time attack prevention.

[!NOTE]
While adopting this library will shield your app against a number of runtime security threats, you need to remember that no security measure can ever guarantee absolute security. Any motivated and skilled enough attacker will eventually bypass all security protections. For this reason, always keep your threat models up to date.

Getting started

First ensure that you have defined mavenCentral in your Gradle configuration.

repositories {
    mavenCentral()
}

Next, add Android RASP library as a dependency to your project.

dependencies {
    implementation 'com.securevale:rasp-android:{version}'
}

Before first use, the library needs to be initialised with init() method. This method is expected to be called only once per app's lifecycle, so the best place for doing it is inside of your app's Application class.

import com.securevale.rasp.android.SecureApp

class SampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        SecureApp.init()
    }
}

Then, create a builder with the desired configuration options.

import com.securevale.rasp.android.emulator.CheckLevel
import com.securevale.rasp.android.api.SecureAppChecker

val shouldCheckForEmulator = true
val shouldCheckForDebugger = true
val shouldCheckForRoot = true

val builder = SecureAppChecker.Builder(
  this,
  checkEmulator = shouldCheckForEmulator,
  checkDebugger = shouldCheckForDebugger,
  checkRoot = shouldCheckForRoot
)

Use the builder to create the RASP checks and trigger them to obtain the result.

import com.securevale.rasp.android.api.result.Result

val check = builder.build()
val checkResult = check.check()

when (checkResult) {
    is Result.EmulatorFound -> {} // app is most likely running on emulator
    is Result.DebuggerEnabled -> {} // app is in debug mode
    is Result.Rooted -> {} // app is most likely rooted
    is Result.Secure -> {} // OK, no threats detected
}

You can also perform more granular checks.

val check = builder.build()
check.subscribe {
    // examine result(s) here
}

Or even subscribe in order to be notified only when a potential threat is detected.

val check = builder.build()
check.subscribeVulnerabilitiesOnly(granular = true) {
  // examine result(s) here
}

You can also choose which checks should be run by passing appropriate list to the checkOnlyFor parameter.

import com.securevale.rasp.android.api.result.DebuggerChecks
import com.securevale.rasp.android.api.result.EmulatorChecks
import com.securevale.rasp.android.api.result.RootChecks

val check = builder.build()
check.subscribeVulnerabilitiesOnly(
  granular = true,
  checkOnlyFor = arrayOf(
    EmulatorChecks.AvdDevice,
    EmulatorChecks.AvdHardware,
    EmulatorChecks.Genymotion,
    EmulatorChecks.Nox,
    DebuggerChecks.Debuggable,
    DebuggerChecks.DebugField,
    RootChecks.SuUser,
    RootChecks.TestTags,
    RootChecks.RootApps,
    RootChecks.RootCloakingApps,
    RootChecks.WritablePaths,
    RootChecks.SuspiciousProperties
  )
) {
  // examine result(s) here
}

For more information about available configuration options, see SecureAppChecker class documentation.

[!IMPORTANT] A skilled attacker might be able to repackage protected app and remove the checks from the source code. With that said, it is highly recommended to add these checks in multiple places in code, so as to maximize the cost and effort required to successfully bypass all the checks. Additionally, in order to further impede the malicious actors' life, the library checks are written in native code (using Rust language) and distributed with library source code as .so library files.

Supported Checks

Debugger Detection

Includes:

  • Several checks for debug flags;
  • Check for connected debugger;
  • Check for threads waiting for the debugger to be attached.

Emulator Detection

Includes:

  • Check for "basic" emulator indicators (mostly device build configuration fields indicating whether particular device is "real" or not). These fields can be easily faked by the emulator makers or even by the device user (if the device happens to be rooted);
  • More advanced checks (such as device's operator name, telephone number, properties etc.). Recommended when you need to be more certain whether the device is an emulator. Please note that in order to take full advantage of these checks, you need to add android.permission.READ_PHONE_STATE permission to your application manifest file.

All implemented checks were tested on various emulators and devices to decrease both false-positives ( when device that is not an emulator is reported as one) and false-negatives. However, as the detection techniques become more advanced, the emulator detection bypass tools are improving as well. This is a never ending cat and mouse game, so there is no guarantee that all emulators will be correctly and accurately reported as such. The library shall be continuously updated with new emulator detection techniques with the aim of catching the emulators that slip through the existing checks.

Root Detection

Includes:

  • Checks for superuser indicators;
  • Checks for test tags present on device;
  • Checks for rooting and root cloaking apps (currently in beta);
  • Checks for paths that should not be writable;
  • Checks for suspicious properties;

[!IMPORTANT] Rooting is an ever-evolving domain and so are the root detection techniques. For this reason it is important to understand that the above checks are not exhaustive and should be expected to undergo continuous improvement.

[!NOTE] Although some basics checks for Magisk are already in place, a comprehensive Magisk detection is expected to be implemented in future releases.

ProGuard

Android RASP ships with its own ProGuard rules, except one caveat regarding the DebugField check. The library relies on BuildConfig class which needs to be excluded from obfuscation. In order for this check to return correct results, add the following line to your ProGuard configuration file:

-keep class {your_package}.BuildConfig{ *; }

Alternatively, you can opt out from this check by excluding DebugField from an array of checks passed to checkOnlyFor parameter.

Sample app

Sample allows you to test checks. To run it you need to clone the project and build and run "sample-app" project on device of your choice.

sample-app

Versioning

This project follows semantic versioning. While still in major version 0, source-stability is only guaranteed within minor versions (e.g. between 0.3.0 and 0.3.1). If you want to guard against potentially source-breaking package updates, you can specify your package dependency using exact version as the requirement.

License

This tool and code is released under Apache License v2.0 with Runtime Library Exception. Please see LICENSE for more information.

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools