Friday, September 18, 2015

MMD-0043-2015 - Polymorphic in ELF malware: Linux/Xor.DDOS

Background

A share of knowledge I have, hopefully to make internet safer - @unixfreaxjp

The threat of Linux/XOR.DDoS, a China-made ELF backdoor & ddoser malware, a rather specific threat compares to other Chinese ELF ddosers, and it's still on going. I just received a good question (from I assumed from a victim of infection or a researcher) about why the found malware binary is not the same as what was firstly executed one. Well, this writing is short and covering the answer for the asked question only. But, the information maybe important for the mitigation and detection, and also various methodology I use for the sharing to other NIX mates, so I write this post with three processes I conduct to every ELF malware investigation: in reversing, debugging and forensics ways. Please bear with the poor english since I had few time to check, or to the lack of the explanation.

Polymorphic is a behavior of malware during self-reproduction constantly changes ("morphs") the file characteristic (size, hash, etc), and it may not be the same with the previous copy or as previous pre-infection state. The goal of this changes is to makes it difficult for signature-based antivirus software programs to recognize and detect the polymorphed malware.

Polymorphic method in malware is an usual practise in windows malware. In UNIX malware maybe it is not as commonly heard as in Windows; but since the nature of NIX malware are coming from networking, either to be "extracted" from encoder/infector files, downloaded or dropped by other malware from the beginning, so..I guess we have many hashes by default. But in this post, we are actually dealing with a polymorphic behavior malware just like ones infecting Windows during the self-copy method.. so I guess it is worth to write a bit.

The reported case was a real infection, a case of known gang/crooks, I am allowed to post the the attack log as per following:

Yes, it is a recent attack, please block the IP addresses.

The above log is typical Linux/Xor.DDOS hostasa.org ssh brute attack pattern. I announced the case not so long ago here (different cases, same attacker)-->[link] and the recent incident was reported too in here-->[link]. I uploaded this ELF malware sample into Virus Total w/the link is here-->[link].

Polymorphic PoC

When Linux/XOR.DDoS malware was executed, it will come to the stage that it seeks the place to self-copy it self, in my case the linux system call can show us the effort to write file like:

