The archive is listed. The visitor asks to inspect it. The decoy answers “command not found,” and the connection ends. Did the visitor recognize a honeypot, or did the machine simply prevent the next step? A more convincing banner will not answer that question.
Treat this as an invented repair job. The importer has promised an archive, and the path to inspecting it is broken. Start there.
Public defaults and protocol differences are familiar ways to identify honeypots. Changing the visible text does not remove differences in the implementation underneath it.1 That is a reason to be selective about realism: there is always more work available than the observation needs.
Repair the path the decoy promises#
Write down the interaction the lure invites. For the importer, it is short: enter the advertised directory, see the archive, request it, inspect the delivered material. Check each step against a fresh instance of the decoy in an authorized local test environment.
A failed step deserves attention before an unrelated hostname or an elaborate history file. Without that step, the operator cannot observe the behavior the lure was built for. Polishing the surrounding fiction leaves the measurement broken.
This gives repairs an order:
| Seam | First question |
|---|---|
| A listed directory cannot be opened | Can the visitor reach the offered material? |
| An archive returns unrelated text | Does the response fulfill the offer? |
| A changed file reverts on the next read | Does the environment remember the action it acknowledged? |
| A banner or file matches a public default | Does this exposed detail contradict the intended persona or identify the implementation? |
The order depends on the purpose. A decoy studying connection handshakes needs care at the protocol boundary. A shell built to observe archive inspection needs a coherent route through the filesystem. There is no single checklist that makes both convincing.
Check sequences, not screenshots#
An answer can look plausible in isolation and collapse on the next request. Suppose a test client creates a harmless scratch file. The decoy acknowledges it, then denies that the file exists. Both replies could pass a quick visual check on their own. Together they describe an impossible sequence.
The same applies without any writes. Entering a directory should affect where a relative path resolves. A listing and a subsequent inspection should agree about the file. An error should fit the operation attempted and the state already established.
Keep a small set of these exchanges as repeatable checks. Start each from a known state, and include a second fresh session to verify the intended reset behavior. The exact transcript matters more than the number of commands the emulator claims to support.
A model generating the responses does not remove this obligation. Fluent text can disagree with an earlier answer. Give important state a defined representation and check the resulting exchange. A promise in a prompt is not a test result.
Let the machine have limits#
A staging service does not need to pretend every operation is available. Permissions, absent dependencies and disabled features belong on ordinary machines. The error has to fit the role and remain consistent across the paths that expose it.
That is different from a catch-all failure. If everything unfamiliar produces the same answer, the apparent environment ends wherever the handler list ends. Adding another isolated response expands that list; it does not necessarily make the adjacent actions coherent.
Choose a smaller supported surface when the alternative is a larger contradictory one. If archive inspection is the point of the decoy, support it. If an unrelated feature would consume the entire maintenance budget, leave it unavailable and make that limit intelligible.
Containment still sets the boundary. Do not attach a real shell, grant real permissions or allow outbound activity just to make a fictional operation look convincing. A realistic response is not worth turning the decoy into a useful machine for someone else.
Verify the repair before reading the traffic#
Replay the original failing exchange after the change. Check that the advertised path now works, that a neighboring operation still gives a coherent answer, and that the record includes both request and response.
That proves the local defect was repaired. It does not explain every earlier departure. Keep that distinction in the change note instead of rewriting old sessions as confirmed recognition of a honeypot.
For the importer, the next useful revision is the one that lets a visitor inspect the offered archive and lets the operator read the attempt. Part 5 takes that repair into the longer routine: choosing what to change without letting the most repetitive visitor design the whole machine.
Footnotes#
-
Bitter Harvest demonstrates fingerprinting through protocol implementation differences. Cowrie’s FAQ covers customization of identifying defaults. Neither makes changing a banner a guarantee against recognition. ↩