Hardening, serendipity, and curiosity Link to heading

A few weeks ago I hardened all of my Linux boxes: flushed all iptables/ipfw rules, set the default policy to DROP, and re-enabled rules selectively as needed. On Ubuntu I used ufw; on Fedora, firewalld.

Once UFW was in place, journalctl started showing every blocked connection attempt:

Sep 03 11:54:16 kernel: [UFW BLOCK] IN=eth0 OUT= MAC=XX SRC=XX DST=XX LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=24286 DF PROTO=TCP SPT=36864 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0

Port 23/tcp (telnet). Someone out there was scanning for open telnetd. Curiosity got the better of me. Which ports were getting the most attempts?

/var/log # zgrep -ho "DPT=\w*" syslog* | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10
# attempts | port
   1009 23
    969 3128
    330 22
    248 8080
    168 80
    158 5060
    151 3389
    111 1825
     94 1433
     77 123

(Port 443 is not shown because the server runs HTTPS and those connections are accepted, not blocked.)

Telnet leads by a wide margin. What exactly are these scanners trying to do?

The idea Link to heading

I decided to write a fake telnetd server in Python using Twisted to find out. The honeypot would:

  • Show a root shell prompt and wait for commands
  • Reply with plausible fake output for common commands like uname and id
  • Return command not found or nothing (randomly) for everything else
  • Log everything the remote end types

This is a simplified honeypot. The code is on GitHub: telnetd_honeypot.

One constraint: I didn’t want to run the honeypot as root, but port 23/tcp requires elevated privileges to bind. The solution is rinetd: configure it to redirect traffic from 23/tcp to 8023/tcp, where the honeypot runs as an unprivileged user. (A Docker container works equally well for isolation.)

Demo Link to heading

% python telnetd.py &
[1] 15171
% telnet localhost 8023
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
/ # uname -a
Linux f001 3.13.3-7-high-octane-fueled #3000-LPG SMPx4 Fri Jun 31 25:24:23 UTC 2200 x86_64 x64_86 x13_37 GNU/Linux
/ # id
uid=0(root) gid=0(root) groups=0(root)
/ # ls
/ # ps auxw
bash: ps: command not found

Results Link to heading

After just three days of running, the top commands sent by attackers were:

cut -d " " -f 6- telnetd_honeypot.log | sort | uniq -c | sort -nr | head -n 3
921 sh
918 /bin/busybox ZORRO
879 root

Most of it is credential stuffing, trying root as a password. The sh attempts are trying to spawn a shell once they think they’re in.

The busybox ZORRO line is the most interesting. At the time, I couldn’t find a clear explanation.

Updated 2026: busybox ZORRO (and similar strings like busybox LMAO, busybox ECCHI) became widely recognized as signatures of the Mirai botnet and its descendants. The bots use busybox to detect the device’s CPU architecture by running it with a unique string argument: if the binary runs and exits with that string, the bot knows which architecture-specific payload to deploy. By 2016 this technique had become a standard fingerprint of IoT-targeting malware.

There were also shellcode injection attempts (which I’m not publishing), and one memorable entry:

08/20/2015 12:43:10 AM INFO: 79.x.x.x knock knock neo

Apparently someone is still looking for Neo.

Three days of data was enough to satisfy my curiosity, but the honeypot is a solid starting point for anyone who wants to dig deeper. Add Docker for easy isolation, improve the command simulation, and you have a useful research platform. Happy investigating!