open("/usr/bin/lgjgjmkkgd", O_WRONLY|O_CREAT, 0777) ; depends, in mine is -1 EACCES (Permission denied)
open("/bin/lgjgjmkkgd", O_WRONLY|O_CREAT, 0777)     ; depends, in mine is -1 EACCES (Permission denied)
In a well-hardened linux system and if the malware is not executed as root you should see the same result as per pasted above. And that time the malware will aim to the only their favorite heavenly place to copy: /tmp :
open("/XOR.DDOS.SAMPLE", O_RDONLY)      ; initial exec malware open itself
lseek(3, 0, SEEK_SET);                  ; set LSET to OFFSET to READ
open("/tmp/lgjgjmkkgd", O_WRONLY|O_CREAT, 0777); open self-copy target w/perm 777
read(3, "\177ELF\1\1\1\0\..");          ; read the malware bin
lseek(4, 0, SEEK_SET)                  ; set LSET to OFFSET to WRITE
14878 read(3, "\177ELF\1\1\1\0\…       ; copy process read..
14878 write(4, "\177ELF\1\1\1\0\…      ; copy process write

By reverse engineering the ELF malware, after seeking for a while, the assembly procedure below is responsible for the above operation: (the bigger picture click-->>THIS )


You can see the cascade of jumps during each error that might occur until it ends up to the accessed one for the self-copy purpose, starting from /usr/bin to /bin , and in my case it is ended with /tmp/[randomname]. The filename is random and the full path with the directory aimed is to be "fired" via an original API to execute the execve(), but we will go to this topic later on.

In Linux memory forensics the blob data copied can be seen clearly with some beautify effort, a good old hexdump is still a favorite in dealing with raw hex data:

## Copy process illustration (read and write of copy process) in the end of file:
[...]
00098bd0  6d 65 00 5f 64 6c 5f 6d  61 70 5f 6f 62 6a 65 63  |me._dl_map_objec|
00098be0  74 5f 64 65 70 73 00 5f  6e 6c 5f 43 5f 4c 43 5f  |t_deps._nl_C_LC_|
00098bf0  49 44 45 4e 54 49 46 49  43 41 54 49 4f 4e 00 5f  |IDENTIFICATION._|
00098c00  64 6c 5f 6e 73 00 5f 6e  6c 5f 6c 6f 61 64 5f 6c  |dl_ns._nl_load_l|
00098c10  6f 63 61 6c 65 5f 66 72  6f 6d 5f 61 72 63 68 69  |ocale_from_archi|
00098c20  76 65 00 77 63 74 72 61  6e 73 00                 |ve.wctrans.|
00098c2b
And the copy process was ended gracefully, as per debug check shows in the system call below:
read(3, "", 4096):   ; EO/termination w/no space
close(3);            ; end of copy (reading)
close(4);            ; end of copy (writing)

Nothing so special about operation above, but it is related to the next steps, let's go forward.. Now, we can see up to here that the malware was self copied! But why the file gets different?
The next system's call showing the effort to open the written file afterward with flag to write.. What's going on?

open("/tmp/lgjgjmkkgd", O_WRONLY); ; opening the copied file
lseek(3, 0, SEEK_END) = 625707 <==size  ; set LSET to the EOF for writing
; SEEK_END = *)    ; note the size of original malware
It looks like the pointer of LSET used to write is pointing to the end of the file itself, noted the SEEK_END flag. For the illustration see the paste "*)" position below:
## Illustration of the LSET set in the end of file..

00098bd0  6d 65 00 5f 64 6c 5f 6d  61 70 5f 6f 62 6a 65 63  |me._dl_map_objec|
00098be0  74 5f 64 65 70 73 00 5f  6e 6c 5f 43 5f 4c 43 5f  |t_deps._nl_C_LC_|
00098bf0  49 44 45 4e 54 49 46 49  43 41 54 49 4f 4e 00 5f  |IDENTIFICATION._|
00098c00  64 6c 5f 6e 73 00 5f 6e  6c 5f 6c 6f 61 64 5f 6c  |dl_ns._nl_load_l|
00098c10  6f 63 61 6c 65 5f 66 72  6f 6d 5f 61 72 63 68 69  |ocale_from_archi|
00098c20  76 65 00 77 63 74 72 61  6e 73 00 *<====          |ve.wctrans.*) <==
00098c2b
And then we have these two operation called timeoftheday() and writing the specific strings in the end of the file:
gettimeofday({1442479267, 397488}, NULL) ; for randomid() seed..
write(3, "wlpvpovdvi\0", 11) ; 'size is set to 11'
    ; write string "wlpvpovdvi\0"-
    ; in the LSET position (EOF)
So this is what happened for BEFORE and AFTER the writing:

So we see the file was added to 11 characters, which means we should have 11 bytes bigger for the size of file after this self-copy process, we'll get there..hang on!

Following the calls of the malware process, we can see the new file was saved:

close(3)  ; end of writing process..
And executed! Noted: execve() function is used to spawn the shell command.
execve("/tmp/lgjgjmkkgd", ..); ; main running process of XOR.DDOS in new PID
                               ; with new size (& hash)
You can see how it was executed in the saved process data in the /proc :-), so believe me, it doesn't really any fancy tools for UNIX forensics, since UNIX gods already provided us openly with everything:
lgjgjmkkg 14881 MMD  cwd   DIR  8,6     4096        7209106 /TESTDIR
lgjgjmkkg 14881 MMD  rtd   DIR  8,1     4096              2 /
lgjgjmkkg 14881 MMD  txt   REG  8,1   "625718 <== NEW SIZE" 829 /tmp/lgjgjmkkgd
lgjgjmkkg 14881 MMD    0u  CHR  1,3      0t0           1028 /dev/null
lgjgjmkkg 14881 MMD    1u  CHR  1,3      0t0           1028 /dev/null
lgjgjmkkg 14881 MMD    2u  CHR  1,3      0t0           1028 /dev/null
..as per seen here it runs in new PID , not clone nor forking/threading since execution used the shell spawning. See the new size, it gets bigger by 11 bytes.

