Part 4 ended with a botnet that had no defences at all. Mirai trusted anyone with a factory password, talked to a handful of command servers, and lost every device on reboot. Its successors learned from that.
Mozi, first spotted in September 2019, combined Mirai’s targets with the defences of the earlier parts of this series. It infected routers and video recorders, but organised them into a peer-to-peer network built on the BitTorrent distributed hash table, the same system millions of file-sharing clients use. Every configuration it accepted had to carry a valid ECDSA signature. At its peak it was seen on about 160,000 devices a day, and IBM attributed 90% of the IoT network traffic it observed between October 2019 and June 2020 to it.
In 2021 its authors were taken into custody in China. The botnet kept running anyway. Then, in August 2023, it suddenly went quiet. A month later researchers found out why: someone had sent the bots a properly signed configuration telling them to replace themselves with a version that did nothing. The botnet’s own trust mechanism was used to shut it down.
This is Part 5 of A Short History of Botnets: how Mozi was built, why arrests did not stop it, and what we know, and do not know, about the kill switch.
A note on the exhibits. As before, the exhibits are reconstructions or illustrations built from published analyses, mainly 360 Netlab’s original report and ESET’s analysis of the kill switch. Each says which kind it is.
Lineage: borrowed parts, new skeleton
On 3 September 2019, 360 Netlab’s monitoring flagged a file that most antivirus engines labelled Gafgyt, an older IoT malware family. On closer inspection it was something new. Netlab named it Mozi after the file names it spread under, Mozi.m and Mozi.a, and published the first analysis in December 2019.
Mozi was assembled rather than invented. Netlab found Gafgyt code for its DDoS attacks, process renaming and firewall changes; IBM’s researchers later noted borrowings from Mirai variants too. What was new was the skeleton: instead of command servers, a peer-to-peer network; instead of trusting whoever answered, signed configuration files.
The numbers grew quickly:
| Date | Measurement |
|---|---|
| Historical total | More than 1.5 million devices infected, about 830,000 of them in China |
| September 2020 | Peak of about 160,000 infected systems a day |
| October 2019 to June 2020 | 90% of the IoT network traffic IBM X-Force observed; about 84% of the botnet’s infrastructure in China |
| April 2020 | More than 15,000 infected devices, per Black Lotus Labs |
The daily peak and historical totals are from The Record’s report on the 2021 arrests, which cites Black Lotus Labs and Netlab. IBM’s figures are from its 2020 research. The sources measure different things, unique devices, daily active systems and traffic share, so they cannot be compared directly, but they agree on the trend: within a year Mozi became the dominant IoT botnet on the Internet.
Architecture: hiding inside BitTorrent
GameOver Zeus ran its own peer-to-peer protocol, which meant its traffic looked like nothing else on the network and its peers could be studied as a closed system. Mozi made a different choice. According to 360 Netlab, it built on the distributed hash table (DHT) that BitTorrent clients use to find each other, and extended it.
Joining the network. A new bot started from eight bootstrap nodes written into its code, including router.bittorrent.com:6881 and dht.transmissionbt.com:6881. Those are public, legitimate servers that millions of ordinary BitTorrent clients rely on. Nobody could take them down to cut Mozi off without breaking BitTorrent for everyone else.
Recognising each other. In a public DHT, Mozi bots share the network with ordinary file-sharing clients. To find their own kind, they used 20-byte node IDs beginning with the prefix 888888, and marked their messages with a 4-byte flag computed by a fixed algorithm. They supported the standard DHT queries ping, find_node and get_peers, and could hand their configuration to another bot inside the answer to one of them.
Taking orders. There was no command server. The operators wrote a configuration file, signed it, and injected it into any node; bots passed it on to each other. Netlab described the format precisely:
- each configuration was 528 bytes: 428 bytes of data, a 96-byte signature and a 4-byte flag that controlled whether a bot should update;
- the data was XOR-encrypted with a fixed 16-byte key;
- the signature used ECDSA with a 384-bit curve, and every sample carried two public keys, one to check the encrypted file and one to check its decrypted contents.
The configuration was both the botnet’s settings and its command channel. A few of its fields show what it could do:
| Field | Purpose |
|---|---|
[atk] | Launch a DDoS attack (HTTP, TCP, UDP and others) |
[ud] | Update the bot from a given URL |
[dr] | Download and run a payload from a given URL |
[rn] | Run system commands |
[idp] | Report the bot’s ID, IP, port, file path, gateway and CPU architecture |
[nd] | Add more bootstrap nodes |
[hp] | Change the node-ID prefix |
Exhibit 1. How an order reached a Mozi bot.

