Play-by-play of the Mirai botnet source code - Part 2 (scanner.c)

Continuing on with our read-through of the Mirai botnet source code, let's take a look at the lines that come after the lines that we covered last time. These lines of code are in charge of more setup that includes setting up the socket, setting up some of the IP and TCP headers, and setting up the array of passwords that the bot will try when attempting to brute force a telnet connection.

    // (A)
    // Set up raw socket scanning and payload
    if ((rsck = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)
    {
#ifdef DEBUG
        printf("[scanner] Failed to initialize raw socket, cannot scan\n");
#endif
        exit(0);
    }
    // (B)
    fcntl(rsck, F_SETFL, O_NONBLOCK | fcntl(rsck, F_GETFL, 0));
    i = 1;
    if (setsockopt(rsck, IPPROTO_IP, IP_HDRINCL, &i, sizeof (i)) != 0)
    {
#ifdef DEBUG
        printf("[scanner] Failed to set IP_HDRINCL, cannot scan\n");
#endif
        close(rsck);
        exit(0);
    }

    // (C)
    do
    {
        source_port = rand_next() & 0xffff;
    }
    while (ntohs(source_port) < 1024);

    // (D)
    iph = (struct iphdr *)scanner_rawpkt;
    tcph = (struct tcphdr *)(iph + 1);

    // (E)
    // 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;

    // (F)
    // Set up passwords
    add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x41\x11\x17\x13\x13", 10);                     // root     xc3511
    ...
    add_auth_entry("\x56\x47\x41\x4A", "\x56\x47\x41\x4A", 1);                              // tech     tech


#ifdef DEBUG
    printf("[scanner] Scanner process initialized. Scanning started.\n");
#endif

Starting at the beginning of this code, segment (A) sets up a socket with the following properties: AF_INET, IPPROTO_TCP, and SOCK_RAW. Let's go over each one of these:

AF_INET - This means that the socket can only send out and receive packets to/from devices with the address family (AF) of IPv4. If you wanted to make a socket for communicating using the IPv6 address family, you would specify AF_INET6, and if you wanted to make a socket for communicating using the bluetooth address family, you would specify AF_BLUETOOTH.

IPPROTO_TCP - This property just means that the socket will be able to speak TCP. 

SOCK_RAW - This is probably the most interesting property here, as it specifically says that this socket is a raw socket. The difference between a socket and a raw socket is that in a socket, you only get to specify the payload and not the headers, while in a raw socket, you can customize the headers as well. This will allow the attacker to play all sorts of interesting tricks by manipulating the headers in code that we'll get to in the future.

In segment (B), fcntl is used to apply the O_NONBLOCK flag to the raw socket. Fcntl allows you to manipulate a file descriptor by applying certain flags to it. A file descriptor, from my understanding, is sort of like a pointer to a file or a socket. Here, the code is setting (F_SETFL) the flags O_NONBLOCK | fcntl(rsck, F_GETFL, 0) to the file descriptor rsck, which references our raw socket. To understand the flag O_NONBLOCK | fcntl(rsck, F_GETFL, 0), we first need to understand that the | operation is essentially allowing you to take two flags and combine them into one megaflag. Knowing this, we can deduce that O_NONBLOCK | fcntl(rsck, F_GETFL, 0) essentially is saying that we take the O_NONBLOCK flag and then combine it with the currently existing flag that rsck has, which is retrieved by fcntl(rsck, F_GETFL, 0). Thus, at the end of the day, this line of code: 

fcntl(rsck, F_SETFL, O_NONBLOCK | fcntl(rsck, F_GETFL, 0));

basically just takes the raw socket referred to by rsck, and then sets the O_NONBLOCK flag on top of it along with all the other flags that it already has.

The O_NONBLOCK flag commands the socket to not wait when we are doing operations that generally require it to block a thread, such as recvfrom which usually would attempt to receive a message from the socket, and if no message was present, it would wait (or block the thread) for the message. This helps make the code a lot more efficient since it will not be required to block the thread at all.

In segment (C), the scanner generates a random port to send the packet from.

In segment (D), the first line 

iph = (struct iphdr *)scanner_rawpkt;

casts the scanner_rawpkt variable, which is a pointer to a char list that is exactly large enough to hold both the iphdr and the tcphdr (initialized here) to be a pointer to a struct of the type iphdr. Then, the following line

tcph = (struct tcphdr *)(iph + 1);

sets the tcph pointer to be a pointer to a tcphdr struct that is immediately after the iph pointer, as adding 1 to a pointer causes it to increment by 1× the size of what the pointer is pointing to.

In a more visual format, this is what memory looks like before the first line of code is run. scanner_rawpkt is just initialized to be a char array large enough to fit an iphdr and tcphdr.

Then, when the first line is run, where the pointer iph is set to point to the same location as scanner_rawpkt but be cast to hold a tcphdr, the memory looks like this:

Then, when the next line is run, where the pointer tcph is set to point to the same location as iph, but incremented by the length of the iphdr struct, this is what memory looks like:

In segment (E), the code is filling in some necessary values for the ip header and the tcp header. There are some more interesting ip and tcp header values that will come later.

In segment (F), the code is storing away a lot of different username, password combinations to try for brute forcing telnet connections. Eventually, the code will select 10 of these at random every time it wants to attempt to brute force access to a device via telnet. More on this later!

Stay tuned for the next installment in this read-through!

Comments

Popular posts from this blog

First-Principles Derivation of A Bank

A Play-by-play of the Mirai botnet source code - Part 1 (scanner.c)

You can control individual packets using WinDivert!