Below is the illustration of malware samples original and after copy-injected.

$ md5sum XOR.DDOS.SAMPLE lgjgjmkkgd
"7642788b739c1ee1b6afeba9830959d3"  XOR.DDOS.SAMPLE
"df50d096fb52c66b17aacf69f074c1c3"  lgjgjmkkgd

$ ls -l XOR.DDOS.SAMPLE lgjgjmkkgd| awk '{print $5, $6, $7, $9}'
"625718" Sep 17 lgjgjmkkgd
"625707" Sep 17 XOR.DDOS.SAMPLE
We have different hash and size.

Okay, we're done with the debugging and forensics. Let's see how the reverse engineering goes for this ELF malware binary for the above processes.

This is the part where the malware self-copy process was executed in my sample case. Noted: there are so many cases to trail with the similar codes in copying, write files and randomizing them, I counted about more than 4 scenarios prepared for this operation and the author really calculate every possibilities in his code to make sure the malware will run.

the jump to 0x804dfc2 will take you to the next process.

The assembly snip below is explaining the writing process to the done-copied file by the malware, it is not using the randomizing 11 characters but the malware was picking a hard coded xor crypt strings that is saved in 0x080cf120 (symbol: str.__Ff3VE._7).

The snprintf() is an API function that will lead (in the VERY end) to SYS_write at sys/syscall, since we deal with the statically compiled ELF many libc trails will appear in reversing the function, we may see more of these, sorry to say, unnecessary codes.

The timeoftheday() result which was shown during debugging is caused by the function which was called, named function randomid().

↑Obviously, is a self-explanatory that the timeoftheday() is fetching the system time as the seed needed in randomid() function.

There is an additional information too actually: I think maybe it is good for our community to know too: Linux/XOR.DDoS ELF malware is using a uncommon seen function to execute the shell command, it was called: LinuxExec_Argv() and LinuxExec_Argv2(), which was called to act as an API to execute non direct syscall basis commands by the malware (well, this is a static compiled binary), these functions are typical in characteristic, it is a very simple in use, easy to spot (smile) and these are responsible to call execve(), a linux system call commands (with the environment parameter parsed) to be executed during an infection, and also to call execvp() for the file execution purpose (with parsing the file path), i.e. shown in the code below:
You may want to see the reference of exec method with UNIX C library (libc) on execve, execvp at man(2) pages, and yes, UNIX gods are also providing us with good reference too.

Conclusion & reference

Yes, Linux/XOR.DDoS malware after copied and executed (read: successfully infecting us) will have a different size (11 bytes bigger..depends.. I only check one binary for this), and have a different hash. So this means that the malware spotted in the panel may not be detected by the scanner used inside of the Linux box if only detecting by the hash.

Many of us still think, Yeah..ELF malware..won't harm us or end users much.. But remember, IoT are mostly linux basis, take a look of the most of router's OS now. Also, the infection method and volume of ELF malware is getting better and bigger by days. As proof: We have about 6 of new ELF malware for 2 and half years span only! As MMD (read: MalwareMustDie, NPO), we suggest to be prepared to update the ELF malware detection quality as earliest as possible, once an ELF malicious binary hit a server the impact can be way much bigger than a PE hit a PC.

Below are links to the previous Linux/XOR.DDoS analysis:.
http://blog.malwaremustdie.org/2015/07/mmd-0037-2015-bad-shellshock.html
http://blog.malwaremustdie.org/2014/09/mmd-0028-2014-fuzzy-reversing-new-china.html

The "new" CNC of the threat:

Oh btw,the CNC is very alive even now...and sending the download/payload too. here's the pcap snips for a hard proof:


Kudos radare.org folks for convincing me to upgrade to git version from /usr/ports one:

Stay safe folks! Hope this short writing helps!

#MalwareMustDie