Malware Analysis: Cryptocurrency-mining Malware Running on DVRs

April 23, 2014

Authors: Jakub Kroustek and Peter Matula Recently, Johannes Ullrich from SANS Technology Institute reported a successful malware attack on digital video recorders (DVRs). More precisely, the attack was aimed on

Authors: Jakub Kroustek and Peter Matula

Recently, Johannes Ullrich from SANS Technology Institute reported a successful malware attack on digital video recorders (DVRs). More precisely, the attack was aimed on Hikvision DVRs that are used for recording video from surveillance cameras. As we have previously reported, these Internet-of-Things devices are increasingly targeted by malware authors, either to create a large botnet or to gather sensitive user information.

A lot of articles and blog posts referred to the original report in the previous two weeks. However, any deeper analysis was missing and some of the conclusions were inaccurate. Therefore, we would like to reveal the details of this malware by using our Retargetable Decompiler.
We focus on three samples attached to the original report that were found running on these devices. All of them are Linux executable files in the ELF format for the ARM architecture.

  • sample A: MD5 sum 26eed53917ff50dc7819fc070412aa6e; size 162kB; original name D72BNr
  • sample B: MD5 sum ae2d1ee49c3a9d905c40422ea4d96be6; size 106kB; original name
  • sample C: MD5 sum 5c8ecd088e68daec867cbc8d86cf3681; size 4kB; original name mzkk8g

All of these samples have been created by a gcc-based compiler and all unneeded information (e.g. symbolic names) has been removed (stripped).

Sample A

A lot of our colleagues found out that this sample is an ARM-compiled version of an open-source tool cpuminer. The official description of this tool says that “cpuminer is a multi-threaded, highly optimized CPU miner for Litecoin, Bitcoin and other cryptocurrencies. Currently supported algorithms are SHA-256d and scrypt(1024, 1, 1). It supports the getwork mining protocol as well as the Stratum mining protocol, and can be used for both solo and pooled mining.“. However, the existing reports state that this sample is used for Bitcoin mining on the attacked devices. This is not exactly true because the Bitcoin-mining feature (i.e. the SHA-256d algorithm) has been removed from this modified version of cpuminer. Instead, it is used for mining scrypt-based cryptocurrencies only, such as Litecoin, Dogecoin, or many others.

The scrypt mode has been chosen because it is more profitable than the Bitcoin mode for the author of this malware. Although ARM processors are more effective than “classical” x86 CPUs in terms of energy consumption, the mining process is terribly ineffective in terms of consumed electricity. However, the author does not care. The mining of Bitcoin (SHA-256d) is for now the domain of specially designed ASIC chips. For example, the device with the most powerful ARM chip will be still more than 1,000,000 times slower than the most modern ASIC chips.

And what is the profit? Approximately $0.01/day (one cent per day) for one such device mining scrypt-based coins. Is it really profitable for the author of this malware? If the malware is capable to infiltrate thousands of such devices, then the answer is probably “yes”. We can only imagine what will be the next target with such an attitude – printers, watches, or your car? According to the Ullrich’s next report, he already found the same malware mining on a MIPS-based router.

This sample contains several differences to the original cpuminer. They are described in the following text.

We can find that the name and version are stored as a string in this sample: cpuminer 2.3.2. More precisely, it is based on the development version released on 2014-01-18. The sample can also invoke a help message via execution with parameter -h:

As we can see in description of parameter -a, the help message has not been modified and it claims that the application is capable of computing the sha256d algorithm (the one used in Bitcoin). However, author of this modified sample removed its support from the source code.

  • Only the scrypt algorithm is allowed in parsing of command-line arguments. The original version also supported option -a sha256d. The modified versions does not support it, see the decompiled code:

  • Furthermore, the execution of each algorithm (i.e. the miner’s work) was originally also based on the user’s choice, but this is not true in the modified version. In here, the scrypt algorithm is the only choice.
    • Original code:

    • Decompiled code:

Another interesting discovery is that the author removed the usage of the libcurl library for network communication and used native Linux functions for socket communication instead. The motivation was probably to achieve a minimal size of the resulting application because the target device has only limited resources.

