NEWS

Honeypot with four fake H100s reveals what bots do after breaking into an SSH server

A honeypot disguised as an AI training server, with fake Nvidia H100s, logged twelve and a half hours of automated SSH attacks and caught a botnet linked to the Outlaw group installing a miner.

Honeypot with four fake H100s reveals what bots do after breaking into an SSH server
Image: Redação iMasters

A honeypot to see what the bot does after logging in

Every SSH log exposed to the internet has the same litany: Failed password for root, Invalid user admin, attempts coming from all over the world. Most administrators treat this as background noise that fail2ban sweeps away on its own. Russian developer phoenix964, from StackWorks, reported on Habr, a Russian tech blog and community site, an experiment that asks the obvious question almost no one answers: what do these bots do when the login actually works? Source

To find out, the team set up a trap: a fake Linux server, convincing enough to look like a valuable AI training machine, and let bots actually get in. The experiment ran from 16:20 UTC on October 24 to 4:59 UTC on October 25, 2026, just over twelve and a half hours of collection.

Why Cowrie, and not an LLM pretending to be a terminal

The team's first idea was trendy: put a fake SSH on port 22 that responds to commands using a language model, generating the output of ls, cat, and the like on the fly. A project like that exists, Beelzebub, which connects SSH to an LLM.

The team dropped that route and went classic: Cowrie, a Python honeypot with more than ten years on the road. No neural network inside: a pre-built filesystem, commands emulated by code or output recorded in advance. The advantage is predictability, zero operating cost, and logging of absolutely everything (login, command, downloaded file), with sessions that can later be replayed like a terminal recording.

The network architecture is simple: the real sshd stays up and only accepts connections from the team's trusted IPs; everything else is redirected by the firewall to Cowrie, with no port forwarding enabled (so it doesn't become an attack proxy) and file downloads capped at 10 MB.

The details that nearly gave away the trap

An out-of-the-box Cowrie falls easily to an experienced bot. By default it accepts almost any password, which gives away the honeypot: a bot that logs in with root:123, root:qwerty, and root:anything quickly realizes it's in a trap.

The team wrote its own authentication class, called StickyAuth: each new IP only gets in after a random number of failed attempts (between 2 and 5), and from then on only that specific login-and-password combination works for that IP, as if it were the server's real password. The core of the logic:

python
fixed = creds.get(src_ip)
if fixed is not None:
    return fixed[0] == login and fixed[1] == passwd

need = self.state["need"].setdefault(src_ip, randint(self.mintry, self.maxtry))
tries = self.state["tries"][src_ip] = self.state["tries"].get(src_ip, 0) + 1
if tries >= need:
    creds[src_ip] = [login, passwd]
    return True
return False

For the decoy itself, the team generated a reference Debian 12 with mmdebstrap, extracted the filesystem with Cowrie's createfs, and dressed up the server as an AI training machine: 128 EPYC 7763 cores, 1 TB of RAM, four 80 GB NVIDIA H100s, 3.5 TB of NVMe, and 28 TB of storage, with a simulated nvidia-smi showing 90% usage and a python3 train.py process running. Fake (and tagged) cloud keys were planted in /opt/ml/.env.

But running real attack scripts, collected from the log itself, against the decoy revealed three holes:

  • The emulated grep only understood the -q and -m flags; commands like lspci | grep -i nvidia (common among bots) came back empty for lack of -i, giving away the absence of a GPU on a machine supposedly packed with H100s.
  • On Debian 12, /bin is a symlink to /usr/bin, but Cowrie's canned responses still pointed to the old paths; commands without emulation or a cached response at the new path returned Exec format error, an error that doesn't exist on a real Debian.
  • The hostname command returned a name different from the one recorded in /etc/hostname, Cowrie's factory default.

After fixing the grep flags, redistributing responses for df, dmesg, mount, and emulating lsblk, ip, ss, systemctl, docker, journalctl, and top, the same bot script began correctly seeing the 128 cores and the four H100s. The team admits that sort, screen, tmux, and gzip still don't respond convincingly, and that the fixed timestamp in the simulated nvidia-smi header is another clue an attentive human would notice.

The numbers from the first twelve and a half hours

MetricValue
Total connections1,846
Distinct IP addresses126
Failed login attempts1,536
Successful logins175, from 70 IPs
Sessions with commands typed102, from 65 IPs
Commands executed1,524
File downloads219 (47 distinct files)

Two-thirds of the connections (1,198 of 1,846) came from a single address running a Go SSH client, which simply worked through a password wordlist, one connection per attempt, never executing a command after logging in.

Almost every login attempt targeted the root user (728 of 1,711), and the most-tried passwords repeat the usual catalog, with two entries that stand out:

