Spin up a high-interaction Modbus/DNP3 ICS honeypot that logs attacker register reads/writes as structured JSON.
IoT / OT / Embedded — firmware, buses, and device security.
pip install modlure
modlure analyze capture.hexlog # → classified Modbus threat eventsReal, reproducible output from the tool — runs offline:
$ modlure-emit --version
modlure 1.0.0$ modlure-emit --help
usage: modlure [-h] [--version] [--format {table,json,sarif}]
{analyze,serve,probe,feeds} ...
MODLURE - a standard-library Modbus TCP honeypot that logs attacker register reads/writes as JSON.
positional arguments:
{analyze,serve,probe,feeds}
analyze decode + classify Modbus frames from a hex capture log
serve run a live Modbus TCP honeypot listener
probe ACTIVE: read-only probe of an AUTHORIZED, in-scope
device (OFF by default; requires --authorized + a
target scope)
feeds manage the bundled threat-intel feeds (feodo-c2,
threatfox)
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). Accepted before OR
after the subcommand.
Command-line interface for MODLURE.
Subcommands
-----------
analyze Decode + classify Modbus TCP frames from a hex capture log
and emit threat events (table or JSON).
serve Run a live Modbus TCP honeypot listener that logs every
request as a JSON threat event.
Examples
--------
# Analyze a captured hex log and pretty-print a table
modlure analyze demos/01-basic/capture.hexlog
# Emit JSON for piping into a SIEM / CI gate
modlure analyze demos/01-basic/capture.hexlog --format json
# Read frames from stdin
cat capture.hexlog | modlure analyze -
# Run a real honeypot on port 5020 (no root needed)
modlure serve --host 0.0.0.0 --port 5020
Exit codes
----------
0 no high-severity findings
1 at least one high-severity event (write/control/recon) -- use as a
Blocks above are real
modlureoutput — reproduce them from a clone.
Sample result format (illustrative values — run on your own data for real findings):
{
"title": "Example Finding",
"id": "1234567890",
"created_by": "John Doe",
"created_at": 1643723400,
"modified_at": 1643723400,
"labels": ["Vulnerability", "Exploit"],
"observables": [
{
"type": "IPv4-Address",
"value": "192.168.1.100"
},
{
"type": "Domain-Name",
"value": "example.com"
}
],
"indicators": [
{
"type": "Hash-MD5",
"value": "1234567890abcdef"
}
]
}
modlure is a standard-library Modbus TCP honeypot that decodes and classifies attacker register reads/writes as JSON threat events. Console script: modlure.
- Install:
pipx install modlure # or: pip install modlure - Analyze a captured hex log of Modbus frames and print a classified threat table (the
--formatflag is global, before the subcommand):Exitmodlure analyze capture.hexlog cat capture.hexlog | modlure analyze - # read frames from stdin
1= at least one high-severity event (write/control/recon),0= none. - Filter to serious events and emit JSON for a SIEM:
modlure --format json analyze capture.hexlog --min-severity high | jq '.[].reasons'
- Run a live honeypot listener (no root needed on a high port); every request is logged as a JSON event on stdout:
modlure serve --host 0.0.0.0 --port 5020
- Use it as a CI / alerting gate over a capture — fail when control-plane writes appear:
modlure analyze capture.hexlog --min-severity high || echo "high-severity Modbus activity — alerting"
MODLURE can score every attacker source IP against two authoritative, keyless abuse.ch feeds and escalate any match to high severity:
| Feed id | Source | URL |
|---|---|---|
feodo-c2 |
abuse.ch Feodo Tracker active botnet C2 IP blocklist (Emotet/Dridex/QakBot/...) | https://feodotracker.abuse.ch/downloads/ipblocklist.json |
threatfox |
abuse.ch ThreatFox recent IOCs (ip:port IOCs are extracted, with malware family + confidence) |
https://threatfox.abuse.ch/export/json/recent/ |
modlure feeds list # show consumed feeds + cache freshness
modlure feeds update # fetch + cache feodo-c2 and threatfox
modlure feeds get feodo-c2 --offline # print the cached feed (no network)
# enrich a capture: a known-C2 source IP becomes a high-severity event
modlure analyze capture.hexlog --enrich --format jsonA match adds a threat_intel block to the event and forces severity: high —
a host that is a known botnet C2 touching an ICS device is critical by definition:
{
"src": "203.0.113.66:50000",
"severity": "high",
"reasons": ["THREAT-INTEL: source IP flagged by feodo-c2 (Emotet)", "..."],
"threat_intel": { "ti_source": ["feodo-c2"], "ti_malware": ["Emotet"], "ti_confidence": 100 }
}The feed layer (modlure/datafeeds.py, stdlib-only) fetches
once over HTTPS, caches to disk (COGNIS_FEEDS_CACHE, default ~/.cache/cognis-feeds),
and re-serves from cache with --offline — so enrichment keeps working on a
disconnected OT / ICS network. To move intel across an air gap (sneakernet):
# on a connected box
modlure feeds update
python -m modlure.datafeeds snapshot-export feeds.tar.gz
# carry feeds.tar.gz to the air-gapped enclave, then
python -m modlure.datafeeds snapshot-import feeds.tar.gz
modlure analyze capture.hexlog --enrich --offline # never touches the networkThe committed tests run fully offline against a trimmed fixture cache
(tests/fixtures/feeds_cache); see demo 06.
modlure is a defensive, authorized-use-only tool. It has two clearly
separated modes.
Everything default is offline: it decodes captures and runs a honeypot you own. No outbound connections to third parties are ever made.
modlure analyze capture.hexlog # decode + classify a captured hex log
modlure analyze capture.hexlog --summary # aggregated scan summary (JSON):
# counts by severity/category/function,
# distinct sources, recon-sweep heuristic
modlure serve --host 0.0.0.0 --port 5020 # honeypot listener — YOU own the socket
modlure analyze capture.hexlog --enrich --offline # score source IPs vs cached C2/IOC feeds
⚠️ AUTHORIZED USE ONLY. Active mode opens outbound Modbus/TCP connections to real devices. Scanning equipment you are not explicitly authorized to test can be illegal and can disrupt live OT/ICS processes. Only probe devices you own or are contracted to assess.
Active probing confirms reachability and fingerprints a device using read-only requests (holding-register read, report-server-id, read device identification). It is gated four ways:
- Off by default — refuses (exit 2) unless you pass
--authorized. - Scope-enforced — every target must be in an allowlist (
--target HOST[:PORT]repeatable, and/or--scope-file FILE); out-of-scope targets are skipped, never contacted. - Read-only — only read/identity function codes are ever transmitted; write
and control codes (
0x05/0x06/0x0F/0x10/0x16/0x08) are refused before a byte leaves the host. - Rate-limited —
--rate(default 1.0s) enforces a minimum inter-request delay so a fragile PLC is not overwhelmed.
# Refused — active mode is off by default:
modlure probe --target 10.0.0.5:502
# error: active probing is OFF by default ...
# Authorized probe of two in-scope devices, 1 request/sec, JSON out:
modlure probe --authorized \
--target 10.0.0.5:502 --target 10.0.0.6:502 \
--rate 1.0 --format json
# Scope from a file (one HOST[:PORT] per line, # comments allowed):
modlure probe --authorized --scope-file authorized_devices.txtThe bundled tests for active mode hit only a localhost fixture server
(tests/modbus_fixture.py) and mocks — never a real external host.
- Why modlure? · Features · Quick start · Example · Demos · Architecture · AI stack · How it compares · Integrations · Install anywhere · Related · Contributing
OT threat-intel content engine — drop it on a VPS, share the 'someone tried to open my fake water-treatment valve' logs. ICS honeypot captures get major infosec-Twitter traction.
modlure 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.
- ✅ Passive (default, offline): parse frame · classify event · analyze capture · scan summary
- ✅ Build honeypot response · live
servelistener - ✅ Threat-intel enrichment (abuse.ch C2/IOC, edge / air-gap, offline cache)
- ✅ Active (authorization-gated, OFF by default): read-only
probeof an in-scope device — scope-enforced + rate-limited - ✅ JSON · SARIF · table output; CI-gate exit codes
- ✅ Runs on Linux/macOS/Windows · Docker · devcontainer
- ✅ Core check ported to Python, JavaScript, TypeScript, Go, and Rust (
ports/, CI-tested)
pip install modlure
modlure --version
modlure analyze capture.hexlog # decode + classify a capture
modlure --format json analyze capture.hexlog # machine-readable (SIEM)
modlure --format sarif analyze capture.hexlog # SARIF for code-scanning
modlure analyze capture.hexlog --min-severity high # CI gate (non-zero exit)$ modlure analyze demos/04-water-treatment-tamper/capture.hexlog
SEV SRC FUNCTION ADDR QTY REASONS
-------------------------------------------------------------------------
low 10.0.4.10 read_holding_registers 100 4 benign register read
high 185.220.101.7 report_server_id suspicious function report_server_id (recon/tamper); undecodable PDU (malformed/fuzz traffic)
high 185.220.101.7 write_single_coil 16 register/coil write attempt against control device
high 185.220.101.7 write_single_register 101 register/coil write attempt against control device
high 185.220.101.7 write_multiple_coils 16 5 register/coil write attempt against control device
-------------------------------------------------------------------------
total=5 high=4, low=1
--format is accepted before or after the subcommand, and supports
table (default), json, and sarif.
Every demo under demos/ is a real Modbus TCP capture in the
tool's actual input format (<src> | <hex> per line) plus a SCENARIO.md
explaining where the traffic came from, the exact run command, what to
expect, and how to act. Each one is verified by the test suite to produce
the finding it describes.
| Demo | Scenario | Outcome |
|---|---|---|
01-basic |
Small mixed capture (reads, writes, recon, malformed) | high + medium + low |
02-clean |
Trusted HMI reads only — negative control | exit 0 |
03-mixed |
Mostly benign polling with one hostile source | high |
04-water-treatment-tamper |
Chlorine-dosing valve/setpoint writes | 4× high |
05-port-scan-recon |
Internet-wide scanner fingerprinting :502 |
3× high (recon) |
06-plc-restart-diagnostics |
Restart / clear-counters / listen-only via FC 0x08 | 3× high |
07-fuzzing-campaign |
Malformed/truncated frames — all recorded | high + medium |
08-benign-scada-poll |
Normal cyclic SCADA polling | exit 0 |
09-setpoint-override |
Turbine overspeed-trip setpoint tamper (mask-write) | 3× high |
10-multi-unit-sweep |
One peer walking unit ids behind a gateway | reads-only, exit 0 |
11-coil-flood |
Mass coil writes flipping actuators | 3× high |
python -m modlure analyze demos/09-setpoint-override/capture.hexlog
python -m modlure --format sarif analyze demos/05-port-scan-recon/capture.hexlog > recon.sarifflowchart LR
IN[attacker traffic] --> P[modlure<br/>capture]
P --> OUT[alerts + indicators]
modlure is interoperable with every popular way of using AI:
- MCP server —
modlure mcp(Claude Desktop, Cursor, Cognis.Studio, uncensored-fleet) - OpenAI-compatible / JSON — pipe
modlure 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 modlure | conpot | |
|---|---|---|
| Self-hostable, no account | ✅ | varies |
| Single command, zero config | ✅ | |
| JSON + SARIF for CI | ✅ | varies |
| MCP-native (AI agents) | ✅ | ❌ |
| Polyglot ports (JS/TS/Go/Rust) | ✅ | ❌ |
| Open license | ✅ COCL | varies |
Built in the spirit of conpot, 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 (modlure mcp) for AI agents, and a webhook forwarder for SIEM/Slack/Jira. See docs/INTEGRATIONS.md.
pip install "git+https://github.com/cognis-digital/modlure.git" # pip (works today)
pipx install "git+https://github.com/cognis-digital/modlure.git" # isolated CLI
uv tool install "git+https://github.com/cognis-digital/modlure.git" # uv
pip install modlure # PyPI (when published)
docker run --rm ghcr.io/cognis-digital/modlure:latest --help # Docker
brew install cognis-digital/tap/modlure # Homebrew tap
curl -fsSL https://raw.githubusercontent.com/cognis-digital/modlure/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/modlure |
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.keyhunt— Scan firmware blobs and filesystem dumps for hardcoded private keys, API tokens, default creds, and weak RSA/ECC material.
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.
{} 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.