MobSF Tutorial: Static and Dynamic Android App Security Testing

Deepak Prasad
Tested on Pentest lab — Kali GNU/Linux Rolling 2026.2 at 192.168.56.120; Genymotion Android 10 (API 29) at 192.168.56.103:5555 for Dynamic Analyzer
Proxmox lab — same MobSF Docker workflow when Genymotion or a rooted AVD is reachable from the Kali VM
Package Docker image opensecurity/mobile-security-framework-mobsf:v4.5.2
Lab environment Pentest lab
Genymotion Desktop lab
Android Studio AVD lab
Privilege sudo for Docker; browser, REST API, and adb as a normal user
Cert prep CompTIA PenTest+ (application / mobile testing concepts)
Scope Start MobSF 4.5.x with Docker, upload InsecureShop, read the static report, map leads to MASVS/MASTG, run Dynamic Analyzer on a rooted emulator, and validate one exported activity at runtime. Does not cover iOS deep-dives, full Frida courses, CI automation, or scanning apps you are not authorized to test.
Related guides Frida Android tutorial
Short on time?Beta Get the gist of this 15-minute read in seconds, then ask follow-up questions

MobSF can perform both static and dynamic Android application analysis. This tutorial runs MobSF v4.5.2 in Docker on Kali, scans the intentionally vulnerable InsecureShop APK, and then validates selected static findings on a rooted Genymotion device.

Treat MobSF findings as leads until you verify the affected code, reachability, and runtime behavior. For the Android runtime, finish either the free Android Studio AVD path (Google APIs image, API ≤ 30 for Dynamic Analyzer) or Genymotion on the physical host before the dynamic sections. This walkthrough was tested with Genymotion.

IMPORTANT
Run MobSF only against APKs you own or are authorized to assess. Use intentionally vulnerable training apps such as InsecureShop in an isolated lab. Put client scope in your rules of engagement. Do not scan third-party production apps without written permission.

MobSF Android test environment

The tested setup uses the following network layout:

Role Where it runs Address used in this article
Kali (MobSF Docker, browser, adb client) VirtualBox guest 192.168.56.120
Genymotion Android (dynamic target) Physical host (not inside Kali) 192.168.56.103:5555

A practical workflow is:

  1. Finish a rooted emulator lab so Kali can see the device with adb devices as device. Use Genymotion Desktop lab (tested here) or a free Android Studio AVD with Google APIs at API ≤ 30 and no Play Store.
  2. Start MobSF with Docker and a persistent data directory. Static analysis needs only the browser on Kali.
  3. Upload InsecureShop and triage overview, manifest, permissions, code, and certificate.
  4. Map one or two leads to MASVS / MASTG language.
  5. Restart MobSF with the analyzer identifier for your emulator (host:port or emulator-5554), run Dynamic Analyzer, then validate one exported activity with adb.
  6. Skim Frida and the scorecard only after you have at least one validated finding.

Static sections below work even if the emulator is powered off. Dynamic sections need a supported rooted runtime reachable from Kali (this lab used Genymotion at 192.168.56.103:5555).


What MobSF does

MobSF is an open-source web framework for mobile app security assessment. You upload an APK (or IPA), and it produces a scored report from automated checks.

What the static pass typically covers:

  • Unpacks the APK and reads AndroidManifest.xml
  • Reads the signing certificate
  • Decompiles DEX into Java-like sources
  • Flags weak patterns (debug builds, cleartext HTTP, risky WebViews, exported components, and similar)

With a supported rooted emulator or VM it can also install the app, proxy traffic on port 1337, and stage Frida for follow-up hooks.

TIP
A MobSF finding is an automated assessment signal, not a guarantee of an exploitable vulnerability. Work each hit as: find evidence, check reachability, reproduce where you can, then report it.

Static vs dynamic analysis

Mode What MobSF looks at Emulator needed?
Static APK package, manifest, code, resources, certificate No
Dynamic Runtime behavior after install (proxy, logs, instrumentation) Yes

