1/ A wild new sample appears
I managed to find another sample on https://malwr.com website:mitsurugi@dojo:~/infected$ ls -l lluxx; md5sum lluxx ; sha1sum lluxx -rw-r--r-- 1 mitsurugi mitsurugi 917143 août 24 09:58 lluxx 4423955345910ab8fb415b9e7fb0285e lluxx 849ebfb5281b4c407e54f5d1ad152bb288d81eb4 lluxx mitsurugi@dojo:~/infected$ file lluxx lluxx: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.15, BuildID[sha1]=3d8cc691cce4db2327212838d2e797d6c25f8776, not stripped mitsurugi@dojo:~/infected$
This one, it's an x86 executable file.
1/1/ Passive reco
A quick look at this binary confirmed that this is the same family as the previous one. If we look at the strings and function in the binary, we find a lot of similarities:mitsurugi@dojo:~/infected$ strings -10 lluxx /proc/cpuinfo %*[^:]: %d /proc/stat %s %u %u %u %u /proc/net/dev eth0:%Lu %*d %*d %*d %*d %*d %*d %*d %Lu /proc/net/tcp %*s %s %*s %x %*s %*s %*s %*d %*d %d /proc/%s/fd /proc/%s/fd/%s ulimit -n 3000 /proc/self/exe while true;do server=`netstat -nlp | grep :39999` if [ ${#server} -eq 0 ] ; then nohup %s -c 1 & /usr/bin/wake /etc/init.d/wake ### BEGIN INIT INFO # Provides: wake # Required-Start: $remote_fs # Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start or stop the HTTP Proxy. ### END INIT INFO mitsurugi@dojo:~/infected$ strings -10 lluxx /proc/cpuinfo %*[^:]: %d /proc/stat %s %u %u %u %u /proc/net/dev eth0:%Lu %*d %*d %*d %*d %*d %*d %*d %Lu /proc/net/tcp %*s %s %*s %x %*s %*s %*s %*d %*d %d /proc/%s/fd /proc/%s/fd/%s ulimit -n 3000 /proc/self/exe while true;do server=`netstat -nlp | grep :39999` if [ ${#server} -eq 0 ] ; then nohup %s -c 1 & /usr/bin/wake /etc/init.d/wake ### BEGIN INIT INFO # Provides: wake # Required-Start: $remote_fs # Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start or stop the HTTP Proxy. ### END INIT INFO case "$1" in nohup /usr/bin/wake -c 1 & update-rc.d wake defaults 99 chkconfig --add wake chkconfig wake on nohup /usr/bin/wake -c 1 & /dev/watchdog /dev/misc/watchdog /usr/bin/shell nohup /usr/bin/shell -c 1 & %d.%d.%d.%d GET %s HTTP/1.1 (...) mitsurugi@dojo:~/infected$ nm -P Arm1 | awk '$2 == "T" && $1 !~ /^_/ {print "b " $1}' (...) b attack_http_get_flood b attack_http_get_slow b attack_http_get_spider b attack_http_post_flood b attack_http_post_slow b attack_tcp_con b attack_tcp_slow b attack_tcp_std b attack_tcp_syn b attack_udp_root b attack_udp_std (...) b Calcpuuser b checksum_ip b checksum_tcpudp (...) b Getcpuinfo b getOutRates b Getsysinfo (...) b initwake b jiemihttp b Jointhread b kill_process_by_inode b kill_process_by_port (...) b matow (...) b RecvDosMsg b recvexit b Resolv b SendCpuMsg (...) b WebToData b WebtoDns (...) mitsurugi@dojo:~/infected$ strings Arm1 | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" 127.0.0.1 8.8.8.8 mitsurugi@dojo:~/infected$
We recognise the 39999 TCP port, jiemihttp function, matow function, attack_* and so on. We have some things new:
- 8.8.8.8 address
- Resolv function
- WebToData and WebtoDns functions
- New attacks
We want to learn if the network protocol used by the server has changed, and if we can continue to implement it in an innocuous client.
1/2/ Network setup for the analysis machine.
For the dynamic analysis, I setup a VM like the ARM machine. Network setup need a little change.My static analysis reveals that the binary implements its own resolver for DNS and not rely anymore on the host configuration. This explains the IP address 8.8.8.8 that we see in the binary, and the "Resolv" function.
The sandbox must have a gateway to 8.8.8.8, and we need to redirect the flow to a resolver which give the IP we want. We have this setup:
+--------+ +--------+ | | | | |Sandbox +----------------+ Host |---FAKE INTERNET | | | | +--------+ +--------+ 172.16.42.2 172.16.42.42
We hav to grab all DNS traffic to 8.8.8.8, and setup a lying resolver. I choose dnsmasq because it can work standalone, without config file, and can answer whatever I want. Launch it on the cmd line, and it'll resolve:
# sudo iptables -t nat -A PREROUTING -i tap0 -p udp --dport 53 \
-j DNAT --to 172.16.42.42:53
# sudo dnsmasq -h -H hosts -d -q -i tap0 -2 -R -r /dev/null -C /dev/null \
-a 172.16.42.42 -A /com/172.16.42.42,/cn/172.16.42.42
Quick explanation for dnsmasq:
dnsmasq -h #don't read /etc/hosts -H hosts #use this hosts file -d #don't daemon -q #log queries -i tap0 #listen on tun0 -2 #only DNS, no DHCP/TFTP -R #No resolv.conf -r /dev/null #No resolv.conf, yes, you must tell twice -A /com/172.16.42.42,/cn/172.16.42.42 -a 172.16.42.42 #listen address -C /dev/null #Conf file
-A is here for a catchall. Any domains ending in .com or .cn will get 172.16.42.42 as its IP.
The iptables rules will redirect any DNS traffic to my dnsmasq. With this simple setup, the malware will connect to *my* C&C server, whatever the real C&C domain is.
1/3/ jiemi means get the C&C
The C&C IP address is the one that resolves:gdb$ x/s 0x080ed040 0x80ed040 <IPA>: "app.yzyrh.com"
1/4/ Unleash Hell
With this setup, we can see the malware connecting to my C&C server on 172.16.42.42The DNS resolution calls the Resolv() function which use WebToDns and WebToData function. The setup explained in the previous paragraph allows us to hijack this DNS connection, so the malware doesn't reach internet in any way.
The first observation is that the malware send a REGISTER message and doesn't send anymore the statistics each 5 seconds.
2/ Analysis
2/1/ Register command
As seen in the previous version, the bot begins to save the result of ` uname -a` , the IP address and if the binary have root rights. The IP detection doesn't rely anymore with the name of the interface, but on the sa_family of the interface, which is better. The REGISTER message also contain the CPU MHz of the machine by checking the 7th line of /proc/cpuinfo (not always accurate, though).Fun fact: the IP address goes through a complicated set of if/else case in order to detect if it begin with:
- 10.0
- 192.16
- 172.[123]
- 127.
And if yes, the address is not copied in the register message. Nice way to avoid IP private range (detection could be more accurate, but hey, this is malware)
But there is a second loop that copy the IP address if it's different from 127.0.0.1 (?!), so IP address is copied anyway, even if it's in private range!
2/2/ Botnet related commands
We find like the first time the usual command, 103 for launching attacks, 104 for an immediate stop, 105 for exec local commands. There is no more updating facility, and strangely, no output from the command launched. The binary exec it, and drop the output.There is a new command, 106 which trigger the stats thread from the malware. It's a basic switch. Sends 106, receive statistics. Send another time 106, stop statistics.
The bytes are BE, so a "trigger statistics on/off" can be written as:
Packet format : (...) elif cmd == '106': client.send('\x06\x00\x01\x00'+'\x00'*16)
This was made to reduce bandwidth sent to the C&C because if you begin to manage a huge list of bots, you can DOS yourself with statistics sent every 5s for each bots ^^
We have now 11 attack types, two of them depends if we have root rights or not.
Another fun fact here. I think that the programmer mistakenly copy paste its code (click to enlarge):
There is almost the same two code bloacks left, and right. The case depends if binary has root rights or not.
You can see at bottom left 'attack_udp_root', and 'attack_udp_std' if you're not root. Then, at bottom right you see 'attack_tcp_root' and ... 'attack_udp_std' ??? I think the programmer wanted to launch a std tcp attack.
Moreover, there isn't any Xref to the attack_tcp_std function:
But everything has been reversed, and here is the python code used to decode the DDOS commands:
2/3/ DDOS related commands
We have a lot of new DDOS commands, you can se the big switch/case here:We have now 11 attack types, two of them depends if we have root rights or not.
Another fun fact here. I think that the programmer mistakenly copy paste its code (click to enlarge):
There is almost the same two code bloacks left, and right. The case depends if binary has root rights or not.
You can see at bottom left 'attack_udp_root', and 'attack_udp_std' if you're not root. Then, at bottom right you see 'attack_tcp_root' and ... 'attack_udp_std' ??? I think the programmer wanted to launch a std tcp attack.
Moreover, there isn't any Xref to the attack_tcp_std function:
But everything has been reversed, and here is the python code used to decode the DDOS commands:
def prettyprint(s): atk=['attack_tcp_con','attack_tcp_slow','attack_tcp_syn/attack_udp_std',\ 'attack_udp_root/attack_udp_std','attack_http_get_flood',\ 'attack_http_get_slow','attack_http_get_spider','attack_http_post_flood',\ 'attack_http_post_slow'] (attack,t1,t2,atk_type,dport,a,timer,delay,threads,size,f,g,h,i) = \ struct.unpack('<I268x128s256sIIIIIIIIIII',s[:700]) if t2[0]=="\x00": t2="NONE" if atk_type < 9: print "[+] Attack type(%d): %s with %d thread(s)" % (atk_type,atk[atk_type], threads) else: print "[+] Attack type(%d): Unkown attack, new bot version? %d Thread(s)" % (atk_type,threads) print " Targets: %s, %s; dport %d, duration %ds , delay %dms, size %d" % \ (t1,t2,dport,timer,delay,size) #other parameter not used/not important print " Other params: a: %d, f: %d, g: %d, h: %d, i: %d" % (a,f,g,h,i)
2/4/ Conclusion
We can see that the coder continue to update its bot. I wrote a python client compatible for both version in my github: https://github.com/0xmitsurugi/afterburnerI found two C&C, the first one has been shut down really fast after the finding.
The second one, also dead at the time of writing, has been monitored for approximatively two weeks. Nothing really interesting :( just some really slow attacks, kind of 2 HTTP GET by seconds on a server for two minutes. Commands were repeated each 120s to restart the "attack" I think.
There were two website targeted, chinese gaming site. I tried to se if they suffered from the attack, but no. They were always up & running with very good response time. I'm even wondering if this bot is not made to simulate traffic on website (why? and what for? No idea), because it doesn't look like a denial of service to me.
3/ Is "wake" the elknot lost son?
I've read some docs online, and found this one here https://www.botconf.eu/wp-content/uploads/2014/12/2014-2.10-Chinese-Chicken-Multiplatform-DDoS-Botnets.pdf3/1/ Weak algorithm
We can see that elknot uses a weak obfuscation algorithm (+1,-1,+1,-1 and so on) while the wake malware uses (-1,+1,+2,-1,+1,+2,-1, and so on). These algorithms are used to hide the C&C address.3/2/ Protocol grammar
The protocol used by elknot to communicate is binary, with command starting at 0x00. We see that's almost the same as wake: 0x00010001 0x00010002 and so on..3/3/ others similarities?
Is there any Resolv() function in Elknot? Is there another similarities? I don't know, it's maybe just a coincidence.4/ Heading to the future
I have a fully functional client for the malware, and I'm ready to listen to botmasters orders.You can check the source code on my github: https://github.com/0xmitsurugi/afterburner/tree/master/WakeUp
I had a very quick communication with a C&C (down at the time present) which sends me a DDOS command number 17, which I don't have. I continue to search for new samples. If you get one, send it to me, I would be pleased to analyze it :)