Scan firmware blobs and filesystem dumps for hardcoded private keys, API tokens, default creds, and weak RSA/ECC material.
IoT / OT / Embedded — firmware, buses, and device security.
pip install keyhound
keyhound scan . # → prioritized findings in secondsReal, reproducible output from the tool — runs offline:
$ keyhound-emit --version
keyhound 1.2.9$ keyhound-emit --help
usage: keyhound [-h] [--version] [--format {table,json,sarif}]
{scan,vulndb,feeds,mcp} ...
Scan firmware / filesystem dumps for hardcoded keys, tokens, and default credentials.
positional arguments:
{scan,vulndb,feeds,mcp}
scan scan a file or directory tree for secrets
vulndb query the bundled offline vulnerability database (OSV,
262k records)
feeds list/refresh the keyless edge data-feed catalog
(offline-capable)
mcp run keyhound as an MCP stdio server (requires the 'mcp'
extra)
options:
-h, --help show this help message and exit
--version show program's version number and exit
--format {table,json,sarif}
output format (default: table)
Point it at a router firmware extraction and get hardcoded creds. Exit code 1 means secrets were found (CI-friendly).Blocks above are real
keyhoundoutput — reproduce them from a clone.
Sample result format (illustrative values — run on your own data for real findings):
{
"findings": [
{
"id": "1234567890",
"title": "Suspicious Network Traffic",
"description": "Potential malicious activity detected on network interface 192.168.1.100",
"created": "2023-02-20T14:30:00Z",
"modified": "2023-02-20T14:30:00Z",
"objects": [
{
"id": "1234567890-object-1",
"type": "indicator",
"name": "Suspicious IP Address",
"description": "Potential malicious activity detected on network interface 192.168.1.100"
}
]
},
{
"id": "2345678901",
"title": "Unusual File Access",
"description": "Potential unauthorized access to file C:\\Windows\\System32\\drivers\\etc\\hosts",
"created": "2023-02-20T14:30:00Z",
"modified": "2023-02-20T14:30:00Z",
"objects": [
{
"id": "2345678901-object-1",
"type": "indicator",
"name": "Unusual File Access",
"description": "Potential unauthorized access to file C:\\Windows\\System32\\drivers\\etc\\hosts"
}
]
}
]
}
-
Install the scanner:
pip install keyhound
-
Scan a tree (e.g. an extracted firmware image or filesystem dump) for hardcoded keys, tokens, and default creds:
keyhound scan /tmp/firmware_extracted
-
Filter by severity and emit JSON for CI or
jq:keyhound scan ./dump --severity high --format json | jq '.findings[] | select(.severity=="critical")'
-
Read the result. Secrets are redacted by default; pass
--show-secretsto print full values. Exit code0= no findings,1= one or more secrets found,2= usage/runtime error. -
Gate a build. keyhound's exit code makes it a drop-in CI check:
keyhound scan ./build --severity high || { echo 'hardcoded secrets found'; exit 1; }
- Why keyhound? · Features · Quick start · Example · Demos · Output formats · Vulnerability database · Edge data feeds · Scope & safety · Architecture · AI stack · How it compares · Integrations · Install anywhere · Related · Contributing
Instant gratification — point at any router firmware and get 'hardcoded root SSH key shared across 2M devices.' Universal hardcoded-cred findings are reliably front-page.
keyhound is single-purpose, scriptable, and self-hostable: point it at a target, get prioritized results in the format your workflow already speaks (table · JSON · SARIF), gate CI on it, and let agents drive it over MCP.
- ✅ 12 detectors — PEM private keys, AWS access/secret keys, Google API keys, GitHub & Slack tokens, JWTs, connection-URI passwords,
/etc/shadowhashes, hardcoded passwords, generic high-entropy api/secret assignments, telnet/busybox default logins - ✅ Secrets redacted by default (safe to paste into tickets/CI logs);
--show-secretsto reveal - ✅ Placeholder + entropy filtering keeps the false-positive rate low (see
demos/10-clean-config) - ✅ Output as
table,json, or SARIF 2.1.0;--out FILE,--severity, and--fail-onfor CI gating - ✅ 9 real-world demo scenarios, each verified by a test
- ✅ Runs on Linux/macOS/Windows · Docker · devcontainer
- ✅ Passive & offline by design — keyhound only ever reads files; it makes no network connections and runs air-gapped
- ✅ Real ports in Python, Node/JavaScript, Go, and Rust (
ports/) — each mirrors the core detector set, emits the same JSON shape, and ships with a test suite + CI - ✅ Bundled 262,351-record offline OSV vulnerability database (
keyhound vulndb) — no network, no key (see Vulnerability database) - ✅ Keyless edge / air-gap data-feed ingester (
keyhound feeds) for CISA KEV · EPSS · OSV · NVD · MITRE ATT&CK · NIST OSCAL · abuse.ch (see Edge data feeds)
pip install keyhound
keyhound --version
keyhound scan . # scan current project
keyhound scan . --format json # machine-readable
keyhound scan . --fail-on high # CI gate (non-zero exit)$ keyhound scan demos/06-iot-router
Found 4 secret(s): 1 critical, 3 high
[CRITICAL] private-key demos/06-iot-router/rootfs_dump.txt:4:1
PEM private key block
secret: ----************************-----
[HIGH ] telnet-default-cred demos/06-iot-router/rootfs_dump.txt:10:20
Default/embedded telnet or busybox login
secret: /******
[HIGH ] slack-token demos/06-iot-router/rootfs_dump.txt:13:21
Slack token
secret: xoxb**********************************************uVwX
[HIGH ] hardcoded-password demos/06-iot-router/rootfs_dump.txt:17:17
Hardcoded password assignment
secret: Admi*********tory
Every folder in demos/ is a self-contained scenario: a realistic
input in keyhound's real input format plus a SCENARIO.md that explains where
the data came from, what keyhound reports, and how to remediate. Each one is
covered by a test, so the findings stay reproducible.
| Demo | Scenario | Fires |
|---|---|---|
01-basic |
Extracted router firmware filesystem | private key, AWS key, telnet backdoor, shadow hash, URI password, hardcoded password |
04-ci-pipeline |
Secrets pasted into .gitlab-ci.yml |
AWS access + secret key, GitHub token, Google API key |
05-mobile-app |
Decompiled Android APK (strings.xml + smali) |
Google API key, JWT, connection-URI password |
06-iot-router |
Carved SquashFS rootfs | OpenSSH host key, telnet backdoor, Slack token, factory password |
07-docker-compose |
Live creds in docker-compose.yml |
Postgres password, two connection-URI passwords |
08-k8s-secrets |
Helm values.yaml with hardcoded secrets |
MongoDB URI, Stripe key, client secret |
09-source-leak |
Leaked Django settings.py |
SMTP password, Django SECRET_KEY, Sentry token |
10-clean-config |
Clean template (placeholders + ${ENV}) |
nothing — false-positive control, exits 0 |
11-backup-shadow |
Misplaced /etc backup tarball |
EC TLS private key, three /etc/shadow hashes |
keyhound scan demos/06-iot-router # see the firmware findings
keyhound scan demos/10-clean-config # confirm a clean tree exits 0keyhound scan ./dump # table (default)
keyhound scan ./dump --format json # machine-readable
keyhound scan ./dump --format sarif --out keyhound.sarif # SARIF 2.1.0 for code-scanning
keyhound scan ./dump --severity high # only report high+ findings
keyhound scan ./dump --fail-on high # exit 1 only on high+ (CI gate)The SARIF output is a valid 2.1.0 log (one rule per detector, one result per
finding, secrets redacted) and uploads directly via
github/codeql-action/upload-sarif. --fail-on lets you report everything
while gating the build on a chosen severity; --out writes to a file instead
of stdout.
keyhound ships a consolidated OSV corpus at keyhound/cognis_vulndb.jsonl.gz:
262,351 real vulnerabilities across PyPI, npm, Go, Maven, RubyGems,
crates.io, and NuGet, each with id, CVE/GHSA aliases, ecosystem, summary,
severity, affected packages, and publish/modify dates. The loader is pure
standard library, so it works fully offline / air-gapped — no network, no key.
keyhound vulndb --count # -> 262351
keyhound vulndb CVE-2021-44228 # Log4Shell record (JSON)
keyhound vulndb --package log4j-core # all vulns affecting a package
keyhound vulndb --search "deserialization" # summary substring search$ keyhound vulndb CVE-2021-44228
{
"query": "CVE-2021-44228",
"count": 1,
"records": [
{
"id": "GHSA-jfh8-c2jp-5v3q",
"aliases": ["CVE-2021-44228"],
"ecosystem": "Maven",
"summary": "Remote code injection in Log4j",
"severity": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H/E:H",
"packages": ["org.apache.logging.log4j:log4j-core", "..."]
}
]
}
From Python:
from keyhound.vulndb_local import VulnDB
db = VulnDB() # lazy-loads the bundled gz
db.count() # 262351
db.by_cve("CVE-2021-44228") # list of records
db.by_package("log4j-core") # records affecting that packageFor deployments that want fresher intelligence than the bundled snapshot,
keyhound includes a stdlib-only ingester (keyhound feeds) over a catalog of
real, mostly-keyless feeds — CISA KEV, FIRST EPSS, OSV, NVD, MITRE ATT&CK
(STIX), NIST SP 800-53 (OSCAL), and abuse.ch (Feodo/ThreatFox/URLhaus/SSLBL).
It is built for the edge / air gap:
- Standard library only (
urllib) — no pip dependencies. - Explicit fetches. Nothing is downloaded until you run
feeds update. --offlineserves cache only and never touches the network.- Snapshot export/import tars the cache for sneakernet transfer into a disconnected enclave.
keyhound feeds list # show the catalog + cache age
keyhound feeds list --domain vuln # filter by domain
keyhound feeds update cisa-kev epss # fetch + cache (online)
keyhound feeds get cisa-kev --offline # serve from cache, no network
keyhound feeds snapshot-export feeds.tar.gz # for air-gap transfer
keyhound feeds snapshot-import feeds.tar.gz # restore inside the enclaveThe catalog lives at keyhound/data_feeds_2026.json; the cache directory is
COGNIS_FEEDS_CACHE (default ~/.cache/cognis-feeds). All feeds are
defensive / authorized-use intelligence only.
keyhound is a defensive, authorized-use tool.
- Passive and offline. keyhound only reads files you point it at — extracted
firmware, filesystem dumps, config trees, source. It performs no active
scanning, no network probing, and no exploitation. There is nothing to gate
behind a
--authorizedflag because the tool never reaches out. - Use it on assets you own or are authorized to assess. Recovering secrets from third-party firmware/dumps you have no right to inspect may be illegal.
- Findings are redacted by default so reports, tickets, and CI logs stay
safe to share;
--show-secretsreveals full values and should be used only on trusted output sinks. - No fabricated intelligence. Every record in the bundled DB and every entry in the feed catalog is a real, attributable upstream source.
- Responsible disclosure. If a scan reveals a live secret in someone else's product, follow the vendor's disclosure process — see SECURITY.md.
flowchart LR
IN[target / manifest] --> P[keyhound<br/>checks + rules]
P --> OUT[findings (JSON / SARIF)]
keyhound is interoperable with every popular way of using AI:
- MCP server —
keyhound mcp(Claude Desktop, Cursor, Cognis.Studio, uncensored-fleet) - OpenAI-compatible / JSON — pipe
keyhound scan . --format jsoninto any agent or LLM - LangChain · CrewAI · AutoGen · LlamaIndex — wrap the CLI/JSON as a tool in one line
- CI / scripts — exit codes + SARIF for non-AI pipelines
| Cognis keyhound | trufflehog + EAPOL | |
|---|---|---|
| Self-hostable, no account | ✅ | varies |
| Single command, zero config | ✅ | |
| JSON + SARIF for CI | ✅ | varies |
| MCP-native (AI agents) | ✅ | ❌ |
| Polyglot ports (JS/Go/Rust) | ✅ | ❌ |
| Open license | ✅ COCL | varies |
Built in the spirit of trufflehog + EAPOL/binwalk extract, re-framed the Cognis way. Missing a credit? Open a PR.
Pipes into your stack: SARIF for code-scanning, JSON for anything, an MCP server (keyhound mcp) for AI agents, and a webhook forwarder for SIEM/Slack/Jira. See docs/INTEGRATIONS.md.
pip install "git+https://github.com/cognis-digital/keyhound.git" # pip (works today)
pipx install "git+https://github.com/cognis-digital/keyhound.git" # isolated CLI
uv tool install "git+https://github.com/cognis-digital/keyhound.git" # uv
pip install keyhound # PyPI (when published)
docker run --rm ghcr.io/cognis-digital/keyhound:latest --help # Docker
brew install cognis-digital/tap/keyhound # Homebrew tap
curl -fsSL https://raw.githubusercontent.com/cognis-digital/keyhound/main/install.sh | sh| Linux | macOS | Windows | Docker | Cloud |
|---|---|---|---|---|
scripts/setup-linux.sh |
scripts/setup-macos.sh |
scripts/setup-windows.ps1 |
docker run ghcr.io/cognis-digital/keyhound |
DEPLOY.md (AWS/Azure/GCP/k8s) |
fwxray— Diff two firmware images and surface exactly what changed: new binaries, flipped config flags, added certs, and shifted entropy regions.canzap— Replay, fuzz, and assert on CAN bus traffic from a .pcap or SocketCAN interface with a tiny YAML DSL.sbomb— Generate a CycloneDX SBOM directly from an unpacked firmware root filesystem and flag components with known CVEs and EOL kernels.mqttspy— Passively map an MQTT broker: enumerate topics, detect unauthenticated writes, spot PII/secrets in payloads, and emit a risk report.uefiscan— Audit UEFI firmware dumps for missing Secure Boot keys, unsigned modules, S3 boot-script vulns, and known SMM threats.modpot— Spin up a high-interaction Modbus/DNP3 ICS honeypot that logs attacker register reads/writes as structured JSON.
Explore the suite → 🗂️ all 170+ tools · ⭐ awesome-cognis · 🔗 cognis-sources · 🤖 uncensored-fleet · 🧠 engram
PRs, new rules, and demo scenarios are welcome under the collaboration-pull model — see CONTRIBUTING.md and SECURITY.md.
keyhound composes with the 300+ tool Cognis suite — JSON in/out and a shared
OpenAI-compatible /v1 backbone. See INTEROP.md for the
suite map, composition patterns, and reference stacks.
Source-available under the Cognis Open Collaboration License (COCL) v1.0 — free for personal, internal-evaluation, research, and educational use; commercial / production use requires a license ([email protected]). See LICENSE.
- Vulnerability DB —
keyhound/cognis_vulndb.jsonl.gz: 262,351 real vulnerabilities (OSV across 7 ecosystems) with detailed metadata; offline stdlib loadervulndb_local.VulnDB, air-gap ready. See Vulnerability database. - Edge feed catalog —
keyhound/data_feeds_2026.json+keyhound/datafeeds.py: keyless, offline-capable refresh of CISA KEV / EPSS / OSV / NVD / ATT&CK / OSCAL / abuse.ch, with snapshot export/import for sneakernet to an air gap. See Edge data feeds.