MobSF flow from APK static scan to report then runtime dynamic check

Static triage is enough to prioritize work. Dynamic analysis answers “does this fire on a device?” For CLI MASVS-oriented rules on the same APK without a GUI, use APKHunt.


Start MobSF with Docker

Pin the official image. On this lab I used v4.5.2. If Docker is missing on Kali (Debian-based), use install Docker on Debian first. Confirm Kali's host-only address is 192.168.56.120 before you rely on Genymotion later:

bash
ip -4 addr show | grep '192.168.56'

Sample output:

output
    inet 192.168.56.120/24 brd 192.168.56.255 scope global noprefixroute eth1

Pull the image:

bash
docker pull opensecurity/mobile-security-framework-mobsf:v4.5.2

Sample output:

output
v4.5.2: Pulling from opensecurity/mobile-security-framework-mobsf
Digest: sha256:af94f81f8b13872cdf556327fab58f52d446299fe153be6e1eeec177f31b503b
Status: Image is up to date for opensecurity/mobile-security-framework-mobsf:v4.5.2
docker.io/opensecurity/mobile-security-framework-mobsf:v4.5.2

Create a persistent MobSF data directory before the first container start. This keeps uploaded APKs, scan data, and the internal database when you restart the container for dynamic analysis later:

bash
mkdir -p "${HOME}/mobsf-data"

mkdir prints nothing on success. Give the MobSF container user ownership of that directory:

bash
sudo chown -R 9901:9901 "${HOME}/mobsf-data"

This command also exits silently when ownership changes cleanly. Start MobSF for static analysis first. Publish the UI (8000) and the dynamic proxy (1337) now so the same persistent setup can be reused for dynamic analysis later. MobSF also recommends --add-host=host.docker.internal:host-gateway for Linux Docker setups where the Android runtime or an ADB forward is exposed through the Docker host. In this lab, Genymotion itself is reached directly at 192.168.56.103:5555. For Docker container basics (pull, run, ps), skim that guide once if the commands are new:

bash
docker run -d --name mobsf --add-host=host.docker.internal:host-gateway -p 8000:8000 -p 1337:1337 -v "${HOME}/mobsf-data:/home/mobsf/.MobSF" opensecurity/mobile-security-framework-mobsf:v4.5.2

Docker prints a container ID when the container starts. Confirm both ports are mapped and the container is healthy:

bash
docker ps --filter name=mobsf --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'

Sample output:

output
NAMES     IMAGE                                                 STATUS                    PORTS
mobsf     opensecurity/mobile-security-framework-mobsf:v4.5.2   Up 16 minutes (healthy)   0.0.0.0:1337->1337/tcp, [::]:1337->1337/tcp, 0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp

You should see both 0.0.0.0:8000->8000/tcp and 0.0.0.0:1337->1337/tcp. Confirm the login page answers before you upload anything:

bash
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/login/

Sample output:

output
200

Open http://127.0.0.1:8000/ from a browser on Kali and sign in with the default mobsf / mobsf (change these on shared hosts). Optional API upload is covered briefly later, so note the REST API key now:

bash
docker logs mobsf 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | grep 'REST API Key' | tail -1

Sample output:

output
REST API Key: <your-container-key>

When you reach Dynamic Analyzer later, you will restart this container with MOBSF_ANALYZER_IDENTIFIER pointed at Genymotion. You can complete the static analysis while Genymotion is offline; the emulator is required only when you reach Dynamic Analyzer.


Upload a deliberately vulnerable APK

Use a legal training APK. InsecureShop is intentionally vulnerable and suitable for an isolated lab.

Create a working directory:

bash
mkdir -p "${HOME}/mobsf-lab"

mkdir prints nothing on success. Download the release with curl and -fL:

bash
curl -fL -o "${HOME}/mobsf-lab/insecureshop.apk" "https://github.com/optiv/InsecureShop/releases/download/v1.0/insecureshop.apk"

