A Quick Insight Into the Mirai Botnet

As you might have read, I recently had a closer look at how easy it actually is to become part of an IoT Botnet. To start a further discussion and share some of my findings I gave a quick overview at the recent Dayton Security Summit. The Mirai Botnet was supposed to be one of the case studies here. But the way things go if one starts diving into code…I eventually gave an overview of how the Mirai Bot actually works and what it does. As such: Here a quick summary of the Mirai Botnet bot.
As described in my previous post, was attacked by a major DDoS attack. Reaching between 620Gbps and 660Gbps it was the largest documented DDoS attack so far. The attack seemingly resulted from a Botnet called Mirai. Shortly after the attack, a post on hackforums claimed to contain the actual source code of just this botnet.
The source code consists of three projects: The bot itself with its CnC server and a loader component.

The first thing one will notice while scrolling the code is the fact that the code also includes debugging symbols. If compiled in DEBUG mode the bot will create a vast amount of output while running. (Also the Source Code is nicely formatted and very easy to read.)
Here a little overview of what the bot does:

  • The bot will monitor the device’s watchdog timer and makes sure it does not simply restart.

    // Prevent watchdog from rebooting device
if ((wfd = open(“/dev/watchdog”, 2)) != -1 ||
(wfd = open(“/dev/misc/watchdog”, 2)) != -1)

  • It hides both its command line arguments and process name

    // Hide argv0
name_buf_len = ((rand_next() % 4) + 3) * 4;
rand_alphastr(name_buf, name_buf_len);
name_buf[name_buf_len] = 0;
util_strcpy(args[0], name_buf);

