mobile security

Performing Android App Static Analysis with MobSF

Performs automated static analysis of Android applications using Mobile Security Framework (MobSF) to identify hardcoded secrets, insecure permissions, vulnerable components, weak cryptography, and code-level security flaws without executing the application. Use when assessing Android APK/AAB files for security vulnerabilities before deployment, during penetration testing, or as part of CI/CD security gates. Activates for requests involving Android static analysis, MobSF scanning, APK security assessment, or mobile application code review.

androidmobile-securitymobsfowasp-mobilepenetration-testingstatic-analysis
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

Use this skill when:

  • Conducting security assessment of Android APK or AAB files before production release
  • Integrating automated mobile security scanning into CI/CD pipelines
  • Performing initial triage of Android applications during penetration testing engagements
  • Reviewing third-party Android applications for supply chain security risks

Do not use this skill as a replacement for manual code review or dynamic analysis -- MobSF static analysis catches pattern-based vulnerabilities but misses runtime logic flaws.

Prerequisites

  • MobSF v4.x installed via Docker (docker pull opensecurity/mobile-security-framework-mobsf) or local setup
  • Target Android APK, AAB, or source code ZIP
  • Python 3.10+ for MobSF REST API integration
  • JADX decompiler (bundled with MobSF) for Java/Kotlin source recovery
  • Network access to MobSF web interface (default: http://localhost:8000)

Workflow

Step 1: Deploy MobSF and Obtain API Key

Launch MobSF using Docker for isolated, reproducible scanning:

docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest

Retrieve the REST API key from the MobSF web interface at http://localhost:8000/api_docs or from the startup console output. The API key enables programmatic scanning.

Step 2: Upload APK for Static Analysis

Upload the target APK using the MobSF REST API:

curl -F "file=@target_app.apk" http://localhost:8000/api/v1/upload \
  -H "Authorization: <API_KEY>"

Response includes the hash identifier used for subsequent API calls. MobSF automatically decompiles the APK using JADX, extracts the AndroidManifest.xml, and indexes all resources.

Step 3: Trigger and Retrieve Static Scan Results

Initiate the static scan and retrieve results:

# Trigger scan
curl -X POST http://localhost:8000/api/v1/scan \
  -H "Authorization: <API_KEY>" \
  -d "scan_type=apk&file_name=target_app.apk&hash=<FILE_HASH>"
 
# Retrieve JSON report
curl -X POST http://localhost:8000/api/v1/report_json \
  -H "Authorization: <API_KEY>" \
  -d "hash=<FILE_HASH>"

Step 4: Analyze Critical Findings

MobSF static analysis covers these categories mapped to OWASP Mobile Top 10 2024:

Manifest Analysis (M8 - Security Misconfiguration):

  • Exported activities, services, receivers, and content providers without permission guards
  • android:debuggable="true" left enabled
  • android:allowBackup="true" enabling data extraction via ADB
  • Missing android:networkSecurityConfig for certificate pinning

Code Analysis (M1 - Improper Credential Usage):

  • Hardcoded API keys, passwords, and tokens in Java/Kotlin source
  • Insecure SharedPreferences usage for storing sensitive data
  • Weak or broken cryptographic implementations (ECB mode, static IV, hardcoded keys)

Network Security (M5 - Insecure Communication):

  • Missing certificate pinning configuration
  • Custom TrustManagers that accept all certificates
  • Cleartext HTTP traffic allowed without exception domains

Binary Analysis (M7 - Insufficient Binary Protections):

  • Missing ProGuard/R8 obfuscation
  • Native library vulnerabilities (stack canaries, NX bit, PIE)
  • Debugger detection absence

Step 5: Generate and Export Reports

Export findings in multiple formats for stakeholder communication:

# PDF report
curl -X POST http://localhost:8000/api/v1/download_pdf \
  -H "Authorization: <API_KEY>" \
  -d "hash=<FILE_HASH>" -o report.pdf
 
# JSON for programmatic processing
curl -X POST http://localhost:8000/api/v1/report_json \
  -H "Authorization: <API_KEY>" \
  -d "hash=<FILE_HASH>" -o report.json

Step 6: Integrate into CI/CD Pipeline

Add MobSF scanning as a build gate:

# GitHub Actions example
- name: MobSF Static Analysis
  run: |
    UPLOAD=$(curl -s -F "file=@app/build/outputs/apk/release/app-release.apk" \
      http://mobsf:8000/api/v1/upload -H "Authorization: $MOBSF_API_KEY")
    HASH=$(echo $UPLOAD | jq -r '.hash')
    curl -s -X POST http://mobsf:8000/api/v1/scan \
      -H "Authorization: $MOBSF_API_KEY" \
      -d "scan_type=apk&file_name=app-release.apk&hash=$HASH"
    SCORE=$(curl -s -X POST http://mobsf:8000/api/v1/scorecard \
      -H "Authorization: $MOBSF_API_KEY" -d "hash=$HASH" | jq '.security_score')
    if [ "$SCORE" -lt 60 ]; then exit 1; fi

Key Concepts

Term Definition
Static Analysis Examination of application code and resources without executing the program; catches structural and pattern-based vulnerabilities
APK Decompilation Process of recovering Java/Kotlin source from compiled Dalvik bytecode using tools like JADX or apktool
AndroidManifest.xml Configuration file declaring app components, permissions, and security attributes; primary target for manifest analysis
Certificate Pinning Technique binding an app to specific server certificates to prevent man-in-the-middle attacks via rogue CAs
ProGuard/R8 Code obfuscation and shrinking tools that make reverse engineering more difficult by renaming classes and removing unused code

Tools & Systems

  • MobSF: Automated mobile security analysis framework supporting static and dynamic analysis of Android/iOS apps
  • JADX: Dex-to-Java decompiler for recovering readable source code from Android APK files
  • apktool: Tool for reverse engineering Android APK files, decoding resources to near-original form
  • Android Lint: Google's static analysis tool for Android-specific code quality and security issues
  • Semgrep: Pattern-based static analysis engine with mobile-specific rule packs for custom vulnerability detection

Common Pitfalls

  • Ignoring false positives: MobSF flags patterns like password in variable names even when not storing actual credentials. Triage all HIGH findings manually before reporting.
  • Missing obfuscated code: Static analysis accuracy drops significantly against obfuscated apps. Supplement with dynamic analysis for apps using DexGuard or custom packers.
  • Outdated MobSF rules: Security rules evolve with Android API levels. Ensure MobSF is updated to match the target app's targetSdkVersion.
  • Skipping native code analysis: MobSF analyzes Java/Kotlin but has limited coverage of native C/C++ libraries. Use checksec and manual review for .so files.
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md5.5 KB

API Reference: MobSF Android Static Analysis

Libraries Used

Library Purpose
requests HTTP client for MobSF REST API v1
json Parse scan reports and finding data
os Read MOBSF_URL and MOBSF_API_KEY environment variables

Installation

pip install requests
 
# MobSF server (Docker)
docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf

Authentication

MobSF uses API key authentication. The default key is shown on the MobSF dashboard:

import requests
import os
 
MOBSF_URL = os.environ.get("MOBSF_URL", "http://localhost:8000")
MOBSF_KEY = os.environ["MOBSF_API_KEY"]
headers = {"Authorization": MOBSF_KEY}

REST API v1 Endpoints

Method Endpoint Description
POST /api/v1/upload Upload APK, IPA, ZIP, or APPX for analysis
POST /api/v1/scan Trigger static analysis on uploaded file
GET /api/v1/report_json Get full JSON analysis report
POST /api/v1/download_pdf Download PDF report
GET /api/v1/scans List recent scans
POST /api/v1/delete_scan Delete a scan and its data
POST /api/v1/search Search scans by hash or filename
POST /api/v1/compare Compare two app scans
GET /api/v1/scorecard Get app security scorecard
GET /api/v1/scan_logs View live scan logs

Core Operations

Upload an APK

def upload_apk(file_path):
    with open(file_path, "rb") as f:
        resp = requests.post(
            f"{MOBSF_URL}/api/v1/upload",
            files={"file": (os.path.basename(file_path), f, "application/octet-stream")},
            headers=headers,
            timeout=120,
        )
    resp.raise_for_status()
    result = resp.json()
    return result["hash"], result["scan_type"], result["file_name"]
    # hash: SHA-256 of the uploaded file
    # scan_type: "apk", "ipa", "zip", "appx"

Trigger Static Analysis

def start_scan(file_hash, scan_type, file_name):
    resp = requests.post(
        f"{MOBSF_URL}/api/v1/scan",
        data={
            "hash": file_hash,
            "scan_type": scan_type,
            "file_name": file_name,
        },
        headers=headers,
        timeout=600,  # Scans can take several minutes
    )
    resp.raise_for_status()
    return resp.json()

Retrieve JSON Report

def get_report(file_hash):
    resp = requests.post(
        f"{MOBSF_URL}/api/v1/report_json",
        data={"hash": file_hash},
        headers=headers,
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json()

Extract Key Findings

def extract_findings(report):
    findings = {
        "security_score": report.get("security_score", "N/A"),
        "app_name": report.get("app_name"),
        "package_name": report.get("package_name"),
        "target_sdk": report.get("target_sdk"),
        "min_sdk": report.get("min_sdk"),
        "permissions": {
            "dangerous": [],
            "normal": [],
        },
        "manifest_issues": [],
        "code_issues": [],
        "binary_issues": [],
    }
 
    # Dangerous permissions
    for perm, details in report.get("permissions", {}).items():
        status = details.get("status", "normal")
        if status == "dangerous":
            findings["permissions"]["dangerous"].append(perm)
        else:
            findings["permissions"]["normal"].append(perm)
 
    # Manifest analysis
    for issue in report.get("manifest_analysis", []):
        if issue.get("severity") in ("high", "warning"):
            findings["manifest_issues"].append({
                "title": issue["title"],
                "severity": issue["severity"],
                "description": issue["description"],
            })
 
    # Code analysis
    for issue_key, issue_data in report.get("code_analysis", {}).items():
        findings["code_issues"].append({
            "rule": issue_key,
            "severity": issue_data.get("level"),
            "description": issue_data.get("description"),
            "files": issue_data.get("path", [])[:5],
        })
 
    return findings

Download PDF Report

def download_pdf(file_hash, output_path):
    resp = requests.post(
        f"{MOBSF_URL}/api/v1/download_pdf",
        data={"hash": file_hash},
        headers=headers,
        timeout=120,
    )
    resp.raise_for_status()
    with open(output_path, "wb") as f:
        f.write(resp.content)

Compare Two Applications

resp = requests.post(
    f"{MOBSF_URL}/api/v1/compare",
    data={"hash1": hash_v1, "hash2": hash_v2},
    headers=headers,
    timeout=120,
)
comparison = resp.json()
# Shows permission changes, new vulnerabilities, code changes

Output Format

{
  "file_name": "app-debug.apk",
  "app_name": "TestApp",
  "package_name": "com.example.testapp",
  "security_score": 42,
  "target_sdk": "33",
  "min_sdk": "24",
  "permissions": {
    "android.permission.INTERNET": {"status": "normal", "description": "..."},
    "android.permission.READ_CONTACTS": {"status": "dangerous", "description": "..."}
  },
  "manifest_analysis": [
    {"title": "Application is debuggable", "severity": "high", "description": "..."}
  ],
  "code_analysis": {
    "android_insecure_random": {
      "level": "high",
      "description": "Insecure Random Number Generator",
      "path": ["com/example/CryptoUtils.java"]
    }
  },
  "binary_analysis": [
    {"title": "NX bit not set", "severity": "high"}
  ]
}
standards.md2.9 KB

Standards Reference: Android Static Analysis with MobSF

OWASP Mobile Top 10 2024 Mapping

OWASP ID Risk MobSF Coverage
M1 Improper Credential Usage Detects hardcoded API keys, passwords, tokens in source code and resources
M2 Inadequate Supply Chain Security Identifies third-party library versions with known CVEs
M5 Insecure Communication Flags missing certificate pinning, cleartext traffic, weak TLS
M7 Insufficient Binary Protections Checks ProGuard/R8 obfuscation, native binary protections
M8 Security Misconfiguration Analyzes AndroidManifest.xml for exported components, debug flags, backup settings
M9 Insecure Data Storage Detects SharedPreferences misuse, world-readable files, SQLite without encryption
M10 Insufficient Cryptography Identifies ECB mode, static IV, hardcoded encryption keys, weak algorithms

OWASP MASVS v2.0 Control Mapping

MASVS Category Controls MobSF Static Checks
MASVS-STORAGE Sensitive data storage SharedPreferences analysis, file permission checks, database encryption
MASVS-CRYPTO Cryptographic implementations Algorithm strength, key management, IV randomness
MASVS-AUTH Authentication mechanisms Credential storage, biometric implementation review
MASVS-NETWORK Network security Network security config, certificate pinning, cleartext detection
MASVS-PLATFORM Platform interaction Intent filter analysis, content provider security, WebView configuration
MASVS-CODE Code quality Code obfuscation, debug symbols, error handling
MASVS-RESILIENCE Reverse engineering resistance Root detection, tamper detection, debugger detection

NIST SP 800-163 Rev 1: Vetting the Security of Mobile Applications

  • Section 4.1: Static analysis as mandatory step in mobile app vetting process
  • Section 4.2: Automated tools should check for known vulnerability patterns
  • Section 5: Integration of vetting into enterprise mobile device management

CWE Mappings for Common MobSF Findings

CWE ID Title MobSF Finding Category
CWE-312 Cleartext Storage of Sensitive Information Hardcoded credentials in source
CWE-319 Cleartext Transmission of Sensitive Information Missing HTTPS enforcement
CWE-327 Use of Broken Cryptographic Algorithm Weak crypto detection
CWE-330 Use of Insufficiently Random Values Static IV, predictable random
CWE-532 Insertion of Sensitive Information into Log File Logging sensitive data
CWE-749 Exposed Dangerous Method or Function Exported components without guards
CWE-919 Weaknesses in Mobile Applications General mobile-specific checks
CWE-925 Improper Verification of Intent by Broadcast Receiver Unprotected broadcast receivers
workflows.md3.7 KB

Workflows: Android Static Analysis with MobSF

Workflow 1: Standalone APK Assessment

[Obtain APK] --> [Deploy MobSF Docker] --> [Upload via API] --> [Run Static Scan]
     |                                                               |
     v                                                               v
[Verify APK integrity]                                    [Review Manifest Analysis]
[Check signing certificate]                               [Review Code Analysis]
                                                          [Review Binary Analysis]
                                                          [Review Network Analysis]
                                                                     |
                                                                     v
                                                          [Triage HIGH/CRITICAL findings]
                                                          [Validate false positives]
                                                          [Generate PDF report]

Workflow 2: CI/CD Pipeline Integration

[Developer pushes code] --> [Build APK] --> [Upload to MobSF] --> [Static Scan]
                                                                      |
                                                          +-----------+-----------+
                                                          |                       |
                                                   [Score >= 60]           [Score < 60]
                                                          |                       |
                                                   [Pass gate]            [Fail build]
                                                   [Archive report]       [Notify developer]
                                                   [Continue pipeline]    [Block deployment]

Workflow 3: Third-Party App Vetting

[Receive third-party APK] --> [MobSF Static Scan] --> [Automated scoring]
                                                            |
                                                            v
                                                    [Manual review of:]
                                                    - Excessive permissions
                                                    - Data exfiltration indicators
                                                    - Known malware signatures
                                                    - C2 communication patterns
                                                            |
                                                            v
                                                    [Risk assessment report]
                                                    [Approve/Reject for enterprise use]

Workflow 4: Comparative Analysis Across Versions

[APK v1.0] --> [MobSF Scan] --> [Baseline report]
                                       |
[APK v2.0] --> [MobSF Scan] --> [Compare findings] --> [New vulnerabilities introduced?]
                                       |                        |
                                       v                 [Yes: Block release]
                                [Regression report]      [No: Approve release]

Decision Matrix: When to Escalate

Finding Severity MobSF Category Action
CRITICAL Hardcoded production API keys Immediate key rotation, block release
HIGH Exported activity with sensitive data Manual verification, fix before release
MEDIUM Missing certificate pinning Add to sprint backlog, risk acceptance if internal app
LOW Debug logging of non-sensitive data Track in issue tracker, fix in next release
INFO Missing ProGuard rules Recommend but do not block

Scripts 2

agent.py9.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""MobSF Android static analysis agent.

Automates APK upload, static analysis scanning, and report retrieval
via the MobSF REST API. Extracts security findings including manifest
analysis, code analysis, binary analysis, and certificate checks.
"""
import argparse
import json
import os
import sys
import time
from datetime import datetime, timezone

try:
    import requests
except ImportError:
    print("[!] 'requests' library required: pip install requests", file=sys.stderr)
    sys.exit(1)


def get_mobsf_config():
    """Return MobSF server URL and API key from env or defaults."""
    server = os.environ.get("MOBSF_URL", "http://localhost:8000")
    api_key = os.environ.get("MOBSF_API_KEY", "")
    if not api_key:
        print("[!] Set MOBSF_API_KEY environment variable", file=sys.stderr)
        sys.exit(1)
    return server.rstrip("/"), api_key


def upload_apk(server, api_key, apk_path):
    """Upload an APK file to MobSF and return the file hash."""
    url = f"{server}/api/v1/upload"
    headers = {"Authorization": api_key}
    if not os.path.isfile(apk_path):
        print(f"[!] File not found: {apk_path}", file=sys.stderr)
        sys.exit(1)
    print(f"[*] Uploading {os.path.basename(apk_path)} to MobSF...")
    with open(apk_path, "rb") as f:
        resp = requests.post(
            url,
            files={"file": (os.path.basename(apk_path), f, "application/octet-stream")},
            headers=headers,
            timeout=300,
        )
    resp.raise_for_status()
    data = resp.json()
    file_hash = data.get("hash", "")
    scan_type = data.get("scan_type", "apk")
    file_name = data.get("file_name", os.path.basename(apk_path))
    print(f"[+] Uploaded: {file_name} (hash: {file_hash}, type: {scan_type})")
    return file_hash, scan_type, file_name


def start_scan(server, api_key, file_hash, scan_type, file_name):
    """Trigger static analysis scan on the uploaded APK."""
    url = f"{server}/api/v1/scan"
    headers = {"Authorization": api_key}
    print(f"[*] Starting static analysis scan for {file_name}...")
    resp = requests.post(
        url,
        data={"hash": file_hash, "scan_type": scan_type, "file_name": file_name},
        headers=headers,
        timeout=600,
    )
    resp.raise_for_status()
    print("[+] Scan complete")
    return resp.json()


def get_report(server, api_key, file_hash):
    """Retrieve the JSON report for a scanned APK."""
    url = f"{server}/api/v1/report_json"
    headers = {"Authorization": api_key}
    resp = requests.post(
        url,
        data={"hash": file_hash},
        headers=headers,
        timeout=120,
    )
    resp.raise_for_status()
    return resp.json()


def get_scorecard(server, api_key, file_hash):
    """Retrieve the security scorecard for a scanned APK."""
    url = f"{server}/api/v1/scorecard"
    headers = {"Authorization": api_key}
    resp = requests.post(
        url,
        data={"hash": file_hash},
        headers=headers,
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json()


def extract_findings(report):
    """Extract structured security findings from MobSF report JSON."""
    findings = []

    # Manifest analysis
    for item in report.get("manifest_analysis", []):
        findings.append({
            "category": "manifest",
            "title": item.get("title", "Unknown"),
            "severity": item.get("severity", "info").upper(),
            "description": item.get("description", ""),
        })

    # Code analysis
    code_analysis = report.get("code_analysis", {})
    if isinstance(code_analysis, dict):
        for key, value in code_analysis.items():
            if isinstance(value, dict):
                findings.append({
                    "category": "code",
                    "title": value.get("metadata", {}).get("description", key),
                    "severity": value.get("metadata", {}).get("severity", "info").upper(),
                    "description": value.get("metadata", {}).get("cwe", ""),
                    "files": list(value.get("files", {}).keys())[:5],
                })

    # Binary analysis
    for item in report.get("binary_analysis", []):
        findings.append({
            "category": "binary",
            "title": item.get("name", "Unknown"),
            "severity": item.get("severity", "info").upper(),
            "description": item.get("description", ""),
        })

    # Certificate analysis
    cert_info = report.get("certificate_analysis", {})
    if isinstance(cert_info, dict):
        cert_findings = cert_info.get("certificate_findings", [])
        for item in cert_findings:
            findings.append({
                "category": "certificate",
                "title": item.get("title", "Certificate finding"),
                "severity": item.get("severity", "info").upper(),
                "description": item.get("description", ""),
            })

    # Permissions
    permissions = report.get("permissions", {})
    dangerous_perms = []
    if isinstance(permissions, dict):
        for perm, details in permissions.items():
            if isinstance(details, dict) and details.get("status") == "dangerous":
                dangerous_perms.append(perm)
    if dangerous_perms:
        findings.append({
            "category": "permissions",
            "title": f"{len(dangerous_perms)} dangerous permissions declared",
            "severity": "WARNING",
            "description": ", ".join(dangerous_perms[:10]),
        })

    return findings


def format_summary(report, findings):
    """Print a human-readable summary of the analysis."""
    app_name = report.get("app_name", "Unknown")
    package = report.get("package_name", "Unknown")
    version = report.get("version_name", "Unknown")
    sdk_min = report.get("min_sdk", "?")
    sdk_target = report.get("target_sdk", "?")
    security_score = report.get("security_score", "N/A")

    print(f"\n{'='*60}")
    print(f"  MobSF Static Analysis Report")
    print(f"{'='*60}")
    print(f"  App Name    : {app_name}")
    print(f"  Package     : {package}")
    print(f"  Version     : {version}")
    print(f"  Min SDK     : {sdk_min} | Target SDK: {sdk_target}")
    print(f"  Security    : {security_score}/100")
    print(f"{'='*60}")

    severity_counts = {}
    for f in findings:
        sev = f.get("severity", "INFO")
        severity_counts[sev] = severity_counts.get(sev, 0) + 1

    print(f"\n  Findings Summary:")
    for sev in ["CRITICAL", "HIGH", "WARNING", "MEDIUM", "INFO", "LOW"]:
        if sev in severity_counts:
            print(f"    {sev:10s}: {severity_counts[sev]}")

    print(f"\n  Top Findings:")
    high_findings = [f for f in findings if f.get("severity") in ("CRITICAL", "HIGH", "WARNING")]
    for f in high_findings[:10]:
        print(f"    [{f['severity']:8s}] [{f['category']:12s}] {f['title']}")

    return severity_counts


def main():
    parser = argparse.ArgumentParser(
        description="MobSF Android static analysis agent - upload, scan, and report"
    )
    parser.add_argument("--apk", required=True, help="Path to the APK file to analyze")
    parser.add_argument("--output", "-o", help="Output JSON report path")
    parser.add_argument("--server", help="MobSF server URL (or set MOBSF_URL env var)")
    parser.add_argument("--api-key", help="MobSF API key (or set MOBSF_API_KEY env var)")
    parser.add_argument("--hash", help="Skip upload; use existing hash to retrieve report")
    parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
    args = parser.parse_args()

    if args.server:
        os.environ["MOBSF_URL"] = args.server
    if args.api_key:
        os.environ["MOBSF_API_KEY"] = args.api_key

    server, api_key = get_mobsf_config()

    if args.hash:
        file_hash = args.hash
        print(f"[*] Using existing hash: {file_hash}")
    else:
        file_hash, scan_type, file_name = upload_apk(server, api_key, args.apk)
        start_scan(server, api_key, file_hash, scan_type, file_name)

    report = get_report(server, api_key, file_hash)
    findings = extract_findings(report)
    severity_counts = format_summary(report, findings)

    output_report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "tool": "MobSF",
        "apk": args.apk,
        "app_name": report.get("app_name", ""),
        "package_name": report.get("package_name", ""),
        "version": report.get("version_name", ""),
        "security_score": report.get("security_score", 0),
        "severity_counts": severity_counts,
        "findings": findings,
        "risk_level": (
            "CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
            else "HIGH" if severity_counts.get("HIGH", 0) > 0
            else "MEDIUM" if severity_counts.get("WARNING", 0) > 0
            else "LOW"
        ),
    }

    if args.output:
        with open(args.output, "w") as f:
            json.dump(output_report, f, indent=2)
        print(f"\n[+] Report saved to {args.output}")
    elif args.verbose:
        print(json.dumps(output_report, indent=2))

    print(f"\n[*] Risk Level: {output_report['risk_level']}")


if __name__ == "__main__":
    main()
process.py8.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
MobSF Automated Static Analysis Pipeline

Automates APK upload, scanning, and report generation via MobSF REST API.
Designed for CI/CD integration with configurable security score thresholds.

Usage:
    python process.py --apk target.apk --api-key <KEY> [--threshold 60] [--output report.json]
"""

import argparse
import json
import sys
import time
import hashlib
from pathlib import Path
from urllib.parse import urljoin

try:
    import requests
except ImportError:
    print("ERROR: 'requests' package required. Install with: pip install requests")
    sys.exit(1)


class MobSFScanner:
    """Interfaces with MobSF REST API for automated static analysis."""

    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({"Authorization": api_key})

    def upload_apk(self, apk_path: str) -> dict:
        """Upload APK file to MobSF for analysis."""
        endpoint = f"{self.base_url}/api/v1/upload"
        apk_file = Path(apk_path)

        if not apk_file.exists():
            raise FileNotFoundError(f"APK not found: {apk_path}")

        if not apk_file.suffix.lower() in (".apk", ".aab", ".zip", ".ipa"):
            raise ValueError(f"Unsupported file type: {apk_file.suffix}")

        with open(apk_path, "rb") as f:
            files = {"file": (apk_file.name, f, "application/octet-stream")}
            response = self.session.post(endpoint, files=files)

        response.raise_for_status()
        result = response.json()
        print(f"[+] Uploaded: {apk_file.name} (hash: {result.get('hash', 'N/A')})")
        return result

    def run_static_scan(self, file_hash: str, file_name: str, scan_type: str = "apk") -> dict:
        """Trigger static analysis scan."""
        endpoint = f"{self.base_url}/api/v1/scan"
        data = {
            "scan_type": scan_type,
            "file_name": file_name,
            "hash": file_hash,
        }
        response = self.session.post(endpoint, data=data)
        response.raise_for_status()
        print(f"[+] Static scan completed for: {file_name}")
        return response.json()

    def get_report_json(self, file_hash: str) -> dict:
        """Retrieve JSON scan report."""
        endpoint = f"{self.base_url}/api/v1/report_json"
        data = {"hash": file_hash}
        response = self.session.post(endpoint, data=data)
        response.raise_for_status()
        return response.json()

    def get_scorecard(self, file_hash: str) -> dict:
        """Retrieve security scorecard."""
        endpoint = f"{self.base_url}/api/v1/scorecard"
        data = {"hash": file_hash}
        response = self.session.post(endpoint, data=data)
        response.raise_for_status()
        return response.json()

    def download_pdf_report(self, file_hash: str, output_path: str) -> str:
        """Download PDF report."""
        endpoint = f"{self.base_url}/api/v1/download_pdf"
        data = {"hash": file_hash}
        response = self.session.post(endpoint, data=data)
        response.raise_for_status()

        with open(output_path, "wb") as f:
            f.write(response.content)
        print(f"[+] PDF report saved: {output_path}")
        return output_path


def extract_critical_findings(report: dict) -> dict:
    """Extract and categorize critical findings from MobSF report."""
    findings = {
        "manifest_issues": [],
        "code_issues": [],
        "network_issues": [],
        "binary_issues": [],
        "crypto_issues": [],
    }

    # Manifest analysis
    manifest = report.get("manifest_analysis", [])
    if isinstance(manifest, list):
        for item in manifest:
            severity = item.get("stat", item.get("severity", "info"))
            if severity.lower() in ("high", "warning"):
                findings["manifest_issues"].append({
                    "title": item.get("title", "Unknown"),
                    "description": item.get("desc", item.get("description", "")),
                    "severity": severity,
                })

    # Code analysis
    code_analysis = report.get("code_analysis", {})
    if isinstance(code_analysis, dict):
        for category, items in code_analysis.items():
            if isinstance(items, dict):
                metadata = items.get("metadata", {})
                severity = metadata.get("severity", "info")
                if severity.lower() in ("high", "warning"):
                    findings["code_issues"].append({
                        "category": category,
                        "description": metadata.get("description", ""),
                        "severity": severity,
                        "files": list(items.get("files", {}).keys())[:5],
                    })

    # Network security
    network = report.get("network_security", [])
    if isinstance(network, list):
        for item in network:
            severity = item.get("severity", "info")
            if severity.lower() in ("high", "warning"):
                findings["network_issues"].append({
                    "title": item.get("scope", "Unknown"),
                    "description": item.get("description", ""),
                    "severity": severity,
                })

    return findings


def evaluate_security_score(scorecard: dict, threshold: int) -> bool:
    """Evaluate whether the app meets the minimum security threshold."""
    score = scorecard.get("security_score", 0)
    print(f"\n[*] Security Score: {score}/100 (threshold: {threshold})")

    if score >= threshold:
        print("[+] PASS: Application meets minimum security threshold")
        return True
    else:
        print("[-] FAIL: Application does not meet minimum security threshold")
        return False


def main():
    parser = argparse.ArgumentParser(
        description="MobSF Automated Static Analysis Pipeline"
    )
    parser.add_argument("--apk", required=True, help="Path to APK/AAB file")
    parser.add_argument("--api-key", required=True, help="MobSF REST API key")
    parser.add_argument(
        "--url", default="http://localhost:8000", help="MobSF server URL"
    )
    parser.add_argument(
        "--threshold", type=int, default=60, help="Minimum security score (0-100)"
    )
    parser.add_argument("--output", default="mobsf_report.json", help="Output report path")
    parser.add_argument("--pdf", help="Optional PDF report output path")
    parser.add_argument(
        "--ci-mode", action="store_true", help="Exit with non-zero code on failure"
    )
    args = parser.parse_args()

    scanner = MobSFScanner(args.url, args.api_key)

    # Upload
    upload_result = scanner.upload_apk(args.apk)
    file_hash = upload_result["hash"]
    file_name = Path(args.apk).name
    scan_type = "apk" if file_name.endswith(".apk") else "aab"

    # Scan
    scanner.run_static_scan(file_hash, file_name, scan_type)

    # Get reports
    report = scanner.get_report_json(file_hash)
    scorecard = scanner.get_scorecard(file_hash)

    # Extract findings
    critical_findings = extract_critical_findings(report)

    # Build summary
    summary = {
        "file": file_name,
        "hash": file_hash,
        "security_score": scorecard.get("security_score", 0),
        "threshold": args.threshold,
        "pass": scorecard.get("security_score", 0) >= args.threshold,
        "findings_summary": {
            "manifest_issues": len(critical_findings["manifest_issues"]),
            "code_issues": len(critical_findings["code_issues"]),
            "network_issues": len(critical_findings["network_issues"]),
            "binary_issues": len(critical_findings["binary_issues"]),
            "crypto_issues": len(critical_findings["crypto_issues"]),
        },
        "critical_findings": critical_findings,
    }

    # Save JSON report
    with open(args.output, "w") as f:
        json.dump(summary, f, indent=2)
    print(f"[+] Report saved: {args.output}")

    # Optional PDF
    if args.pdf:
        scanner.download_pdf_report(file_hash, args.pdf)

    # Evaluate
    passed = evaluate_security_score(scorecard, args.threshold)

    if args.ci_mode and not passed:
        sys.exit(1)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 3.6 KB
Keep exploring