Confirm the size (about 4–5 MB):

bash
ls -lh "${HOME}/mobsf-lab/insecureshop.apk"

Sample output:

output
-rw-r--r-- 1 kali kali 4.6M Sep  5 07:38 /home/kali/mobsf-lab/insecureshop.apk

Upload in the browser

  1. Open http://127.0.0.1:8000/ and log in.
  2. Click Upload & Analyze, or drag insecureshop.apk onto the page.
  3. Wait for the scan to finish (first run can take several minutes).

MobSF v4.5.2 home page with Upload and Analyze button

Open Recent Scans and click InsecureShop when the row appears.

MobSF recent scans list showing InsecureShop APK

Optional: upload via API

This is only a quick upload shortcut. Set MOBSF_API_KEY to the value from docker logs, then:

bash
curl -s -F "file=@${HOME}/mobsf-lab/insecureshop.apk" http://127.0.0.1:8000/api/v1/upload -H "Authorization: ${MOBSF_API_KEY}"

Sample output:

output
{
    "analyzer": "static_analyzer",
    "status": "success",
    "hash": "c5d872355e43322f1692288e2c4e6f00",
    "scan_type": "apk",
    "file_name": "insecureshop.apk"
}

"status": "success" means MobSF accepted the APK. Continue the scan in the web interface. For API automation, /api/v1/scan uses the values returned by the upload request, including hash, scan_type, and file_name.


Read the app overview

The top of the static report is inventory: name, hashes, exported counts, and the security score.

MobSF static overview for InsecureShop with security score and exported components

Lab snapshot from this scan:

Field Value
App / package InsecureShop / com.insecureshop
Main activity com.insecureshop.ProductListActivity
Size 4.53MB
MD5 c5d872355e43322f1692288e2c4e6f00
SHA256 a83298ae4a37fcab8101e8b41e513dd2199af71a94ea537d556a318e07d4d1bd
Security score 33/100
Exported activities 5 listed (including AboutUsActivity and WebView activities)
Trackers detected 0

Record the package name, hashes, exported component counts, and signing summary before you dive into individual findings. The security score is a prioritization hint. See Report interpretation below for how to use it.


Manifest and permission analysis

Permissions requested are not proof of abuse. Ask whether the permission is used, and whether that use exposes sensitive data on the target SDK. Permission severity must be interpreted against the app's target SDK and Android version.

Open Manifest Analysis and Permissions.

MobSF manifest analysis showing cleartext traffic, debuggable, and exported activity findings

MobSF application permissions table with dangerous storage and contacts entries

Focus areas on this InsecureShop scan:

  • android:usesCleartextTraffic=true
  • android:debuggable=true
  • Exported activities without a protecting permission (including AboutUsActivity)
  • Network security / cleartext implications for HTTP stacks
  • Dangerous permissions: READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, READ_CONTACTS

Dangerous means Android treats the capability as sensitive. It does not mean MobSF already proved misuse. Storage permissions matter especially on older target SDK training apps; scoped storage on newer targets changes what the same permission names allow.

If the manifest also defines android:networkSecurityConfig, inspect that XML before concluding that cleartext traffic is allowed. On Android 7.0 (API 24) and later, usesCleartextTraffic is ignored when the app defines a Network Security Configuration, so inspect that configuration before deciding whether cleartext traffic is effectively permitted.


Code, secret, and URL findings

Open Code Analysis. Expect high and warning rows mixed with library noise.

MobSF code analysis showing debug configuration, WebView file access, external storage, and insecure SSL certificate handling findings

Three findings worth reviewing from this scan:

Finding Evidence on InsecureShop False-positive risk
WebView certificate-validation flaw CustomWebViewClient calls handler.proceed() on SSL errors Low once you open the file
Hardcoded “secret” strings Cognito identity pool id / library strings High (identifiers and third-party code often look secret)
URLs https://www.insecureshopapp.com and similar in activities Medium (confirm sensitivity and whether the host is still relevant)

