Linux Malware Analysis
From Ggl's wiki
Contents |
Introduction
Tools
- qemu
- qemu image filesystem
- binutils
- gdb
- elfsh
- maybe (depends on the malware complexity) IDA Pro (on x86, 4.3 freeware version can be used)
- samhain
- AIDE
- md5sum
- sha1sum
- gnupg
other debugger (alternatives to gdb):
- linice
- the Dude
- kdb
- kgdb
- pice (private ice)
Analysis
Now you have your toolz armory, you can begin to identify the malware. I'll focus on userland malware so I'll use gdb with mammon_'s configuration file.
- First, do some hashes of it to identify the binary later:
md5sum malware
sha1sum malware
- After you need to have a sandbox environnement. Install qemu and the target OS on it (for this example, I choose to install a debian sarge).
- On debian :
apt-get install qemu apt-get install debbootstrap qemu-make-debian-root 1024 stable http://ftp.de.debian.org sandbox.qemu.img
- note: qemu-make-debian-root [-k] size-in-MiB distrib deburl image [files-to-copy-in-/root]
- Now, install samhain to monitor modification of the sandbox OS. You can also use AIDE to make a snapshot and to verify after.
apt-get install samhain
- What are the main locations to monitor ?
/etc/, everything in $PATH and everything in /lib, /usr/lib. Don't forget /dev and /proc.
- log onto the sandbox and begin to analyse the malware :
file malware strings malware
- If strings doesn't return anything interesting the binary is probably packed. If you have luck it's pack with a known packer/encrypter (upx, burneye, or shiva but shiva is difficult to analyse so no luck :p).
Session 1 - pbueno-06
- download debian 3.1r1 iso
- create qemu HD image :
qemu-img create -f qcow hd.img 1200M
- start qemu to install image :
qemu -boot -d -cdrom ../files/debian31r1-i386-binary-1.iso -hda hd.img
- <Enter> to start install
- Configure timezone
- Enter root password (testhostile)
- Create unprivileged user (greg/zorglub)
- Install base system
- Additionnal packages to install :
- vim
- screen
- samhain
- gdb
- binutils (strings, readelf, objdump)
- elfsh
- upx
- unzip
- hte
- hexdump
- strace
- ltrace
aide -i (init db in /var/lib/aide)
aide -C (check the files with rules defined in /etc/aide/aide.conf)
- no changes with unprivileged user
- _0x01. Are these files packed? If so, which packer?
$ readelf -h shelll [...] Entry point address: 0x8048e60 [...]
$ readelf -S shelll There are 35 section headers, starting at offset 0x5aa8: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4 [ 3] .note.SuSE NOTE 08048168 000168 000018 00 A 0 0 4 [ 4] .hash HASH 08048180 000180 000184 04 A 5 0 4 [ 5] .dynsym DYNSYM 08048304 000304 0003a0 10 A 6 1 4 [ 6] .dynstr STRTAB 080486a4 0006a4 0001cc 00 A 0 0 1 [ 7] .gnu.version VERSYM 08048870 000870 000074 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 080488e4 0008e4 000030 00 A 6 1 4 [ 9] .rel.dyn REL 08048914 000914 000008 08 A 5 0 4 [10] .rel.plt REL 0804891c 00091c 0001b0 08 A 5 12 4 [11] .init PROGBITS 08048acc 000acc 000017 00 AX 0 0 4 [12] .plt PROGBITS 08048ae4 000ae4 000370 04 AX 0 0 4 [13] .text PROGBITS 08048e60 000e60 003184 00 AX 0 0 16 [14] .fini PROGBITS 0804bfe4 003fe4 00001a 00 AX 0 0 4 [15] .rodata PROGBITS 0804c000 004000 000e83 00 A 0 0 4 [16] .eh_frame PROGBITS 0804ce84 004e84 000004 00 A 0 0 4 [17] .ctors PROGBITS 0804d000 005000 000008 00 WA 0 0 4 [18] .dtors PROGBITS 0804d008 005008 000008 00 WA 0 0 4 [19] .jcr PROGBITS 0804d010 005010 000004 00 WA 0 0 4 [20] .dynamic DYNAMIC 0804d014 005014 0000c8 08 WA 6 0 4 [21] .got PROGBITS 0804d0dc 0050dc 000004 04 WA 0 0 4 [22] .got.plt PROGBITS 0804d0e0 0050e0 0000e4 04 WA 0 0 4 [23] .data PROGBITS 0804d1e0 0051e0 000100 00 WA 0 0 32 [24] .bss NOBITS 0804d2e0 0052e0 000690 00 WA 0 0 32 [25] .comment PROGBITS 00000000 0052e0 000173 00 0 0 1 [26] .debug_aranges PROGBITS 00000000 005458 000078 00 0 0 8 [27] .debug_pubnames PROGBITS 00000000 0054d0 000025 00 0 0 1 [28] .debug_info PROGBITS 00000000 0054f5 0001fa 00 0 0 1 [29] .debug_abbrev PROGBITS 00000000 0056ef 000076 00 0 0 1 [30] .debug_line PROGBITS 00000000 005765 00017c 00 0 0 1 [31] .debug_str PROGBITS 00000000 0058e1 000095 01 MS 0 0 1 [32] .shstrtab STRTAB 00000000 005976 000132 00 0 0 1 [33] .symtab SYMTAB 00000000 006020 000bd0 10 34 61 4 [34] .strtab STRTAB 00000000 006bf0 0007ee 00 0 0 1
Sections are all explicitly present and readable. Entry point is at the beginning of .text section. Thus,apt shelll is not packed. cmd.zip contains "cmd.gif" which is an "exported SGML document text". Actually it's a PHP script fully readable. Not packed.
- _0x02. (a)- Without running the applications, identify what the malware can/will do, then [...]
shelll
strings shows some irc client commands :
NOTICE %s :Receiving file. NOTICE %s :Saved as %s NOTICE %s :Spoofs: %d.%d.%d.%d NOTICE %s :Spoofs: %d.%d.%d.%d - %d.%d.%d.%d NOTICE %s :Kaiten wa goraku NOTICE %s :NICK <nick> NOTICE %s :Nick cannot be larger than 9 characters.
shelll seems to be an irc bot.
The elf binary was compiled with full symbols and debug symbols. readelf -s shows network related symbols :
in .dynsym
14: 00000000 120 FUNC GLOBAL DEFAULT UND accept@GLIBC_2.0 (2) 16: 00000000 57 FUNC GLOBAL DEFAULT UND listen@GLIBC_2.0 (2) 19: 00000000 120 FUNC GLOBAL DEFAULT UND sendto@GLIBC_2.0 (2) 20: 00000000 57 FUNC GLOBAL DEFAULT UND setsockopt@GLIBC_2.0 (2) 28: 00000000 39 FUNC GLOBAL DEFAULT UND inet_addr@GLIBC_2.0 (2) 29: 00000000 836 FUNC GLOBAL DEFAULT UND inet_network@GLIBC_2.0 (2) 37: 00000000 407 FUNC GLOBAL DEFAULT UND gethostbyname@GLIBC_2.0 (2) 43: 00000000 14 FUNC GLOBAL DEFAULT UND htons@GLIBC_2.0 (2) 53: 00000000 57 FUNC GLOBAL DEFAULT UND socket@GLIBC_2.0 (2)
in .symtab
58: 00000000 0 FILE LOCAL DEFAULT ABS kaiten.c
tells that the malware might be a kaiten variant. Kaiten is a IRC DDoS Bot.
cmd.gif
cmd.gif is a PHP script used to execute command of a http server. It's part of a well known tool called "Defacing Tool Pro" (DTool).
- _0x02. (b)- run the applications and identify additional details evident when the applications are run.
$ ./shelll $
What happened ?
'ps' aux doesn't show shelll processes. 'pstree -p' or only 'ps' does :
$ pstree -p [...] |--shelll(1019) [...]
'killall shelll' kills all shelll processes.
Actually 'ps aux' show the command name '-bash' but in /proc/<pid>/ we see the real name of the process:
$ head -n 1 /proc/1019/status Name: shelll
and also /proc/1019/exe -> /mnt/hdb/shelll
Two ways to trace the execution of shelll : using strace/ltrace or using gdb.
ltrace shows an overview of library functions calls :
$ ltrace ./shelll __libc_start_main(0x804b947, 1, 0xbffffb94, 0x804bf60, 0x804bef0 <unfinished ...> fork() = 1045 exit(o <unfinished ...> +++ exited (status 0) +++ $
So, shelll starts a child with PID 1045.
$ gdb ./shelll (gdb) b fork Function "fork" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 2 (fork) pending. (gdb) r Starting program: /mnt/hdb/pbueno-ma6/shelll [...] Breakpoint 3, 0x400c2df6 in fork () from /lib/libc.so.6 (gdb) s Single stepping until exit from function fork, which has no line number information. 0x0804b96d in main () (gdb) p $eax $1 = 1019
It's the return of the fork(), so it represents the PID of the child.
(gdb) s Single stepping untiel exit form function main, which has no line number information. Program exited normally. (gdb)
fork() was the last function to be called.
(gdb) attach 1019 [...] (gdb) 0x400e9a98 in poll () from /lib/libc.so.6
In parallel, lsof shows shelll connecting to the fake dns server and try to connect to irc.ircnet.net : $ lsof | grep shelll [...] shelll 1019 root 4u IPv4 414041 UDP 10.0.2.15:4007->10.0.2.3:domain shelll 1019 root 3u sock 0,0 414039 can't identify protocol $ ls -l /proc/1019/fd shows to sockets : 3 -> socket:[414039] 4 -> socket:[414041]
tcpdump tells that shelll try to connect to irc.ircnet.net So I add :
127.0.0.1 irc.ircnet.net
to /etc/hosts
and start netcat to listen on port 6667/tcp
- _0x05. Why didn't the 'shelll' or the 'cmd' applications show up at the ps aux ?
shelll was started with '-bash' as cmdline so ps aux display this name. pstree shows the real binary name (like /proc/<pid>/status Name field).
cmd is a php script so it is interpreted by apache from http request.
- _0x06. Do you have any clues of how the machine was compromised ?
In secure.txt, there are a lot of Failed ssh authentication attempts :
Feb 13 10:28:58 linux sshd[1446]: Failed password for invalid user mark from xxx.60.116.10 port 44242 ssh2 Feb 13 10:28:58 linux sshd[1446]: Failed password for invalid user mark from xxx.60.116.10 port 44242 ssh2 Feb 13 10:28:58 linux sshd[1446]: Received disconnect from xxx.60.116.10: 11: Bye Bye Feb 13 10:28:59 linux sshd[1446]: Invalid user mark from xxx.60.116.10 Feb 13 10:29:00 linux sshd[1446]: reverse mapping checking getaddrinfo for 10-116-60-xxx.serverpronto.com failed - POSSIBLE BREAKIN ATTEMPT!
At the end of the file, the backup user authenticates :
Feb 13 11:05:50 linux sshd[1446]: Accepted password for backup from xxx.60.116.1
0 port 20577 ssh2
And after used sftp :
Feb 13 19:35:23 linux sshd[1446]: subsystem request for sftp Feb 13 19:51:44 linux sshd[1446]: Accepted password for backup from xxx.60.116.10 port 3635 ssh2
It may be a ssh brute-force attack.
What is backup's password :
$ /usr/sbin/john backup Loaded 1 password (OpenBSD Blowfish [32/32]) backup (backup) guesses: 1 time: 0:00:00:00 100% (1) c/s: 1.00 trying: backup
Ok so backup's password was trivial.
I guess that the host was compromised by this ssh brute-force attack and the backup user with password 'backup' was used to upload malwares.

