Conversation with Merlin [email protected] · Thu Jan 04 2024

https://mobile-security.gitbook.io/mobile-security-testing-guide/android-testing-guide/0x05j-testing-resiliency-against-reverse-engineering

hackers can bypass the root detection controls of my flutter mobile app by using open source tools. Rooted android device gives similar access to administrative (superuser) permissions as on Linux or any other Unix-like operating system such as FreeBSD or macOS. The mobile application can be installed on a rooted devices. It will lead to disclosure of all the personal and confidential data stored in side the application. They recommends to implement the root detection controls strongly to verify devices the application is running on are rooted. Implement Android Anti-Reversing Defenses in Flutter u can refer the document above.

To implement Android anti-reversing defenses in your Flutter application, you should focus on a multi-layered approach that combines several security techniques to increase the app's resilience against reverse engineering and specific client-side attacks. Here's a step-by-step guide based on best practices and the information from the provided documentation:

Step 1: Implement Basic Security Measures

  • Employ code obfuscation to make reverse engineering more difficult.
  • Encrypt sensitive data stored within the app using strong encryption algorithms.
  • Use HTTPS with certificate pinning for network communications to prevent Man-in-the-Middle (MitM) attacks.

Step 2: Integrate Root Detection Mechanisms

  • Utilize libraries like RootBeer to check if the device is rooted.
  • Implement custom checks to detect the presence of root-management apps, unusual system paths, or suspicious system properties.
  • Disperse root checks throughout the app's codebase to complicate the reversing process for an attacker.

Step 3: Leverage SafetyNet API

  • Integrate the SafetyNet API to assess the integrity and compatibility profile of the running device.
  • Check the ctsProfileMatch and basicIntegrity fields to ensure the device hasn't been tampered with.
  • Use the nonces and timestampMs fields to match responses to their requests and identify any potential time-based attacks.

Step 4: Harden the App Against Debugging and Patching

  • Integrate checks to detect if a debugger is attached to the app.
  • Prevent runtime manipulation by identifying and preventing tools like Frida or Xposed from interfering with the app's operations.

Step 5: Use Native Code

  • Write critical code and security checks in C/C++ using the Android NDK to make it harder to reverse engineer.
  • Periodically call these checks from Flutter through platform channels.

Step 6: Monitor and Update

  • Regularly update your app's security measures to counteract new rooting methods and reverse engineering tools.
  • Monitor for suspicious activity and consider implementing telemetry to understand how your app is being used.

Example Code Snippet for a Simple Root Check:

import 'package:flutter/services.dart'; class SecurityUtils { static const platform = MethodChannel('com.yourapp/security'); static Future<bool> isDeviceRooted() async { try { final bool isRooted = await platform.invokeMethod('isDeviceRooted'); return isRooted; } on PlatformException catch (e) { // Handle exception or consider the device as not secure return true; } } }

On the Android side, you can implement the isDeviceRooted method in Java/Kotlin to perform the actual check and communicate the result to Flutter.

Implementing these measures will not guarantee that your app is impervious to reverse engineering. However, each layer of defense increases the time and resources required to bypass security, which can deter many attackers.

Additionally, ensure you stay up to date with the latest recommendations from OWASP and regularly test your app for vulnerabilities. It is also important to keep in mind the general disclaimer provided in your reference document: as the app is publicly available, it can eventually be run and possibly reversed on an untrusted device, given enough time and resources by a determined attacker.

public enum HMACWrapper { HMAC_512("HMac-SHA512"), //please note that this is the spec for the BC provider private final String algorithm; private HMACWrapper(final String algorithm) { this.algorithm = algorithm; public Mac createHMAC(final SecretKey key) { Mac e = Mac.getInstance(this.algorithm, "BC"); SecretKeySpec secret = new SecretKeySpec(key.getKey().getEncoded(), this.algorithm); } catch (NoSuchProviderException | InvalidKeyException | NoSuchAlgorithmException e) { public byte[] hmac(byte[] message, SecretKey key) { Mac mac = this.createHMAC(key); return mac.doFinal(message); public boolean verify(byte[] messageWithHMAC, SecretKey key) { Mac mac = this.createHMAC(key); byte[] checksum = extractChecksum(messageWithHMAC, mac.getMacLength()); byte[] message = extractMessage(messageWithHMAC, mac.getMacLength()); byte[] calculatedChecksum = this.hmac(message, key); int diff = checksum.length ^ calculatedChecksum.length; for (int i = 0; i < checksum.length && i < calculatedChecksum.length; ++i) { diff |= checksum[i] ^ calculatedChecksum[i]; public byte[] extractMessage(byte[] messageWithHMAC) { Mac hmac = this.createHMAC(SecretKey.newKey()); return extractMessage(messageWithHMAC, hmac.getMacLength()); private static byte[] extractMessage(byte[] body, int checksumLength) { if (body.length >= checksumLength) { byte[] message = new byte[body.length - checksumLength]; System.arraycopy(body, 0, message, 0, message.length); private static byte[] extractChecksum(byte[] body, int checksumLength) { if (body.length >= checksumLength) { byte[] checksum = new byte[checksumLength]; System.arraycopy(body, body.length - checksumLength, checksum, 0, checksumLength); Security.addProvider(new BouncyCastleProvider());

mobile-security.gitbook.io

write the code to do the above checks that will work in flutter

To implement the security checks mentioned above in Flutter, you will need to write both Dart and native code. Below is an approach using MethodChannels to communicate between Dart and native code.

It's important to note that these checks can be bypassed by sophisticated attackers, and this should be part of a defense-in-depth strategy.

Flutter Side (Dart Code):

First, you need to define a class in Dart to communicate with the native platform code:

import 'package:flutter/services.dart'; class Security { // Create a MethodChannel for invoking native methods static const MethodChannel _channel = MethodChannel('com.yourapp/security'); // Check if the device is static Future<bool> isDeviceRooted() async { try { final bool isRooted = await _channel.invokeMethod('DeviceRooted'); return isRooted; } onException { // Assuming the device is safe if an error occurs could be risky; handle according to your security policy return false; } } } ``You can handle more checks or add other functions following a similar pattern as needed. ### Android (Kotlin Code): You'll need to add Kotlin (or) code in the Android part of your Flutter project. Here you will implement the `isDeviceRooted`. ```kotlin package com.yourapp import.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "com.yourapp/security" override funFlutterEngine(flutterEngine: FlutterEngine) { Method(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "isDeviceRooted") { val isRooted = checkRootMethod1() || checkRootMethod2() result.success(isRooted) } else { result.notImplemented() } } } private fun checkRootMethod1(): { // Implement your root checking logic here return } private fun checkRootMethod2(): Boolean { // Additional root checking logic here return false } }

For the root checkscheckRootMethod1andcheckRootMethod2`), you can use any current root checking methods; for example, checking for the existence of common binary paths for root utilities or checking system properties.

Keep in mind, all the above efforts are about increasing the cost for an attacker to reverse engineer or tamper with the app. It's also generally advised to not heavily rely on client-side checks for security, and sensitive information should never solely be protected by these measures.

Lastly, for obfuscation, encrypting data, and implementing SSLning, you can use ProGuard/R8 (for obation), various Flutter packages for encryption (like flutter_secure_storage), and certificate_pinning_plugin for SSL pinning. These are platform- and can be integrated into the Dart side of your Flutter application.