CLI tool
Cyber-Buddy/APKHunt avatar
Cyber-Buddy/APKHunt

APKHunt turns an APK into a MASVS workbench with an honest coverage ledger

APKHunt is a comprehensive static code analysis tool for Android apps that is based on the OWASP MASVS framework. Although APKHunt is intended primarily for mobile app developers and security testers, it can be used by anyone to identify and address potential security vulnerabilities in their code.

987 stars106 forksPythonGPL-3.0

At a glance

What is it?
APKHunt is a local static analysis workbench for Android APKs: it decompiles with JADX, runs manifest and code checks mapped to OWASP MASVS groups, writes a CycloneDX SBOM, and produces coverage, entry point and threat model views. The part that sets it apart from a scanner that prints a scary list is the ledger, which records what was skipped and what stayed unknown.
Who is it for?
APKHunt is a good fit for a mobile security team that wants a repeatable static pass over an APK and wants to see the gaps in its own coverage, and it fits a developer who needs MASVS mapped findings with a saved evidence trail rather than a report generator.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 29 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A workbench that refuses to call a match a vulnerability

The framing in the project documentation is unusually careful, and it is the first thing to understand about the tool. APKHunt is described as a local Android APK static analysis workbench: it extracts manifest and code signals, maps its static checks to OWASP MASVS groups, captures a CycloneDX SBOM, and enriches versioned package identities through OSV when that service is reachable. Two sentences set the boundaries. The tool does not verify MASVS compliance, and static matches are investigation targets rather than proof of runtime reachability or exploitation. That stance runs through the whole product, down to the screenshots, which are taken from a local InsecureBankv2 sample scan, with secret values collapsed, no API host enabled for live tests, and static signals explicitly not treated as validated vulnerabilities. The project is GPL-3.0 licensed, releases v2.0.0 came out on 2026-09-24, and the release page points to notes about a new web workbench and a Go command line migration.

One scan at a time, JADX first and Apktool as the safety net

The scan pipeline is where the engineering detail sits, and it is careful about failure. An APK up to 200 MB by default is accepted and its ZIP structure is validated before a background scan is queued, so the page returns immediately and a status dock follows navigation across the application. Only one scan runs at a time. JADX goes first, and if JADX hits an error in auto mode the tool retries with simplified output; individual DEX files are retried in a bounded way only when the APK level attempts leave no usable Java or Kotlin source at all. That per file rescue uses the `--show-bad-code` flag, which is described as optional for the initial passes because it significantly slowed the reference APK without resolving its method errors. Apktool then supplies fallback manifest and resources when JADX returns errors, and it also decodes Smali, whose file count is reported separately because the Java and Kotlin rules never inspect Smali instructions. One more caveat worth knowing: when JADX emits work unit progress, that percentage applies to the current JADX attempt, not to the scan as a whole.

What the rule engine reads and what it skips

Rule evaluation happens in two passes. The first evaluates structural manifest rules and the manifest linked Network Security Configuration. The second walks the recovered JADX files recursively and applies static rules to them. Text files are read when they are eligible, up to 10 MB each, and the documentation is explicit that only Java, Kotlin and XML currently carry security rules. The report records scanned and skipped file counts, which is the mechanism that makes the coverage ledger meaningful later: the tool knows what it did not look at. The repository layout backs this up, with a `rules.json` at the top level, a `masvs_catalog.py` for the mapping, a `rule_triager.py`, a `sensitive_log_rule.py`, a `scan_extensions.py` and a `network_policy.py` for the manifest linked configuration. The current rule set is described as 39 checks, made up of 38 active signals and one inventory only rule. The documentation does not claim any detection accuracy, and a tool that reports coverage rather than a hit rate is a different kind of instrument.

The coverage ledger is the honest part of the report

The saved report is where most static tools lose the reader, and APKHunt splits the evidence into distinct pages. The coverage ledger inspects which archive members were copied out, which recovered files and rules were evaluated, which paths remain skipped or unknown, and it keeps listing limits and failed JADX attempts visible rather than hiding them. The entry point graph connects manifest declarations to recovered handler class evidence, and the documentation is careful about what that means: code in the same class is a lead, not a proven call path, and the graph does not claim execution. The threat model opens an interactive map for a saved entry path with Android trust boundaries, linked rule findings, exact source references, STRIDE review questions, a counter hypothesis and a path specific proof step. It is stated to be deterministic and to use no AI, the JSON can be downloaded for further review, and the graph library is Cytoscape.js, included locally under the MIT licence. What this combination buys you is a review trail rather than a score.

Running the stack needs Docker and a generated secret

The deployment path is a Compose stack, and the documentation is explicit about the two requirements: Docker Desktop with Docker Compose v2, and a unique application secret. From the project directory:

bash
export SECRET_KEY="$(openssl rand -hex 32)"
docker compose up --build

Then open http://localhost:5005. The Compose file binds the service to 127.0.0.1 on port 5005, so the workbench is not exposed on every interface by default, and it refuses to start without the secret, since the environment line requires `SECRET_KEY` to be set. The stack stores uploads, decompiled output, reports, scan history and worker status in named volumes, so a restart does not lose saved scans. Two lifecycle details are documented and easy to get wrong. The stack is stopped with `docker compose down`, and the `--volumes` flag should be added only when you deliberately want to delete all saved scan data. And an application restart interrupts a running worker, in which case the restored status marks that scan as interrupted rather than claiming it is still running. The image also pins its tools, with JADX 1.5.1 and Apktool 2.10.0 installed at build time alongside Syft and TruffleHog.

Supply chain evidence and secret candidates, kept separate

Three analysis steps produce material that is deliberately kept apart from the security findings. A CycloneDX SBOM is created with Syft when it is available, which is what feeds the dependency dashboard. OSV is queried once for each versioned package URL, and unavailable or unknown states are preserved instead of being turned into a severity, so an empty result never masquerades as a clean bill of health. A separate static API route inventory and a TruffleHog credential candidate snapshot are saved alongside the report. Here the care is in the filtering: Android XML namespace URLs are ignored, query strings and token like path segments are removed from the route evidence, and no extracted host is contacted during a scan. TruffleHog does not verify candidates with providers unless the upload explicitly opts in, and the secret review page reveals full candidate values only on request, which is why the screenshot shows them collapsed. Opening a saved report never reruns the decompilers, the route extraction, TruffleHog or OSV, so a review of an old scan is reproducible.

Runtime proof and route probes, worded to stay inside their limits

Two features reach beyond static reading, and both are described in language that limits the claim. The runtime proof workbench shows whether ADB is ready, then launches an explicitly exported activity from its saved graph entry, checking the hash of the installed base APK before the launch and recording the ADB result. The documentation calls this shell identity launchability evidence and states that it is not proof of an app to app vulnerability. The API route evidence page asks you to choose one exact authorised hostname taken from source code URL evidence, then runs a bounded anonymous GET for that concrete set of HTTPS routes through curl, saving the status, the response body and a digest. A 2xx answer is labelled as responding without credentials rather than as a confirmed authorisation failure, and the older manual comparison page remains available at its direct URL for saved cases. The pattern across the whole tool is consistent: each workbench produces a fact about the app, and the fact stops short of being called a finding.

Editorial conclusion

APKHunt is a good fit for a mobile security team that wants a repeatable static pass over an APK and wants to see the gaps in its own coverage, and it fits a developer who needs MASVS mapped findings with a saved evidence trail rather than a report generator. It is not a fit for a release gate on its own, because the documentation states plainly that it does not verify MASVS compliance and that a static match is an investigation target rather than proof of reachability, and it is not a fit for an environment without a device, since the runtime proof workbench needs ADB. Before you trust a scan, check three things: the coverage ledger for the files that were skipped rather than the findings list, the decompiler attempt history in the same ledger, and the version of the pinned JADX and Apktool images, because the newest release at the time of writing, v2.0.0, also carries migration notes about a web workbench and a Go command line.

Frequently asked questions

How do I run APKHunt on my machine?

With Docker Desktop and Docker Compose v2, export a unique secret and start the stack from the project directory with `export SECRET_KEY="$(openssl rand -hex 32)"` followed by `docker compose up --build`, then open http://localhost:5005. The Compose file binds the service to 127.0.0.1 on port 5005.

Does APKHunt verify OWASP MASVS compliance?

No. The documentation states that APKHunt maps its static checks to OWASP MASVS groups but does not verify compliance, and that static matches are investigation targets rather than proof of runtime reachability or exploitation.

What does APKHunt do when JADX fails to decompile an APK?

It retries JADX with simplified output after an auto mode error, retries individual DEX files in a bounded way when no usable source is recovered, and falls back to Apktool for the manifest and selected resources. The per DEX rescue uses `--show-bad-code`, which the documentation says slowed the reference APK significantly without resolving its method errors.

Does APKHunt contact the servers found in an app's URLs?

No. The static API route inventory ignores Android XML namespace URLs, removes query strings and token like path segments, and never contacts an extracted host. The live route page only runs a bounded anonymous GET after you pick one exact authorised hostname yourself.

Where does APKHunt store scan results?

The Compose stack keeps uploads, decompiled output, reports, scan history and worker status in named Docker volumes. `docker compose down` stops the stack, and the `--volumes` flag removes all saved scan data as well.

Official sources

  1. Cyber-Buddy/APKHunt on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cyber-buddy-apkhunt.svg)](https://hysenlabs.com/projects/cyber-buddy-apkhunt)