PasswordAttempts
12345683
345gs5662d3458
3245gs5662d3457
12344
123439
admin26
1234567820

The two random strings in second and third place aren't passwords at all: they're the signature of the botnet that dominated the experiment.

The botnet linked to the Outlaw group

Of the 65 sources that got as far as typing commands, 59 ran exactly the same script, word for word. The SSH key this group leaves behind carries the comment mdrfckr, and security researchers already associate this signature with Outlaw, a group that has been building mining networks out of compromised servers since 2018.

The pattern is always the same: the bot logs in with a dictionary password on the root user and, within seconds, tries two credentials of its own (345gs5662d34 with the same password, then root/3245gs5662d34) before logging in again with the password that worked. The team speculates it's a check for "is this server already ours?"; the string 345gs5662d34 had already puzzled another researcher in a 2023 SANS diary entry, as one of the most frequent logins seen in another honeypot.

From there, the script follows a routine of 18 to 27 commands in four to five minutes:

  1. Deletes .ssh and writes its own public key, guaranteeing passwordless entry even if the password is changed.
  2. Changes the root password, locking out both competitors who found the same password and the server's original owner.
  3. Searches for and kills rival miner processes.
  4. Checks CPU, memory, and architecture, to estimate how much mining can be done there.
  5. Creates a cron job and downloads a file called dota3.

The decoy's root password was changed 42 times over the twelve and a half hours. The dota3 package had already been analyzed by Elastic in another investigation: it contains a miner, an IRC-controlled backdoor, and a module that goes out brute-forcing SSH passwords on other servers, closing the loop in which the compromised machine itself becomes an attacker.

The recon that never turned into an attack

The remaining six addresses behaved differently. Four just ran uname and vanished, likely feeding a "what's running where" list to attack later with the right tool. Two ran longer reconnaissance scripts, and one of them came back 38 times.

That last one is precisely the bot affected by the grep bug: it only looks for a GPU via lspci, never calling nvidia-smi. In the first 34 visits, because of the bug that hadn't been fixed yet, it saw a machine with no graphics card at all, and still kept coming back, which suggests it runs on a fixed schedule, not out of curiosity. After the grep fix, it visited four more times, saw the four H100s, and disappeared, never going on to install a miner in the twelve and a half hours of collection.

SSH client signatures also tell the story: 1,305 connections came from a program written in Go, 380 from libssh 0.9.6, 32 from libssh 0.11.1, and just 3 from PuTTY, the client real humans tend to use. Almost everything that hits an exposed port 22 is automation, and only automation responds efficiently to automation.

What changes for people who actually run servers

The practical conclusion of the experiment is uncomfortable in its simplicity: everything the bots did depends on password authentication being enabled. A server that only accepts SSH keys gives these scripts nothing to guess.

The minimal recipe, in /etc/ssh/sshd_config or a dedicated file under /etc/ssh/sshd_config.d/:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password

Test with sshd -t before applying with systemctl reload ssh, and confirm key-based login in a second session before closing the first; it's the classic mistake that locks the administrator out.

With password login off, fail2ban stops being the first line of defense and becomes log hygiene: the bot that generated 1,198 of the experiment's 1,846 connections would be banned by the fifth attempt with a simple config:

[sshd]
enabled = true
maxretry = 5
findtime = 10m
bantime = 1h

For those who suspect a weak password has already been exploited by this specific botnet, the report offers three checking commands: grep -r mdrfckr /root/.ssh /home/*/.ssh 2>/dev/null looks for the group's key, crontab -l; ls -la /etc/cron.d shows suspicious scheduled tasks, and lastb | head -50 lists the most recent failed logins. Found a sign of any of the three: the report's own recommendation is not to try to clean the server manually, but to migrate the data to a new machine, changing every password and key that ever passed through it.

Reproducing the experiment

Cowrie is available via PyPI, at version 3.1.3, with installation instructions in the project's official documentation: an unprivileged dedicated user, a virtual environment, pip install cowrie, cowrie start. By default it listens on port 2222; redirecting external traffic from port 22 to it, while keeping direct access limited to the administrator's IP, is possible with an nftables rule:

define ssh_home = { 203.0.113.10 }

table ip nat {
    chain prerouting {
        type nat hook prerouting priority dstnat;
        tcp dport 22 ip saddr != $ssh_home redirect to :2222
    }
}

The original report reinforces a detail that's easy to forget: port 2222 needs to be accessible only through the redirect (ct status dnat accept), otherwise the honeypot is directly visible to anyone who knows to look for it. And the common-sense recommendation applies to any honeypot: run it on a separate machine, with nothing of real value, because the whole point is to attract exactly the people trying to break in.

Translated from the Brazilian Portuguese original · Read the original