// Hide process name
name_buf_len = ((rand_next() % 6) + 3) * 4;
rand_alphastr(name_buf, name_buf_len);
name_buf[name_buf_len] = 0;
prctl(PR_SET_NAME, name_buf);

  • Establishes raw socket connection to CnC server
  • Reads a command from the CnC server and executes it
    • The commands mainly consist of attack duration (uint32_t), attack id (uint8_t) and the (target count (uint8_t) which are followed by a list of targets and an attack type. The attack types are TCP flooding, UDP flooding, HTTP GET and an attack based of GRE.
  • “Brick” the device and ensure that it can not be accessed
    • It will first check which PIDs are behind the services listening on ports 22 (SSH), 23 (TELNET) and 80 (www/http). It will then kill the identified processess using “kill -10” and bind an own socket to these ports.

// Kill telnet service and prevent it from restarting
#ifdef DEBUG
printf(“[killer] Trying to kill port 23\n”);
if (killer_kill_by_port(htons(23)))
#ifdef DEBUG
printf(“[killer] Killed tcp/23 (telnet)\n”);
} else {
#ifdef DEBUG
printf(“[killer] Failed to kill port 23\n”);
tmp_bind_addr.sin_port = htons(23);
if ((tmp_bind_fd = socket(AF_INET, SOCK_STREAM, 0)) != -1)
bind(tmp_bind_fd, (struct sockaddr *)&tmp_bind_addr, sizeof (struct sockaddr_in));
listen(tmp_bind_fd, 1);
#ifdef DEBUG
printf(“[killer] Bound to tcp/23 (telnet)\n”);

  • Iterate through /proc/ and kill all identified PIDs using “kill -9”
    • All this actually happens periodically. As such the infected device will simply not be usable at all. On the other side this actually gives us a chance to fingerprint infected devices, as they will only be listening on the ports 22, 23 and 80 but will not reply to any requests.
  • The bot will also scan the devices memory for certain signatures
    • (TABLE_MEM_QBOT|”REPORT %s:%s”), (TABLE_MEM_QBOT2|”HTTPFLOOD”), (TABLE_MEM_QBOT3|”LOLNOGTFO”), (TABLE_MEM_UPX|”\x58\x4D\x4E\x4E\x43\x50\x46\x22″), (TABLE_MEM_REMAITEN|”GETLOCALIP”). These signatures actually identify other known IoT/embedded bots.
  • For expansion the bot will start connecting to randomly generated IP address using port 23/TELNET
    • As can be seen, the bot has a hardcoded blacklist of IP addresses to which it will not connect. While Loopback, internal networks, Multicast and reserved networks make total sense the other ranges are interesting. So one would not want to scan the DoD, as they do have certain defensive measures in place, so that’s fine, too. I figured the same might actually also apply to the US postal service (just a guess, but there might be some Feds in that address space). So it’s still interesting to see that the networks of the General Electric Company and HP are actually also blacklisted. During my talk an attendee noted that HP had also a tight security system and might have reacted to the scanning, but, well, I guess there are more addresses out there and this list looks a bit half-baked.

uint32_t tmp;
uint8_t o1, o2, o3, o4;
tmp = rand_next();
o1 = tmp & 0xff;
o2 = (tmp >> 8) & 0xff;
o3 = (tmp >> 16) & 0xff;
o4 = (tmp >> 24) & 0xff;
while (o1 == 127 ||                             //      – Loopback
(o1 == 0) ||                              //        – Invalid address space
(o1 == 3) ||                              //        – General Electric Company
(o1 == 15 || o1 == 16) ||                 //       – Hewlett-Packard Company
(o1 == 56) ||                             //       – US Postal Service
(o1 == 10) ||                             //       – Internal network
(o1 == 192 && o2 == 168) ||               //   – Internal network
(o1 == 172 && o2 >= 16 && o2 < 32) ||     //    – Internal network
(o1 == 100 && o2 >= 64 && o2 < 127) ||    //    – IANA NAT reserved
(o1 == 169 && o2 > 254) ||                //   – IANA NAT reserved
(o1 == 198 && o2 >= 18 && o2 < 20) ||     //    – 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
return INET_ADDR(o1,o2,o3,o4);

  • When connected to a remote telnet service it tries to login using a specific set of hardcoded credentials and will load itself to the device.

    • The bot does actually not create random combinations of usernames and passwords. It simply works through the list stated below. This is a very direct sign that the author actually did his homework and is targeting specific devices rather than something random.
    • Brian Krebs has actually compiled a list of specific devices which are affected by some of the credentials. (IP camera bulletin boards are a very good source.) The list of devices includes:
      • Dreambox sattelite receivers
      • Packet8 VoIP phones
      • SMC routers
      • Xerox printers
      • Ubiquiti AirOS routers
      • RealTek Routers
      • Many different IP cameras (HiSilicon, ACTi, Axis, Samsung, Vivotek, Mobotix)
    • root
      (none), xc3511, vizxv, admin, 888888, xmhdipc, default, juantech, 123456, 54321, root, 12345, pass, 1111, 666666, password, 1234, klv123, klv1234, Zte521, hi3518, jvbzd, anko, zlxx., 7ujMko0vizxv, 7ujMko0admin, system, ikwb, dreambox, user, realtek, 00000000
    • admin
      (none), admin, password, admin1234, smcadmin, 1111, 1111111, 1234, 12345, 54321, 123456, 7ujMko0admin, 1234, pass
    • support
    • user
    • Administrator
    • administrator
    • supervisor
    • guest
      guest, 12345
    • 666666
    • 888888
    • ubnt
    • meinsm
    • service
    • tech
    • mother
  • Works of static configuration data
    • The static configuration data, like signatures and the CnC server address are stored in “table.c”. All data in here is stored in hex data, but sadly cannot be directly read: it is not plain ASCII. A quick dive into the source code, which my colleague Birk actually gave me a hand with, brings forward that the data is actually obfuscated. A few lines of python later we can actually decode all data in “table.c”. The table_key is actually also part of the .c file.

import sys

decrypt = sys.argv[1].decode(‘string_escape’)

done = “”
table_key = 0xdeadbeef
k1 = table_key & 0xff
k2 = (table_key >> 8) & 0xff
k3 = (table_key >> 16) & 0xff
k4 = (table_key >> 24) & 0xff
for words in decrypt:
done += str(unichr((((ord(words) ^ k1) ^ k2) ^k3) ^ k4))

print done


All in all the Mirai bot is a slim and simple bot with a few surprises. I suppose that most bots are actually aimed at being stealthy and hidden away somewhere in the depths of a device, Mirai on the other side literally turns its victims into zombies. As such if users would actually be relying or using their device, they would notice the infection very quickly. If not, this means that there might be thousands of zombies out there, which will not be discovered before the next summer holidays, when the cameras are used again.

I guess its time for us, as the security community, to both create ethical and lawful approaches for identifying and fixing all those zombies out there and develop a contingency plan for the next time!

Also, as stated during my talk, a little bit of personal opinion: The core problem is not users not changing default passwords, but rather the fact that these devices have interfaces (telnet, ssh) which a) are not documented anywhere b) the user does not know how to use and c) the user simply does not have a need for. Its just sad to know, that Mirai would not have had it so easy, if the manufacturers would actually have applied security best practices out of the box.




Comments are closed.