Skip to content

cognis-digital/keyhound

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KEYHOUND

KEYHOUND

Scan firmware blobs and filesystem dumps for hardcoded private keys, API tokens, default creds, and weak RSA/ECC material.

PyPI CI License: COCL 1.0 Suite

IoT / OT / Embedded — firmware, buses, and device security.

pip install keyhound
keyhound scan .            # → prioritized findings in seconds

🔎 Example output

Real, 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 keyhound output — 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"
            }
        ]
    }
]
}

Usage — step by step

  1. Install the scanner:

    pip install keyhound
  2. Scan a tree (e.g. an extracted firmware image or filesystem dump) for hardcoded keys, tokens, and default creds:

    keyhound scan /tmp/firmware_extracted
  3. Filter by severity and emit JSON for CI or jq:

    keyhound scan ./dump --severity high --format json | jq '.findings[] | select(.severity=="critical")'
  4. Read the result. Secrets are redacted by default; pass --show-secrets to print full values. Exit code 0 = no findings, 1 = one or more secrets found, 2 = usage/runtime error.

  5. 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; }

Contents

Why keyhound?

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.

Features

  • ✅ 12 detectors — PEM private keys, AWS access/secret keys, Google API keys, GitHub & Slack tokens, JWTs, connection-URI passwords, /etc/shadow hashes, hardcoded passwords, generic high-entropy api/secret assignments, telnet/busybox default logins
  • ✅ Secrets redacted by default (safe to paste into tickets/CI logs); --show-secrets to 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-on for 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)

Quick start

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)

Example

$ 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

Demos — real-world scenarios

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 0

Output formats & CI gating

keyhound 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.

Vulnerability database — bundled, offline, 262k records

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 package

Edge data feeds — keyless, offline-capable refresh

For 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.
  • --offline serves 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 enclave

The 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.

Scope, authorization & safety

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 --authorized flag 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-secrets reveals 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.

Architecture

flowchart LR
  IN[target / manifest] --> P[keyhound<br/>checks + rules]
  P --> OUT[findings (JSON / SARIF)]
Loading

Use it from any AI stack

keyhound is interoperable with every popular way of using AI:

  • MCP serverkeyhound mcp (Claude Desktop, Cursor, Cognis.Studio, uncensored-fleet)
  • OpenAI-compatible / JSON — pipe keyhound scan . --format json into 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

How it compares

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.

Integrations

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.

Install — every way, every platform

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)

Related Cognis tools

  • 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

Contributing

PRs, new rules, and demo scenarios are welcome under the collaboration-pull model — see CONTRIBUTING.md and SECURITY.md.

⭐ If keyhound saved you time, star it — it genuinely helps others find it.

Interoperability

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.

License

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 (licensing@cognis.digital). See LICENSE.


Cognis Digital · one of 170+ tools in the Cognis Neural Suite · Making Tomorrow Better Today

Bundled offline data

  • Vulnerability DBkeyhound/cognis_vulndb.jsonl.gz: 262,351 real vulnerabilities (OSV across 7 ecosystems) with detailed metadata; offline stdlib loader vulndb_local.VulnDB, air-gap ready. See Vulnerability database.
  • Edge feed catalogkeyhound/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.

Releases

Packages

Contributors

Languages