MobSF labels the WebView issue as high severity with CWE-295. Open View Source for com/insecureshop/util/CustomWebViewClient.java:

text
public final class CustomWebViewClient extends WebViewClient {
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        if (handler != null) {
            handler.proceed();
        }
    }
}

handler.proceed() continues the load after a certificate failure. That is a strong static confirmation for insecure endpoint identity checks. A Cognito identity pool ID is a cloud identifier, not a secret by itself. Review IAM roles before you call it a credential leak.


Certificate and signing analysis

Click Signer Certificate (or read the certificate block on the overview).

MobSF signer certificate showing Android Debug subject and v1/v2 signatures

Lab certificate highlights:

  • Subject / issuer: CN=Android Debug, O=Android, C=US
  • v1 and v2 signatures present
  • Hash algorithm shown as SHA-1 in the certificate panel (MobSF warns about collision risk)

A production APK should not ship with the standard Android debug certificate. Treat SHA-1 notes as signing hygiene in context of scheme and deployment. That note alone is not automatic proof of a practical signature bypass.


Map findings to OWASP MASVS / MASTG

Do not recreate the whole catalog. Map the leads you care about so the report uses current language.

MobSF finding (lab) Current OWASP area Notes
usesCleartextTraffic=true MASVS-NETWORK Also inspect android:networkSecurityConfig before concluding effective cleartext policy
handler.proceed() on SSL error MASVS-NETWORK Code-confirmed above
Exported unprotected activity MASVS-PLATFORM Confirm whether sensitive behavior is externally reachable
android:debuggable=true MASVS-RESILIENCE Production builds should not expose debugging
Debug signing certificate MASVS-RESILIENCE / release integrity context Expected in a training APK; inappropriate for production distribution
Cognito identity pool ID Storage / cloud config review Identifier rather than a secret by itself

MobSF may still show legacy MSTG-* identifiers inside finding text. Prefer current OWASP MASVS / MASTG categories when you write the client report.


Configure MobSF Dynamic Analyzer

Static analysis does not need an emulator. Dynamic analysis does. MobSF's current Android Dynamic Analyzer supports rooted Android runtimes through Android 11 / API 30. This does not limit static APK analysis of apps targeting newer Android versions.

Start the Genymotion device on the physical host (finish that lab first if adb still shows nothing). From Kali, set the tested Genymotion ADB endpoint and confirm it is reachable on the same host-only segment as 192.168.56.120:

bash
export ANDROID_ADB=192.168.56.103:5555

Connect and list devices. You want device, not offline or an empty list:

bash
adb connect "${ANDROID_ADB}"

Sample output:

output
connected to 192.168.56.103:5555

List attached devices and confirm the state column is device:

bash
adb devices -l

Sample output:

output
List of devices attached
192.168.56.103:5555    device product:vbox86p model:Phone device:vbox86p transport_id:1

Restart MobSF with that identifier so the container's Dynamic Analyzer can reach Genymotion. Remove the static-only container first, then start a replacement that keeps both ports and the same persistent data volume:

bash
docker rm -f mobsf

docker rm -f prints the container name when it removes the old instance. Start the dynamic-ready container:

bash
docker run -d --name mobsf --add-host=host.docker.internal:host-gateway -e MOBSF_ANALYZER_IDENTIFIER="${ANDROID_ADB}" -p 8000:8000 -p 1337:1337 -v "${HOME}/mobsf-data:/home/mobsf/.MobSF" opensecurity/mobile-security-framework-mobsf:v4.5.2

Wait until http://127.0.0.1:8000/login/ returns 200 again, then sign in. Open Dynamic Analyzer -> Android Dynamic Analyzer. On this lab MobSF connected to Android 10 (API 29) at 192.168.56.103:5555, prepared ("MobSFy'd") the runtime by installing and configuring the components needed for dynamic analysis, including Frida support and the MobSF CA, installed InsecureShop, and marked the testing environment ready.

