An Analysis of ShellShock Malware
TL;DR I had this malware from a gist which i dont really remember right now. I downloaded it and started to analyse what this is all about. As i analyzed so far, it's somehow broken. May be i am late ?
Ok lets start with file command.
eren@lisa:~$ file nginx
nginx: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.18, stripped
eren@lisa:~$
file program tells us that it's statically linked and stripped. So, Most of its dependencies are inside the code and we will have difficulties while debugging it because the debug informations have been removed.
We'll fire up gdb and start to debug the malware from its starting point.
eren@lisa:~$ gdb nginx
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/eren/nginx...(no debugging symbols found)...done.
(gdb) info file
Symbols from "/home/eren/nginx".
Local exec file:
`/home/eren/nginx', file type elf32-i386.
Entry point: 0x8048160
I'll put a breakpoint to 0x8048160
b * 0x8048160
and then i'll run the executable and it'll will stop in its entry point.
(gdb) b * 0x8048160
Breakpoint 1 at 0x8048160
(gdb) r
Starting program: /home/eren/nginx
Breakpoint 1, 0x08048160 in ?? ()
=> 0x08048160: 31 ed xor ebp,ebp
(gdb) x/20i $pc
=> 0x8048160: xor ebp,ebp
0x8048162: pop esi
0x8048163: mov ecx,esp
0x8048165: and esp,0xfffffff0
0x8048168: push eax
0x8048169: push esp
0x804816a: push edx
0x804816b: push 0x804be00
0x8048170: push 0x804be40
0x8048175: push ecx
0x8048176: push esi
0x8048177: push 0x804b2cc
0x804817c: call 0x804b7c0
0x8048181: hlt
From 0x8048160 to 0x8048181, the code blocks are standart way of glibc execution. Therefore, the malware programmer didnt modified the entry point. If you compile an empty helloworld program, you would see the same starting code like above. Xoring ebp to ebp means nothing but this type of practices come from ABI (Application Binary Interface Specs). In the address of 0x804817c, the calls refers to <_libcstart_main@plt> function.
You can look at the details here So, first paramether to libcstartmain function is actually the last one pushed on the stack. Therefore, 0x804b2cc is our main function. Let's put a breakpoint there and go on.
b * 0x804b2cc
Breakpoint 2, 0x0804b2cc in ?? ()
=> 0x0804b2cc: 8d 4c 24 04 lea ecx,[esp+0x4]
(gdb) x/30i $pc
=> 0x804b2cc: lea ecx,[esp+0x4]
0x804b2d0: and esp,0xfffffff0
0x804b2d3: push DWORD PTR [ecx-0x4]
0x804b2d6: push ebp
0x804b2d7: mov ebp,esp
0x804b2d9: push edi
0x804b2da: push esi
0x804b2db: push ebx
0x804b2dc: push ecx
0x804b2dd: sub esp,0x1464
0x804b2e3: push 0x0
0x804b2e5: call 0x8056ce0
0x804b2ea: mov ebx,eax
0x804b2ec: call 0x8057430
0x804b2f1: xor eax,ebx
0x804b2f3: mov DWORD PTR [esp],eax
0x804b2f6: call 0x804cb90
0x804b2fb: mov DWORD PTR [esp],0x0
0x804b302: call 0x8056ce0
0x804b307: mov ebx,eax
0x804b309: call 0x8057430
0x804b30e: xor eax,ebx
0x804b310: mov DWORD PTR [esp],eax
0x804b313: call 0x8048240
0x804b318: call 0x8048fd5
0x804b31d: movzx eax,BYTE PTR ds:0x80cc2b5
0x804b324: add esp,0xc
0x804b327: push eax
0x804b328: movzx eax,BYTE PTR ds:0x80cc2b4
0x804b32f: push eax
(gdb)
As you may see, the main function has a lot of calls to other functions. I'll not go into every call but cover most important aspects of its actions.
0x8074fd0: mov edx,ebx
0x8074fd2: mov ebx,DWORD PTR [esp+0x4]
0x8074fd6: mov eax,0x7a
0x8074fdb: call DWORD PTR ds:0x80cbfd0
In the forth line, i see a call to somewhere over a pointer. I need to deference it.
(gdb) p/x *0x80cbfd0
$1 = 0xf7ffd420
(gdb) x/10i 0xf7ffd420
0xf7ffd420 <__kernel_vsyscall>: push ecx
0xf7ffd421 <__kernel_vsyscall+1>: push edx
0xf7ffd422 <__kernel_vsyscall+2>: push ebp
0xf7ffd423 <__kernel_vsyscall+3>: mov ebp,esp
0xf7ffd425 <__kernel_vsyscall+5>: sysenter
After deferencing, It becomes our gate to the kernel. I wrote the address of 0x80cbfd0 somewhere to keep in my mind. We need to analyze the registers before it goes through syscall gate.
(gdb) info reg
eax 0x7a 122
ecx 0xffffffff -1
edx 0x0 0
ebx 0xffffd5f6 -10762
esp 0xffffd5e0 0xffffd5e0
ebp 0xffffd7c8 0xffffd7c8
esi 0x0 0
edi 0x804be00 134528512
eip 0x8074fdb 0x8074fdb
eflags 0x286 [ PF SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x0 0
We need to look up syscall table for 0x7a (eax value). You can find syscall table here
122 belongs to uname syscall.
(gdb) x/20c $ebx
0xffffd5f6: 76 'L' 105 'i' 110 'n' 117 'u' 120 'x' 0 '\000' 0 '\000' 0 '\000'
0xffffd5fe: 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
0xffffd606: 0 '\000' 0 '\000' 0 '\000' 0 '\000'
So the malware got the information about my system.At least it knows which operating system im running :)
It uses several times of brk syscall to allocate some space from the heap.
0x8056ce6: push ebx
0x8056ce7: xor ebx,ebx
=> 0x8056ce9: mov eax,0xd
0x8056cee: call DWORD PTR ds:0x80cbfd0
Another syscall for getting time and later it will get also pid number.I'll skip these until socket things come up.
0x805864f: nop
0x8058650: mov edx,ebx
0x8058652: mov eax,0x66
0x8058657: mov ebx,0x1
=> 0x805865c: lea ecx,[esp+0x4]
0x8058660: call DWORD PTR ds:0x80cbfd0
This is very important, the malware creates a socket with socket syscall.This time, eax is 102 and it is sys_socketcall in linux syscall table (i386).
When we look at the parameters,
(gdb) info reg
eax 0x66 102
ecx 0xffffb2b0 -19792
edx 0x54302d33 1412443443
ebx 0x1 1
esp 0xffffb2ac 0xffffb2ac
ebp 0xffffc338 0xffffc338
esi 0x2e5fd200 778031616
edi 0x804be00 134528512
eip 0x8058660 0x8058660
eflags 0x282 [ SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
Ebx is really important, 0x1 tells us that it is a syssocket and it will go down just right there. Ecx is our pointer which is being passed into syssocket function.
And then, after syscall execution, the kernel should give us a file descriptor that we can write to or read from it.
I go to my proc file system to see which file descriptor are mapped to my process adress space.
eren@lisa:/proc/30480/fd$ ls -la
total 0
dr-x------ 2 eren eren 0 Oct 4 18:03 .
dr-xr-xr-x 8 eren eren 0 Oct 4 17:24 ..
lrwx------ 1 eren eren 64 Oct 4 18:04 0 -> /dev/pts/0
lrwx------ 1 eren eren 64 Oct 4 18:04 1 -> /dev/pts/0
lrwx------ 1 eren eren 64 Oct 4 18:03 2 -> /dev/pts/0
lrwx------ 1 eren eren 64 Oct 4 18:04 3 -> socket:[324793]
lrwx------ 1 eren eren 64 Oct 4 18:04 4 -> socket:[324794]
lr-x------ 1 eren eren 64 Oct 4 18:04 5 -> pipe:[324795]
l-wx------ 1 eren eren 64 Oct 4 18:04 6 -> pipe:[324795]
lrwx------ 1 eren eren 64 Oct 4 18:04 7 -> socket:[327132]
Yeah there it is.
0x805844c: mov eax,0x66
0x8058451: mov ebx,0x3
0x8058456: lea ecx,[esp+0x4]
=> 0x805845a: call DWORD PTR ds:0x80cbfd0
This time, Ebx is 0x3 that means SYSCONNECT. Ecx now points to paramethers that we pushed into sysconnect. Let's examine the parameter.
2515 case SYS_CONNECT:
2516 err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
2517 break;
The code above is from kernel source.Paramethers are a0,a pointer and another a2. These are all 12 bytes. So ecx should point to 12 bytes.
(gdb) x/3wx $ecx
0xffffb2b0: 0x00000007 0xffffc30c 0x00000010
(gdb)
Now, a0 => 0x07, sockaddr pointer is => 0xffffc30c and last paramether is 0x00000010.
We want to find where this malware connects. We know that sockaddr structure holds this information. Sockaddr structure is here
typedef unsigned short sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
We can do our estimation that 1 short + 14 char => 16 bytes. So i'll skip the first 2 bytes and print the others.
(gdb) x/14bu 0xffffc30e
0xffffc30e: 0 80 108 162 197 26 0 0
0xffffc316: 0 0 0 0 0 0
So, it's clear that the malware wants to connect to 108.162.197.26 through 80 port.
Malware then tries to get the ip of host machine by executing getsockname.
It then reads "/proc/net/route" here to parse network interface and uses ioctl syscall to get its MAC address (SIOCGIFHWADDR).
Later, It forks itself and quit.
The forked process also tries to connect somewhere(89.238.150.154) but it cannot. I don't know why.
root@lisa:~/blog# tcpdump -v -X dst 89.238.150.154
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
18:52:50.373950 IP (tos 0x0, ttl 64, id 46732, offset 0, flags [DF], proto TCP (6), length 60)
95.85.48.36.51746 > 89.238.150.154.5: Flags [S], cksum 0x8030 (incorrect -> 0x9be6), seq 3399773995, win 14600, options [mss 1460,sackOK,TS val 113698896 ecr 0,nop,wscale 8], length 0
0x0000: 4500 003c b68c 4000 4006 042e 5f55 3024 E..<..@.@..._U0$
0x0010: 59ee 969a ca22 0005 caa4 6f2b 0000 0000 Y...."....o+....
0x0020: a002 3908 8030 0000 0204 05b4 0402 080a ..9..0..........
0x0030: 06c6 e850 0000 0000 0103 0308 ...P........
Anyway, this malware looks harmless now :)