honeypot.observer

Give your honeypot a reason to exist.

A removable comb frame, with its corner joint exposed to show how the structure supports what is inside.
A frame, and what it has to support.

Suppose a honeypot lists customer-backup.zip. A visitor asks for it. The log now contains an attempted read of a sensitive-looking file. Did the visitor come looking for customer records, or did the filename put the idea there?

The operator wrote the filename, then read the response as evidence of intent. That small design choice deserves more thought than whether the name sounds plausible.

A default honeypot is nobody’s machine. It’s a film-set storefront: fine from the sidewalk, hollow the second someone opens the door. Replacing recognizable defaults and making the answers agree is established advice.1 The harder choice is how much of a machine to invent.

A small, ordinary job is a better starting point than a detailed fictional company. It gives the files a reason to be there and sets limits on what else belongs. Grandeur is a tell too. A staging server stuffed with executive documents, production credentials and customer archives looks arranged for the visitor’s convenience.

A job small enough to explain#

Take an invented staging server for a CSV import service. It accepts sample files and checks whether an importer can process them. Start with this working directory:

Illustrative directory listing, not an observed system
text
/srv/importer/
  app.py
  requirements.txt
  fixtures/
    sample.csv

The application has an entry point. Its dependencies have a place. A sample belongs beside the code that consumes it. The web response needs to describe the same importer.

A biography for the fictional developer adds little. A deployment note earns its place when it explains something the visitor can inspect. Every extra detail gives the visitor another question the decoy has to answer.

Follow the next question#

Add customer-backup.zip to that listing. It looks tempting, and a staging server is a plausible place to leave a backup. Now follow the action it invites.

What happens when the file is opened? Does its content belong to this importer? Why does a service being tested with sample data also hold customer records? The filename creates obligations. Its contents, location and explanation all need to agree.

There’s no partial credit for a mostly consistent box. A convincing directory listing does not repair an archive that returns unrelated text when inspected. Support the file, change what it claims to be, or remove it.

For this importer, leave the backup out unless observing responses to an offered archive is part of the job. Ordinary shell exploration does not require every tempting thing its author can imagine. A file can be believable and still be the wrong thing to add.

That is a practical stopping rule. Before adding a file, message or service, follow the next question it invites. Decide whether answering that question serves the observation being sought. Then build enough to answer it properly.

The explanation has to reach the visitor. “The backup service is offline” explains an unavailable file only when the decoy conveys that state. A story kept in the author’s head does no work on the wire.

Keep the clue in the record#

A well-supported backup also changes how the resulting log should be read. Compare two sequences on this fictional server:

  • The visitor asks whether backups exist before the decoy mentions them.
  • The decoy lists a backup, and the visitor then attempts to read that named file.

The first request introduces backups into the exchange. In the second, the decoy introduces them. Neither reveals an original objective by itself, but the difference matters: one request supplies a topic; the other follows an invitation.

Visitor asks:“Are there backups?”Decoy offers:customer-backup.zipSame attemptedread in the log.Keep what came before it.
Who introduced the file? An attempted read alone loses the distinction. Keep the invitation beside the response.

A command list strips that difference away. A request for customer-backup.zip looks unusually specific until the preceding response shows who supplied the name.

Keep that response beside the attempted action, along with the version of the decoy that produced it. The operator needs to see what the visitor was offered. Without it, the analyst is interpreting half an exchange.

“The visitor followed the offered filename” is a useful observation. Calling it evidence that the visitor came looking for customer data assigns a motive the operator’s own fiction helped create. A decoy can write the motive into its own logs, then persuade its author that it was discovered there.

Restraint helps here too. Fewer clues mean fewer invitations to account for when reading what happened next. That is another reason to give the importer a small, clear job.

Why did they keep going?#

More time on the box does not establish belief in the story. A visitor can keep going because a script has more instructions or a client is retrying an unusable response. Read the exchange: which clue was shown, and what was attempted after it? That tells the operator more about this design than session length alone.

Give the backup a job#

The importer’s backup belongs when there is a reason to observe how offered material is handled. Build a synthetic archive that fits the service, keep working credentials and third-party data out of it, and record the invitation alongside the attempted read.

That is the design decision to carry forward: choose the observation first, then build the detail that makes it possible. The operator is responsible for both the story the visitor encounters and the story later told about the log.

Part 2, “Plant something worth stealing,” takes that decision into the bait itself: what to offer, where it belongs, and what an attempted use can tell the operator.

Footnotes#

  1. See Cowrie’s FAQ and filesystem guide for the existing customization guidance. This example is a design sketch, not a Cowrie configuration. ↩