MobSF Android Dynamic Analyzer with Genymotion runtime and InsecureShop available

In the Apps Available (or Apps in Device) table, click Start Dynamic Analysis for InsecureShop. The first MobSFy run can take several minutes. When the session is up you get activities, exported activities, and deeplink metadata for the instrumented package.

MobSF Dynamic Analyzer session for InsecureShop after Genymotion attach

On supported Genymotion Android versions, MobSF applies the global proxy during dynamic setup, so you normally do not need to configure the device proxy manually.

What to record while the session runs:

  • Logcat / MobSF live logs while you click through the app
  • HTTP(S) traffic through the proxy on port 1337
  • Exported-activity launch attempts (next section)
  • Whether WebView paths behave as the static finding predicted

When you finish, stop the session from the UI (or POST /api/v1/dynamic/stop_analysis) so the device proxy settings do not stay pointed at MobSF.


Run dynamic analysis on Genymotion

Once the runtime is prepared and InsecureShop is available, spend a few minutes in the app while MobSF collects runtime context. Open the app from Dynamic Analyzer, click through screens that use WebViews or deeplinks, and watch what changes in the MobSF session view.

Useful runtime checks in this phase:

  • Review live logs and app activity changes
  • Confirm whether traffic appears through the proxy on port 1337
  • Note which exported activities are exposed in the dynamic view
  • Decide which static lead is worth validating next

This phase tells you whether the APK's attack surface is only theoretical or is easy to exercise on a device.


Validate exported activity reachability

Static claim: AboutUsActivity is exported without a protecting permission. That is attack surface under MASVS-PLATFORM.

Manifest evidence:

text
<activity android:exported="true" android:name="com.insecureshop.AboutUsActivity"/>

Runtime check from the host (same Genymotion adb endpoint). Launch the activity by component name:

bash
adb -s "${ANDROID_ADB}" shell am start -n com.insecureshop/.AboutUsActivity

Sample output:

output
Starting: Intent { cmp=com.insecureshop/.AboutUsActivity }

Confirm it became the resumed activity:

bash
adb -s "${ANDROID_ADB}" shell dumpsys activity activities | grep -E 'mResumedActivity|AboutUsActivity' | head -10

Sample output (trimmed):

output
intent={flg=0x10000000 cmp=com.insecureshop/.AboutUsActivity}
mActivityComponent=com.insecureshop/.AboutUsActivity
Activities=[ActivityRecord{... com.insecureshop/.AboutUsActivity ...}]

On the device the About screen opens without needing the normal in-app navigation path.

Genymotion screen showing About InsecureShop after launching exported AboutUsActivity

This confirms that the activity is externally reachable. Whether that becomes a reportable vulnerability depends on whether the activity exposes sensitive data or functionality.

Conclusion for this lab:

  • Static: exported and unprotected in the manifest (confirmed)
  • Dynamic: any app or adb session that can send the intent can open the activity (confirmed on Genymotion)
  • Report language: attack-surface / IPC finding; severity depends on what the activity exposes. Here it is a simple About UI, which demonstrates unprotected reachability rather than meaningful security impact by itself

The WebView certificate-validation flaw stays code-confirmed. Full MITM proof against that WebView path is a separate proxy exercise once traffic is flowing through port 1337.


Frida integration

During MobSFy, MobSF installs a matching frida-server on the Android runtime (this lab pulled Frida server v17.17.0 for x86). MobSF uses Frida for runtime instrumentation, but the static analysis and exported-activity validation above do not require you to write Frida scripts.

Practical rules:

  • Match the Frida client on Kali with the frida-server version on the device
  • Use Frida after the Dynamic Analyzer environment is already MobSFy’d
  • Keep hooks in scope of your RoE

Static triage and the exported-activity check above do not require Frida.


Report interpretation

Open MobSF Scorecard from the overview when you are ready to summarize the scan.

