Back to Blog

What Mirai Actually Does at the Network Layer

Mirai's source code has been public since 2016, and every variant that has followed — Satori, Okiru, Masuta, and dozens of others — shares the same fundamental architecture. A loader server exploits vulnerable IoT devices. Those devices connect to a command-and-control (C2) server over a proprietary binary protocol on TCP port 23 or 2323 (the same ports used for Telnet, which is how devices get compromised in the first place). The C2 then issues attack commands targeting third-party victims.

From the perspective of a bare-metal Linux server, there are two distinct scenarios. First, your server is being scanned and potentially recruited into the botnet. Second, your server is a victim — receiving DDoS traffic from bots that have already been recruited. The traffic signatures for each are completely different, and conflating them is a common source of confusion.

Mirai Scanning Traffic: What It Looks Like

Mirai's scanner generates TCP SYN packets to random IPv4 addresses on a fixed list of ports. The default port list in the original source includes 23, 2323, 80, 8080, 8443, and 9527. More recent variants have expanded this to include 48101, 37215 (a Huawei home router port), and 52869 (used in some Realtek SDK exploits).

On your server, incoming Mirai scanning traffic looks like a low-rate SYN flood spread across the above ports from hundreds of different source IPs. The key distinguishing characteristic is that no ACK ever follows. The scanner moves on immediately after the SYN. You will never see an established connection from a Mirai scanner unless your server is genuinely vulnerable on those ports.

# Watch for inbound SYN-only connections to Mirai target ports
tcpdump -nn -i eth0 'tcp[tcpflags] == tcp-syn and (port 23 or port 2323 or port 48101 or port 37215)' -c 50

# Typical output during active scanning:
# 14:32:01.112233 IP 185.220.101.47.54821 > 203.0.113.5.23: Flags [S]
# 14:32:01.114891 IP 91.108.4.120.41234 > 203.0.113.5.2323: Flags [S]
# 14:32:01.119204 IP 45.155.205.33.58812 > 203.0.113.5.48101: Flags [S]

Notice the source ports are high-numbered ephemeral ports (above 32768), source IPs are globally distributed and non-sequential, and the destination ports are exactly those Telnet/management service ports. In /proc/net/dev, a Mirai scan manifests as a modest increase in received packets without a proportional increase in received bytes, because SYN packets are only 40–60 bytes each.

# Watch /proc/net/dev for packet-to-byte ratio anomalies
watch -n1 'awk "/eth0/{print \"RX pkts:\", \$3, \"bytes:\", \$2}" /proc/net/dev'

# During Mirai scanning, you might see:
# RX pkts: 142837  bytes: 8570220   (~60 bytes/packet = SYN flood pattern)
# Versus normal web traffic:
# RX pkts: 12450   bytes: 9847200   (~790 bytes/packet = mixed TCP)

Mirai C2 Command Traffic: The Outbound Signature

If your server has actually been compromised and enrolled in a Mirai botnet, the traffic pattern flips. The infected process maintains a persistent outbound TCP connection to the C2 server. Original Mirai used TCP port 23 for C2 communication as well, relying on the fact that most firewalls allow outbound Telnet. Some variants use ports 80 or 443 to blend into normal web traffic.

The C2 keepalive sends a small packet (typically 4–8 bytes) every 60 seconds. When an attack command arrives, the bot opens raw sockets and begins generating attack traffic. The key tell in /proc/net/tcp or ss output is an established outbound connection to an external IP on an unusual port with a tiny receive queue and a very high uptime:

# Look for suspicious persistent outbound TCP connections
ss -tnp state established '( dport = :23 or dport = :2323 or dport = :48101 )'

# Or scan for established connections with very small traffic volume
# (C2 keepalives are tiny but connections are long-lived)
ss -tnp | awk '$3 > 0 {print}' | grep -v "127.0.0.1"

# Check /proc/net/tcp for established connections (hex port values)
# Port 23 = 0x0017, port 2323 = 0x0913
awk '$4 == "01" {printf "ESTABLISHED dst:%s port:%d\n", $3, strtonum("0x" substr($3,10,4))}' \
  /proc/net/tcp | grep -E "port:(23|2323|48101)"

If your server is running legitimate Telnet services (rare, but it happens on old infrastructure), filter those out first. A Mirai C2 connection will be to an IP that does not belong to your organization, with no reverse DNS or with reverse DNS pointing to a residential or cloud hosting provider in an unexpected country.

iptables and ipset Detection Patterns

The most reliable way to detect active Mirai scanning at scale is to log hits to the known Mirai port list using iptables. This is not mitigation — it is detection. You are creating evidence and measuring attack intensity:

# Create a log chain for Mirai-port hits
iptables -N MIRAI_DETECT
iptables -A MIRAI_DETECT -m limit --limit 10/min --limit-burst 20 \
  -j LOG --log-prefix "MIRAI_SCAN: " --log-level 4
