Skip to content

Latest commit

 

History

History
64 lines (51 loc) · 2.98 KB

File metadata and controls

64 lines (51 loc) · 2.98 KB

ssh

Medium-interaction SSH honeypot for SOFAH. It presents a cloned device's SSH banner, accepts logins under a configurable weak-credential / accept-Nth policy, and drops the attacker into a fake shell whose ls/cat/uname/busybox/wget responses are all canned. Nothing the attacker types is ever executed; wget/curl/tftp URLs are logged but never fetched.

It mirrors Cowrie's medium-interaction model but feeds the SOFAH log schema and takes its persona (banner, hostname, kernel, weak creds) from recon via ennorm.

Layout

src/
  startup.py          # entrypoint: env -> SofahLogger + Persona + FakeFilesystem -> server
  ssh_honeypot.py     # paramiko ServerInterface + accept loop (the only paramiko-aware file)
  auth.py             # AuthPolicy: planted weak creds + accept-Nth (paramiko-free)
  persona.py          # Persona: banner / hostname / prompt / uname, from data/persona.json
  shell/
    emulator.py       # FakeShell: parse (shlex, never exec) + dispatch + per-session state
    commands.py       # canned responders: ls, cat, uname, busybox, wget(capture), ...
    filesystem.py     # FakeFilesystem: in-memory tree; cat/ls never touch the host FS
  data/
    persona.json      # default persona (ennorm overwrites this per cloned device)
    sandbox/          # static decoy files planted into the fake FS (/etc/passwd, ...)

Logging

All events use the ssh.honeypot.* eventid namespace and carry a per-connection session:

eventid when
ssh.honeypot.connect new TCP connection
ssh.honeypot.client_version client's SSH banner
ssh.honeypot.login_attempt every credential pair (result: accepted/failed)
ssh.honeypot.command every command line entered
ssh.honeypot.download wget/curl/tftp URL (captured, never fetched)
ssh.honeypot.direct_tcpip refused pivot/tunnel attempt
ssh.honeypot.session_end duration + command count

Configuration (env)

var default meaning
LOG_API log-api base URL (e.g. http://log_api:50005)
SSH_PORT 65022 in-container listen port (unprivileged; compose maps the real port to it)
EXT_PORT 22 externally published port, used as the logging dst_port
PERSONA data/persona.json persona file path
SANDBOX data/sandbox decoy-file tree planted into the fake FS

Hardening

  • Never executes attacker input — the shell is canned; downloads are logged, not fetched.
  • Non-root, read-only root FS, cap_drop: [ALL] — the pot listens on a high port so it needs no added capabilities; it writes nothing to disk.
  • Unique host key per deployment — generated in memory at startup, never reused.
  • Resource caps — max concurrent sessions, per-session command cap, idle timeout.

Tests

pytest -q. The shell/persona/filesystem/auth suites are paramiko-free; the ServerInterface test importorskips paramiko (installed in CI and in the image).