MobSF AppSec Scorecard for InsecureShop showing security score 33 of 100

Bucket Count (this lab)
Security score 33/100
High 8
Warning 11
Info 2
Secure 1
Hotspot 1
Trackers detected 0

How to read it:

  • Score and severity buckets rank work
  • They are not a pass/fail pentest grade
  • Export PDF for stakeholders, but keep your own validated list (WebView SSL in code; AboutUsActivity export proven on Genymotion)

Troubleshooting

Symptom Likely cause Fix
Scan timeout / hang First run or low resources docker logs -f mobsf; wait; rescan
APK malformed / upload fails Truncated download Re-run curl -fL; check ls -lh ~4.6M
Android Runtime not found No emulator or wrong identifier Set MOBSF_ANALYZER_IDENTIFIER to a reachable host:port; confirm adb devices shows device
Dynamic Analyzer cannot launch API > 30, Play image, or adb bridge Rooted API ≤ 30; Genymotion/AVD without Play Store; republish ports 8000 and 1337
Frida mismatch Client vs frida-server versions differ Align versions; re-MobSFy if needed
Proxy / network problems Port 1337 not published or device proxy stuck Re-run with -p 1337:1337; stop dynamic analysis when finished
Packed / obfuscated app Limited decompilation Expect thinner code findings; rely on manifest, cert, and dynamic behavior
unhealthy in docker ps host.docker.internal missing on Linux Add --add-host=host.docker.internal:host-gateway; confirm UI with curl
Static report disappears after restart Container was recreated without persistent storage Re-run MobSF with -v "${HOME}/mobsf-data:/home/mobsf/.MobSF" for both static and dynamic starts

Summary

MobSF 4.5.x in Docker gives a reproducible Android triage lab on Kali when you keep scan data in a persistent volume. On InsecureShop the static score landed at 33/100. The WebView certificate-validation flaw is visible in decompiled code, and AboutUsActivity is an exported, unprotected entry that opens from adb on Genymotion once Dynamic Analyzer has prepared the device.

Do not treat the overall score as the pentest verdict. Permissions and high findings are starting points. Validate what matters, map those leads to MASVS/MASTG language, and keep Frida for instrumentation after the runtime is ready.

Next steps that fit this lab: finish a proxy session through port 1337 against the WebView path, or compare the same APK with APKHunt for CLI MASVS-oriented output.


References


Frequently Asked Questions

Does MobSF automatically find real vulnerabilities?

No. It finds patterns and attack-surface indicators. Validate the code path, configuration, reachability, and runtime behavior before you report a vulnerability.

Can MobSF analyze an APK without source code?

Yes. You upload the APK package. MobSF unpacks it, reads the manifest and certificate, and decompiles bytecode for static rules. You do not need the original project source.

What does the MobSF security score mean?

It is a weighted summary of automated checks. A low score means triage those findings first. It is not a pass or fail pentest grade by itself.

Why does MobSF report so many false positives?

Rules match patterns. Third-party libraries and ordinary strings can hit. Open the linked file and decide if the issue is real.

Why is the Dynamic Analyzer not working?

MobSF needs a supported rooted Android emulator or VM (API 30 or lower) and the correct analyzer identifier for Docker. Confirm adb devices shows device, then set MOBSF_ANALYZER_IDENTIFIER to that host:port and restart the container.

Do I need Frida to use MobSF?

No. Static triage works without Frida. MobSF can install frida-server during MobSFy for later hooks; you only need Frida when you instrument the prepared runtime.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Kennedy Muthii

Information Security Analyst

Accomplished professional proficient in Python, ethical hacking, Linux, cybersecurity, and OSINT. With a track record including winning a national cybersecurity contest, launching a startup in Kenya, and holding a degree in information science, he is currently engaged in cutting-edge research in ethical hacking.

  • Python (programming language)
  • Certified Ethical Hacker
  • White Hat (Computer Security)
  • Linux
  • Penetration Testing