HoneyNet — Modular Honeypot Framework
SSH, HTTP, and FTP decoy services that log attacker credentials, shell commands, and file probes into a single JSON stream — with real-time coordinated scan detection
HoneyNet — Modular Honeypot Framework
Three decoy services, one log stream, and a detector that flags the moment a scanner pivots across protocols.
The Goal
I wanted to understand what automated attackers actually do when they reach an internet-exposed service — not from a PCAP, but by giving them something that looks real and watching what happens.
The design goals were:
- Run convincing SSH, HTTP, and FTP decoys that accept connections and log everything
- Write all events to a single structured JSON log so they could be analyzed together
- Detect when a single IP hit more than one service within a configurable time window — a signal that someone is running a multi-protocol scanner, not just guessing passwords
How It Works
Each honeypot runs in its own thread. All three share a single HoneyLogger instance, which is the only place events are written.
SSH
Uses paramiko in server mode. Presents a realistic OpenSSH banner (SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6) to fingerprint-aware scanners. Captures every credential pair that comes in, and if a session reaches a shell prompt, logs every command typed — whoami, cat /etc/passwd, all of it.
HTTP
A Flask server presenting a generic login form. Logs every POST credential submission and every request to sensitive paths like /.env, /.git/config, and /wp-admin.
FTP
A raw socket FTP server. Speaks enough of RFC 959 to get through the authentication sequence. Captures credentials and file-access commands.
Coordinated Scan Detection
HoneyLogger._check_multi_service() maintains a per-IP dictionary of {service: last_seen_timestamp}. After every event it prunes entries older than the configured window (default 60 seconds), then checks whether the same IP has touched two or more services. If it has, it fires a COORDINATED_SCAN alert to the console and writes a coordinated_scan event into the log.
[!] COORDINATED SCAN DETECTED
185.220.101.45 hit 2 honeypot services: SSH, HTTP
This indicates automated multi-service scanning.That alert means the traffic isn't a human guessing passwords on one port. It's a tool working a list.
What It Captures
| Service | Events Logged |
|---|---|
| SSH | Connection, login attempt (user + password), shell commands |
| HTTP | Login attempt (user + password), sensitive path probe |
| FTP | Login attempt (user + password), file access commands |
| System | Coordinated scan detection (cross-service correlation) |
All events share the same JSON schema:
{
"timestamp": "2024-08-22T14:32:03.841234",
"honeypot": "SSH",
"source_ip": "185.220.101.45",
"source_port": 52341,
"event_type": "login_attempt",
"details": {
"username": "root",
"password": "123456"
}
}The log is append-only NDJSON — one event per line — so it's easy to pipe into jq, grep, or any SIEM that accepts JSON.
Built-in Analyzer
Running python honeynet.py --analyze produces a summary from the log file: total events broken down by honeypot and event type, top source IPs by volume, and the most-attempted credential pairs. In a sample run of 1,247 events the top credential was root:123456 at 47 attempts, and admin:admin was close behind.
MITRE ATT&CK Coverage
Every event maps to a documented technique. The log is usable as labeled training data for detection rules.
| Observed Behavior | Tactic | Technique |
|---|---|---|
| SSH/FTP/HTTP credential stuffing | Credential Access | T1110 — Brute Force |
| Shell commands after login | Execution | T1059.004 — Unix Shell |
whoami, id |
Discovery | T1033 — System Owner/User Discovery |
cat /etc/passwd, account enumeration |
Discovery | T1087.001 — Local Account Discovery |
| HTTP sensitive file probes | Discovery | T1083 — File and Directory Discovery |
| Multi-service scanning within time window | Reconnaissance | T1595 — Active Scanning |
Tech & Tools
- Python 3.8+ — threading, sockets, argparse
- paramiko — SSH server mode
- Flask — HTTP honeypot
- PyYAML — configuration loading
- NDJSON — log format (one JSON object per line)
- All three services are individually togglable via
config.yaml
What I Learned
On attacker behavior: The credential lists are remarkably consistent. root:123456, admin:admin, root:root, ubuntu:ubuntu — the same pairs show up in every run because scanners are cycling through the same wordlists. A defender who monitors for any of those exact strings has a reliable low-false-positive signal.
On multi-service detection: The coordinated scan alert was the most interesting part to build. The sliding window approach — pruning stale entries on every event rather than on a timer — keeps the data structure lightweight without needing a background thread. The tradeoff is that very slow scanners (one hit per service per hour) won't trigger it, but those aren't the automated mass-scanners this is aimed at.
On log design: Centralizing all events from three different services into one schema was the right call. Being able to jq 'select(.source_ip == "185.220.101.45")' and see a complete timeline across protocols — SSH attempt, then HTTP probe three minutes later — is much more useful than three separate log files.
On threading: Each honeypot's blocking accept loop runs as a daemon thread. threading.Lock() on every file write keeps the log from getting interleaved lines when two connections arrive simultaneously.
On deception as a detection primitive: A honeypot has near-zero false positives. There is no legitimate reason for a real user to connect to port 2222 on a VPS that only exists to be a decoy. Every event in the log is real adversary activity — which makes it unusually clean training data compared to production traffic.