The author also added support for translating domain names to IP addresses via the publicly available Verizon DNS servers (,, and
However, this functionality is not used because the author disabled the usage of target servers in the form of http:// and https:// addresses and permitted only IP addresses as a valid input. These addresses specify a remote server that sends a “work” that has to be computed to each miner. Without this server, the miner will be unable to do anything. According to our study, there were several such servers running on different IP addresses and ports. Most of them were hosted in Netherlands. Some of them are still alive (e.g. The default communication port is set to 8334 in this sample, which is also not the default one in cpuminer.

Furthermore, as we can see in the official description, cpuminer supports multiple communication protocols with the server. This is not the case of our sample because its author removed all of them except the stratum protocol. The difference can be seen in the following code snippets:

  • Original code: usage of curl functions, support of http addresses, no default communication port.

  • Simplified decompiled code. The libcurl function has been replaced by native Linux socket calls.

We can also mention that this file has been linked with the uClibc C standard library. This library is much smaller then the “classical” GNU C Library and it is intended to be used on embedded Linux systems, which perfectly fits to this task.

Sample B

The second sample repeatedly picks and scans an IPv4 address to find out if it runs a vulnerable version of the Synology® DiskStation Manager. Our analysis showed that it searches for devices exposed to vulnerabilities CVE-2013-6955 and CVE-2013-6987, both of which have already been fixed in the newer versions of DiskStation Manager. Our analysis also proved that this program only reports potential victim’s IP address (not the firmware version as is claimed in the original article) to remote address No further steps are taken by it at the moment. However, it is highly possible that the author will launch a second wave of attack once he accumulates enough vulnerable targets.

The program’s activity is shown in a form of a call graph in the picture below. Green functions were defined by the author, purple ones are from the C standard library. Functions are called in the top-to-bottom, left-to-right order. The following subsections describe the behavior of individual functions. The presented source codes are simplified and modified outputs of our Retargetable Decompiler. User defined functions and variables were given more meaningful names. The analysis is focused on the malicious activity of the program and ignores some implementation details like parallel execution of functions working with sockets.


Function main()

The main function is the program’s entry point. At the beginning, it checks if the device was not already infected, i.e. if it is not running another instance of this program. Then, it enters an infinite loop, where each iteration generates a certain amount of IPv4 addresses, checks them for a vulnerability and reports the exposed devices.

Function kill_old()

This function checks whether the /var/run/.qq file exists. If the file exists, it means that another instance of this program is already running on the device. In that case, the file contains the identifier of such a process (PID), which is used by the new process to terminate the old one. In any case, the new process always creates the file and writes its PID into it.

Function rand_1_254()

The function generates an integral pseudo-random number in range from 1 to 254 and returns it through its parameter.

Function gen_half_IP()

The function randomly generates the first two octets (16 bits) of an IPv4 address by using rand_1_254(int *res). The numbers are stored into global variables first_octet and second_octet. Not all combinations are allowed because the program contains a list of IP ranges that are not scanned:

Any other combination is accepted.

Function gen_IP_array()

The function generates an array of pointers to strings that represent IPv4 addresses. Variables first_octet and second_octet are used as the first two octets for all the addresses. The rest is generated for all possible address combinations (0 to 255). The size of the array is stored to global variable generate_IP_array_size.

Function check_IP_array()

The function iterates over generate_IP_array and tries to connect to the IP addresses by using try_to_connect(char *ipv4). If it succeeds, the IP is added to global variable final_IP_array, which contains only addresses that will be further scanned. The original entries from generate_IP_array are freed. New entries are added by using the add_to_final() function.

Function try_to_connect()

The function tries to connect to the provided IPv4 address on port 5000.

Function process_final()

The function iterates over entries in final_IP_array and calls scan_and_report(char *ipv4) on each one of them.

Function scan_and_report()

The function tries to connect to the given IPv4 address in the same way try_to_connect(char *ipv4) does. The only difference is that it sets the socket timeout by using setsockopt(). If the connection was established, then send_scan(char *ipv4, int sfd) sends the scanning request. The response is checked, and if it satisfies the conditions, send_report(void *bin_ipv4) sends the binary representation of the vulnerable device’s IP address to the remote server.

The received response has to satisfy these conditions in order to report the device as vulnerable:

  • The response has to be OK: it has to contain substrings HTTP/ and 200 OK.
  • The response has to contain substrings major=, &minor=, &build=, &junior=, and &unique=synology_.

Moreover, the device firmware version has to be of one of these combinations:

  • major == 4 && minor == 1
  • major == 4 && minor == 2 && build <= 3242
  • major == 4 && minor == 3 && build <= 3809
  • major == 4 && minor == 0 && build <= 2258

Function send_scan()

The function sends a request to the opened socket.

Function send_report()

The functions sends the provided IPv4 address in the binary form to

Sample C

The last sample (calling itself http) is quite small and it serves as a file downloader over the HTTP protocol. The author left a help message so we can easily deduct its purpose. This program expects the following five arguments. First, the IP address (1), port (2), and host-name (3) of the remote sever has to be specified. The next argument (4) contains the name of the file that will be downloaded. The final argument specifies the path and name of the downloaded file within the current device (DVR). The simplified decompiled code is shown next.


We can conclude that each of these three samples contained a different set of functionality. The first one earn profit to its author via cryptocurrencies mining. The second one is used for scanning network and looking for other vulnerable devices probably for a future attack. The last one is a utility that prepares a run-time environment for the other samples via downloading the necessary files from the remote server.

The infection of these DVR devices has been done without any advanced exploitation of vulnerabilities. It was much simpler this time. Indeed, it just tries to get in via a default combination of login and password. Our advice is to always change the default login and password, set by manufacturers, as soon as you start using your device. Furthermore, it is also necessary to periodically update software running in your devices whenever this process is not automated.

Keeping an eye on protection of multiple devices may be time consuming and it is always welcomed to do that from one place, like we do in AVG Zen for mobiles, tablets, and PCs.

April 23, 2014