iptables -A MIRAI_DETECT -j DROP

# Route Mirai-typical ports into the detection chain
iptables -A INPUT -p tcp --dport 23 -j MIRAI_DETECT
iptables -A INPUT -p tcp --dport 2323 -j MIRAI_DETECT
iptables -A INPUT -p tcp --dport 48101 -j MIRAI_DETECT
iptables -A INPUT -p tcp --dport 37215 -j MIRAI_DETECT

# Count hits over time to spot scanning waves
watch -n5 'iptables -nvL MIRAI_DETECT | head -3'

The --limit flag keeps the kernel log from flooding. The hits will appear in /var/log/kern.log or journalctl -k with the MIRAI_SCAN: prefix, including source IPs. To build a dynamic blocklist with ipset:

# Create a hash:ip set for Mirai scanner IPs
ipset create mirai_scanners hash:ip timeout 3600 maxelem 65536

# Use iptables to auto-add IPs that hit Mirai ports
iptables -A INPUT -p tcp --dport 23 \
  -m recent --name mirai_hits --set --rsource
iptables -A INPUT -p tcp --dport 2323 \
  -m recent --name mirai_hits --set --rsource
iptables -A INPUT -p tcp -m recent --name mirai_hits \
  --rcheck --seconds 60 --hitcount 3 \
  -j SET --add-set mirai_scanners src
iptables -A INPUT -m set --match-set mirai_scanners src -j DROP

Is your node scanning others — or being scanned?

Flowtriq detects attacks like this in under 2 seconds, classifies them automatically, and alerts your team instantly. 7-day free trial.

Start Free Trial →

Log-Based Detection Without Packet Capture

If you do not have packet capture running, system logs still offer useful signals. The SSH daemon, if it is exposed, will log rapid failed authentication attempts from IoT IP ranges. These are often Mirai bots that have been retasked to credential-stuff SSH after failing to find Telnet. More directly, /var/log/auth.log or journalctl -u sshd will show the pattern:

# Summarize SSH brute-force sources over the last hour
journalctl -u sshd --since "1 hour ago" \
  | grep "Failed password" \
  | awk '{print $(NF-3)}' \
  | sort | uniq -c | sort -rn | head -20

# Example output during Mirai-sourced credential stuffing:
#  143 45.155.205.33
#   98 185.220.101.47
#   87 91.108.4.120
#   62 192.241.235.132

For systems where you want a persistent record without full packet capture, ulogd2 can write netfilter log entries to a structured database instead of syslog. This lets you query historical Mirai scanning activity long after the event.

What Flowtriq Sees: Node Being Attacked vs. Node in Botnet

From Flowtriq's perspective, the two scenarios produce very different metric patterns. When your node is being attacked by a Mirai botnet, the Flowtriq agent sees a spike in inbound PPS that crosses the anomaly threshold. The attack traffic is typically UDP-based (Mirai's default attack vectors include UDP flood, ACK flood, DNS flood, and HTTP flood). Flowtriq classifies the attack type within the first few seconds and fires an alert with the attack vector, PPS rate, and a PCAP for upstream mitigation requests.

When your node is part of a botnet and is generating outbound attack traffic, Flowtriq sees an anomalous increase in outbound PPS. This is the more dangerous scenario from a legal and operational standpoint: your server's IP is in the PCAP of whoever you are attacking, and your ISP may receive abuse complaints. Flowtriq's outbound anomaly detection fires an alert in the same way as an inbound attack, but the direction field in the alert reads "egress." The PCAP will show packets destined for the victim IP rather than sourced from external addresses.

Incident response note: If Flowtriq fires an outbound anomaly alert and your PCAP shows UDP traffic destined for a single external IP at high PPS, immediately isolate the affected host from the network. Do not just kill the process — the system is compromised and should be treated as untrusted until it has been fully re-imaged from a known-good snapshot.

Hardening Against Initial Compromise

Mirai cannot infect a server it cannot log into. The hardening steps are well-known but worth repeating because Mirai variants still successfully recruit thousands of new hosts every month:

  • Disable Telnet (port 23 and 2323) entirely. There is no legitimate use case for Telnet on a modern server.
  • If you run SSH, move it off port 22 and enforce key-only authentication. Set PasswordAuthentication no in /etc/ssh/sshd_config.
  • Block outbound connections to port 23 and 2323 at the firewall. A compromised host cannot phone home if it cannot reach the C2.
  • Run chkrootkit or rkhunter weekly. Mirai installs itself in /tmp and sets up a cron entry or modifies /etc/rc.local.
  • Monitor for unexpected processes opening raw sockets: ss -np | grep "UNCONN" combined with the PID and process name is a fast check.

Protect your infrastructure with Flowtriq

Per-second DDoS detection, automatic attack classification, PCAP forensics, and instant multi-channel alerts. $9.99/node/month.

Start your free 7-day trial →
Back to Blog

Related Articles