getdents
115
Oh shit! Data have been stolen from my computer… I looked for malicious activity but found nothing suspicious. Could ya give me a hand and find the malware and how it’s hiding?
I love CTF challenges that push me out of my comfort zone and force me to learn new skills and tools.
Here we get a memory image that we want to analyze for malware and find the flag. Volatility is the “go-to” tool for such analysis, and there is a lot of information on the Web on how to use it. For example this tutorial is very detailed and helpful.
Volatility requires an OS “profile” to be able to analyze a memory dump, and the organizers, helpfully, included one (Ubuntu_4.15.0-72-generic_profile.zip
) in the memory dump archive. Let’s verify that it works:
1
2
3
4
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_banner
Volatility Foundation Volatility Framework 2.6
Linux version 4.15.0-72-generic (buildd@lcy01-amd64-026) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 (Ubuntu 4.15.0-72.81-generic 4.15.18)
We were told that there is a “malware” on the system, let’s start with listing the running processes:
1
2
3
4
5
6
7
8
9
10
11
12
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_pslist
Volatility Foundation Volatility Framework 2.6
Offset Name Pid PPid Uid Gid DTB Start Time
------------------ -------------------- --------------- --------------- --------------- ------ ------------------ ----------
...
0xffff8a9dacd12e80 gnome-terminal- 1724 1237 1000 1000 0x0000000014c3a000 2020-01-16 14:00:57 UTC+0000
0xffff8a9db6dc0000 bash 1733 1724 1000 1000 0x0000000014662000 2020-01-16 14:00:57 UTC+0000
0xffff8a9dcf5ec5c0 sudo 1750 1733 0 0 0x000000005388a000 2020-01-16 14:01:22 UTC+0000
0xffff8a9dcf5edd00 meterpreter 1751 1750 0 0 0x0000000014540000 2020-01-16 14:01:22 UTC+0000
...
0xffff8a9dc3c40000 sh 2964 1751 0 0 0x0000000076aec000 2020-01-16 14:02:57 UTC+0000
So there is a Meterpreter running, spawning a shell - that’s not suspicious at all! Let’s dump the memory image for the shell:
1
2
3
4
5
6
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_procdump -p 2964 -D ./m
Volatility Foundation Volatility Framework 2.6
Offset Name Pid Address Output File
------------------ -------------------- --------------- ------------------ -----------
0xffff8a9dc3c40000 sh 2964 0x000055f26b1a8000 ./m/sh.2964.0x55f26b1a8000
Quick examination of the shell image brings up execution of the insmod
command:
1
2
3
4
5
6
$ strings m/sh.2964.0x55f26b1a8000 | grep insmod
insmod /home/julien/Downloads/rkit.ko hide=rJ/1g5PA5amy176A64akjuq/jryOug== hide_pid=1751
insmod
insmod
/sbin/insmod
insmod
inserts a kernel module into the kernel at runtime, and the name like rkit.ko
does not inspire much confidence. Looks like a rootkit kernel module is being installed. Note the hide
parameter passed to it on command line - we will need it later…
Not surprisingly this module does not show up on the list of modules:
1
2
3
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_lsmod | grep rkit
Volatility Foundation Volatility Framework 2.6
Yet it does seem to load:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_dmesg
Volatility Foundation Volatility Framework 2.6
[0.0] Linux version 4.15.0-72-generic (buildd@lcy01-amd64-026) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 (Ubuntu 4.15.0-72.81-generic 4.15.18)
[0.0] Command line: BOOT_IMAGE=/boot/vmlinuz-4.15.0-72-generic root=UUID=aee60f3a-b82f-4d1a-92ca-98ab58c5f506 ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet
[0.0] KERNEL supported cpus:
[0.0] Intel GenuineIntel
[0.0] AMD AuthenticAMD
[0.0] Centaur CentaurHauls
[0.0] Disabled fast string operations
...
[168682653963.168] rkit: loading out-of-tree module taints kernel.
[168682697557.168] rkit: module verification failed: signature and/or required key missing - tainting kernel
If we use special commands to list the hidden modules and to check the module, it does show up:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_hidden_modules
Volatility Foundation Volatility Framework 2.6
Offset (V) Name
------------------ ----
0xffffffffc011b3e0 resetafter
0xffffffffc05a0300 qni
0xffffffffc0943080 rkit
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_check_modules
Volatility Foundation Volatility Framework 2.6
Module Address Core Address Init Address Module Name
------------------ ------------------ ------------------ ------------------------
0xffffffffc0943080 0xffffffffc0941000 0x0 rkit
Let’s dump the module contents and look inside:
1
2
3
4
$ volatility --plugins=. --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 -f memory.vmem linux_moddump -D ./m -b 0xffffffffc0943080
Volatility Foundation Volatility Framework 2.6
Wrote 4146551 bytes to rkit.0xffffffffc0943080.lkm
Analysis in Ghidra is complicated by the fact that addresses of some functions cannot be resolved without further digging, but we can make some educated observations:
- There is code in method
FUN_ffffffffc0a41300()
that seems to use a key of length0x3e
to XOR a piece of data:
1
2
3
4
5
6
7
8
...
do {
s__ffffffffc09433d8[lVar3 + 8] =
*(byte *)(lVar2 + (int)(uVar4 % 6)) ^ s_H_}_ffffffffc0943000[lVar3];
lVar3 = lVar3 + 1;
uVar4 = uVar4 + 0x2a;
} while (lVar3 != 0x3e);
...
- There is a piece of data that is exactly
0x3e
bytes in length in the dump that may be the key:
1
2
3
4
5
$ xxd -g 1 -s $((0x2420)) -l $((0x3e)) m/rkit.0xffffffffc0943080.lkm
00002420: e5 d1 a6 f8 c1 f0 d5 dd f9 e6 ca c6 db f4 f6 e1 ................
00002430: da d4 e7 d9 fd c7 a5 fc c8 c4 e4 de e3 a2 f7 c5 ................
00002440: fe a3 ff d0 c3 e0 ab c2 a7 d8 d7 e2 df eb dc aa ................
00002450: a1 a0 d3 cb a4 f1 fa c0 fb f5 d6 f3 ea e8 ..............
- Function
FUN_ffffffffc0a41575()
seems to unpack a base64-encoded string by callingFUN_ffffffffc0a413b0()
and then XOR it with the key by callingFUN_ffffffffc0a41300()
:
1
2
3
4
...
DAT_ffffffffc0a43428 = FUN_ffffffffc0a413b0(uVar1,uVar2,0xffffffffc0943420);
FUN_ffffffffc0a41300();
...
If we think back to the parameters to insmod
there was a base64-encoded string specified there. Let’s try and decode it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ python
Python 2.7.15+ (default, Nov 28 2018, 16:27:22)
[GCC 8.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import base64
>>> import binascii
>>> data = base64.b64decode("rJ/1g5PA5amy176A64akjuq/jryOug==")
>>> key = binascii.unhexlify("e5d1a6f8c1f0d5ddf9e6cac6dbf4f6e1dad4e7d9fdc7a5fcc8c4e4dee3a2f7c5fea3ffd0c3e0abc2a7d8d7e2dfebdcaaa1a0d3cba4f1fac0fbf5d6f3eae8")
>>> flag = ""
>>> for x in range(len(data)):
... flag += chr(ord(data[x]) ^ ord(key[x]))
...
>>> flag
'INS{R00tK1tF0rRo0kies}'
Bingo! The flag is INS{R00tK1tF0rRo0kies}
.