There is no command server in this picture. The only thing that made a configuration an order was the signature, which is exactly what made the 2023 kill switch work.
Put together, this solved the two problems that brought down the earlier botnets in this series. There was no server to seize, as there had been with Coreflood, and no private peer network of the kind defenders had taken over from the inside in GameOver Zeus. As long as a bot only obeyed configurations signed with the authors’ private key, nobody else could give it orders. That last condition turned out to be the whole story.
How it spread, and what it did
Mozi recruited in two ways. Like Mirai, it tried weak and default Telnet passwords. When it got in, it wrote a small downloader onto the device line by line with echo, ran it, and fetched the full sample over HTTP from another infected node. There was no central download server to block.
Unlike the original Mirai, it also used exploits. Netlab listed eleven, all for known and long-patched vulnerabilities in devices that had simply never been updated:
- GPON home routers (CVE-2018-10561 and CVE-2018-10562)
- Huawei HG532 routers (CVE-2017-17215)
- the Realtek SDK used in many routers (CVE-2014-8361)
- several Netgear, D-Link and Eir router flaws
- several CCTV, DVR and NVR web interfaces
IBM’s researchers concluded that much of Mozi’s success came from command injection in misconfigured devices, and they suggested the rise in remote working during 2020 may have helped by putting more such devices on the Internet. They also noted one difference from Mirai: Mozi did not try to remove competing malware from the devices it infected.
Once in, a bot did what its signed configuration told it: join DDoS attacks over HTTP, TCP and UDP, report details about the device, run commands, or download and run other payloads. That last capability made Mozi less a single-purpose DDoS tool than a general platform on hundreds of thousands of routers, which is exactly why its end mattered.
2021: the authors arrested, the botnet alive
In August 2021, 360 Netlab announced that Mozi’s authors had been taken into custody by Chinese law enforcement, and that Netlab had provided technical assistance throughout the investigation. The Record reported that the arrests had happened earlier that year. Chinese authorities did not publish the suspects’ names.
Arrests rarely feature in this series. Coreflood’s and GameOver Zeus’s operators were never caught, and Mirai’s authors were caught only after their code had leaked. Mozi is the one case where the authors were arrested while the botnet was still theirs, and it made surprisingly little difference. Netlab explained why in the title of its post: the botnet was “mostly dead”, but its bots were “lingering”. Every infected router was still a scanner, still tried Telnet passwords and exploits on its neighbours, and still served the Mozi binary to new victims over HTTP. Nobody needed to give an order for that to continue. Netlab expected the botnet to “oscillate downward in size on a weekly basis but might keep alive for a long time”.
This is the uncomfortable property of a self-propagating peer-to-peer botnet. Arresting its authors removes the people who can give it new commands, but not the code that recruits new members. Radware’s Daniel Smith put it simply at the time: “Because Mozi is a P2P botnet, taking down this botnet in one swoop is incredibly challenging.”
There was, however, one thing the arrests may have changed. Whoever now had the authors also had, or could get, the private keys that every Mozi bot trusted.
2023: the kill switch
On 8 August 2023, Mozi’s activity in India dropped suddenly. On 16 August the same happened in China. ESET’s researchers, who had been tracking the botnet, set out to find out why, and on 27 September they found it: a configuration message that told the bots to replace themselves. Their technical write-up describes what happened.
How it was delivered. The message arrived in a UDP packet in the place where Mozi’s configuration normally travels, but without the usual BitTorrent DHT wrapping. It was sent eight times. Each copy told the bot to download an update of itself over HTTP, a file called ud.7 hosted on a server first seen on 20 September.
Why the bots accepted it. The configuration was signed with the correct private keys. To a Mozi bot it was indistinguishable from an order from its authors, because, cryptographically, it was one. ESET also found a strong similarity between the original Mozi source code and the new binary.
What it did. According to ESET, the update:
- killed the running Mozi process;
- disabled system services such as
sshdanddropbear; - replaced the Mozi file with itself;
- ran router and device configuration commands;
- blocked access to various ports with
iptables; - kept the same persistence the original Mozi had.
ESET found two versions of the payload. The newer one wrapped the older one and added a function that pinged a remote server, which ESET suspected was for counting how many bots had been neutralised.
Exhibit 2 (illustrative). An infected router before and after the kill switch, following the steps ESET describes. Process names and port numbers are placeholders; the hosting address is the one ESET published, defanged.
# BEFORE: infected router
$ ps
PID COMMAND
211 dropbear -p 22 <- remote admin service
843 <mozi process> <- scanning, serving DHT, obeying configs
$ iptables -S INPUT
-P INPUT ACCEPT <- as shipped by the vendor
# A signed configuration arrives over UDP (sent 8 times):
# "update yourself from 157.119.75[.]16 -> ud.7"
# Signature check against the authors' public keys: VALID
# AFTER: kill switch installed
$ ps
PID COMMAND
902 <ud.7 process> <- same persistence, no scanning, no DDoS
<- dropbear/sshd stopped
$ iptables -S INPUT
-P INPUT ACCEPT
-A INPUT -p tcp --dport <port> -j DROP <- several ports blocked
-A INPUT -p tcp --dport <port> -j DROP
Note what the owner of this router would see: nothing obvious. The device keeps working, a foreign binary is still running, and remote administration has quietly stopped.
Who sent it. ESET named two candidates: the original authors, or Chinese law enforcement, possibly compelling the authors to cooperate. Two facts point to a deliberate, organised shutdown rather than an accident: the correct private keys were used, and India was targeted a week before China. Nobody has publicly claimed responsibility.
What it left behind. The kill switch did not clean the routers. It replaced one piece of unauthorised code with another, changed device configurations, closed ports and switched off remote-administration services, on hardware owned by people who were never asked. It stopped a botnet; it also made a large number of undocumented changes to other people’s devices. That tension, between neutralising malware and acting on machines you do not own, is the subject of the next part.
Hunting the pattern today
Mozi’s choice of BitTorrent’s DHT made it hard to take down, but easy to spot in the right place. A laptop running a torrent client may legitimately talk to the DHT. A router, a camera, a PLC gateway or a building-management controller never should. On an IoT or OT segment, any DHT traffic is a finding.
Exhibit 3a: Sigma, DHT bootstrap lookups from device networks. A DNS lookup for a public BitTorrent bootstrap node from a subnet that holds only embedded devices. Replace the address range with your own IoT or OT segments.
title: BitTorrent DHT Bootstrap Lookup From Device Network
id: 2c9f4e81-7a3d-4b6e-9d12-6e5a0b8c3f47
status: experimental
description: DNS query for a public BitTorrent DHT bootstrap node from a network segment that holds only embedded devices, as used by Mozi
references:
- https://blog.netlab.360.com/mozi-another-botnet-using-dht/
logsource:
category: dns
detection:
selection_names:
query:
- 'router.bittorrent.com'
- 'dht.transmissionbt.com'
- 'router.utorrent.com'
selection_segment:
src_ip|cidr: '10.30.0.0/16' # your IoT/OT segments
condition: selection_names and selection_segment
falsepositives:
- None expected on segments without user devices
level: high
Exhibit 3b: Suricata, Mozi node IDs in DHT traffic. DHT messages are bencoded, and a query starts with the bytes d1:ad2:id20: followed by the 20-byte node ID. Mozi’s IDs begin with 888888. This rule is a starting point built from Netlab’s description; test it against captured traffic before relying on it, because operators could change the prefix through the [hp] field.
alert udp $HOME_NET any -> $EXTERNAL_NET any (
msg:"Possible Mozi DHT query (node ID prefix 888888)";
content:"d1:ad2:id20:888888"; depth:18;
classtype:trojan-activity; sid:9000501; rev:1; )
Exhibit 3c: Splunk, DHT activity from device segments. Combines the two signals in Zeek data: lookups for bootstrap nodes, and UDP conversations with many external peers on high ports, the shape of DHT participation.
index=zeek (sourcetype=zeek:dns query IN ("router.bittorrent.com", "dht.transmissionbt.com", "router.utorrent.com"))
OR (sourcetype=zeek:conn proto=udp local_resp=false "id.resp_p">=1024)
"id.orig_h"="10.30.0.0/16"
| eval signal=if(sourcetype="zeek:dns", "bootstrap_lookup", "udp_peer")
| bin _time span=10m
| stats values(signal) AS signals
dc(eval(if(signal="udp_peer", 'id.resp_h', null()))) AS udp_peers
BY _time, "id.orig_h"
| where mvfind(signals, "bootstrap_lookup")>=0 OR udp_peers>=50
Exhibit 3d: KQL (Microsoft Sentinel). The same bootstrap check through the ASIM DNS parser, joined with firewall logs for UDP traffic from the same hosts.
let DeviceSegment = "10.30.0.0/16";
let Bootstrap = dynamic(["router.bittorrent.com", "dht.transmissionbt.com", "router.utorrent.com"]);
let Lookups = _Im_Dns(starttime = ago(7d))
| where DnsQuery in~ (Bootstrap) and ipv4_is_in_range(SrcIpAddr, DeviceSegment)
| summarize FirstLookup = min(TimeGenerated), Names = make_set(DnsQuery) by SrcIpAddr;
let UdpPeers = CommonSecurityLog
| where TimeGenerated > ago(7d)
| where Protocol =~ "UDP" and ipv4_is_in_range(SourceIP, DeviceSegment)
| where not(ipv4_is_private(DestinationIP))
| summarize Peers = dcount(DestinationIP) by SourceIP;
Lookups
| join kind=leftouter UdpPeers on $left.SrcIpAddr == $right.SourceIP
| project SrcIpAddr, FirstLookup, Names, Peers
| order by Peers desc
The preventive controls are those from Part 4, with one addition. Give device segments an outbound allow-list, patch or replace devices with known exploitable firmware, and remember that the kill switch left neutered Mozi code running on many devices. A router that once hosted Mozi should be reflashed or replaced, not trusted because the botnet is “dead”.
What I take away
A signature proves who holds the key, not who is giving the order. Every earlier part of this series treated signing as the strongest defence a botnet could have. Mozi shows its limit. The kill switch was valid because it was signed with the right key, and that told the bots nothing about whether the authors, the police or someone else was behind it. For any fleet we build, the same is true: protecting the private key, and knowing who can use it, matters as much as the signature check itself.
Arresting people does not stop code that spreads itself. Mozi’s authors were in custody for two years while their botnet kept recruiting. A self-propagating system needs to be switched off from the inside, or starved of vulnerable devices from the outside.
Hiding in public infrastructure cuts both ways. Building on BitTorrent’s DHT made Mozi impossible to cut off without harming everyone else. It also made Mozi conspicuous in any network where BitTorrent has no business, which on a device segment is everywhere.
A kill switch is not a cleanup. The bots stopped attacking, but they still run foreign code, with changed firewall rules and remote administration switched off. An unknown number of devices were altered without their owners’ knowledge, and nobody has said who did it or under what authority.
Mozi’s end raises a question the earlier parts only touched: when, and on whose authority, should anyone change software on machines they do not own to stop a botnet? The next part looks at two operations that answered it openly and in court: Emotet in 2021, where police scheduled the malware to delete itself, and Qakbot in 2023, where the FBI pushed an uninstaller to 700,000 machines.
Sources
- 360 Netlab: Mozi, another botnet using DHT (December 2019)
- 360 Netlab: The mostly dead Mozi and its lingering bots (August 2021)
- The Record: Mozi botnet authors arrested in China (September 2021)
- Security Affairs: Mozi botnet is responsible for most of the IoT traffic, on IBM X-Force’s research (September 2020)
- ESET / WeLiveSecurity: Who killed Mozi? Finally putting the IoT zombie botnet in its grave (November 2023)
- ESET: Infamous IoT botnet Mozi taken down via a kill switch (November 2023)
- Earlier parts of this series: Coreflood, Conficker, GameOver Zeus and Mirai