使用此开源工具执行 Linux 内存取证
通过 Volatility 了解应用程序、网络连接、内核模块、文件等的情况
计算机的操作系统和应用程序使用主内存(或 RAM)来执行各种任务。这种易失性内存包含有关正在运行的应用程序、网络连接、内核模块、打开文件以及几乎所有其他信息的大量信息,每次计算机重新启动时都会被清除。
内存取证是一种从内存中查找并提取有价值信息的方法。 Volatility 是一个开源工具,它使用插件来处理此类信息。然而,有一个问题:在处理这些信息之前,必须将物理内存转储到文件中,而 Volatility 不具备这种能力。
因此,本文分为两部分:
- 第一部分涉及获取物理内存并将其转储到文件中。
- 第二部分使用 Volatility 来读取和处理此内存转储中的信息。
我在本教程中使用了以下测试系统,但它适用于任何 Linux 发行版:
$ cat /etc/redhat-release
Red Hat Enterprise Linux release 8.3 (Ootpa)
$
$ uname -r
4.18.0-240.el8.x86_64
$
注意事项:第 1 部分涉及编译和加载内核模块。不用担心;这并不像听起来那么困难。一些指导方针:
- 按照步骤。
- 不要在生产系统或主计算机上尝试这些步骤中的任何一个。
- 始终使用测试虚拟机 (VM) 进行尝试,直到您可以轻松地使用这些工具并了解它们的工作原理。
安装所需的软件包
在开始之前,请安装必要的工具。如果您使用基于 Debian 的发行版,请使用等效的 apt-get 命令。这些软件包中的大多数都提供了编译代码所需的内核信息和工具:
$ yum install kernel-headers kernel-devel gcc elfutils-libelf-devel make git libdwarf-tools python2-devel.x86_64-y
第 1 部分:使用 LiME 获取内存并将其转储到文件
在开始分析内存之前,您需要一个可供使用的内存转储。在实际的取证事件中,这可能来自受损或被黑客入侵的系统。通常收集和存储此类信息是为了分析入侵是如何发生的及其影响。由于您可能没有可用的内存转储,因此您可以获取测试虚拟机的内存转储并使用它来执行内存取证。
Linux Memory Extractor (LiME) 是一种在 Linux 系统上获取内存的流行工具。通过以下方式获取 LiME:
$ git clone https://github.com/504ensicsLabs/LiME.git
$
$ cd LiME/src/
$
$ ls
deflate.c disk.c hash.c lime.h main.c Makefile Makefile.sample tcp.c
$
构建 LiME 内核模块
在 src
文件夹中运行 make
命令。这将创建一个扩展名为 .ko 的内核模块。理想情况下,lime.ko
文件将在 make
末尾使用 lime-
格式重命名:
$ make
make -C /lib/modules/4.18.0-240.el8.x86_64/build M="/root/LiME/src" modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
<< snip >>
make[1]: Leaving directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
strip --strip-unneeded lime.ko
mv lime.ko lime-4.18.0-240.el8.x86_64.ko
$
$
$ ls -l lime-4.18.0-240.el8.x86_64.ko
-rw-r--r--. 1 root root 25696 Apr 17 14:45 lime-4.18.0-240.el8.x86_64.ko
$
$ file lime-4.18.0-240.el8.x86_64.ko
lime-4.18.0-240.el8.x86_64.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=1d0b5cf932389000d960a7e6b57c428b8e46c9cf, not stripped
$
加载 LiME 内核模块
现在是时候加载内核模块来获取系统内存了。 insmod
命令帮助加载内核模块;加载后,该模块会读取系统上的主内存 (RAM),并将内存内容转储到命令行上的 path
目录中提供的文件中。另一个重要参数是format
;保持格式lime
,如下所示。插入内核模块后,使用 lsmod 命令验证其是否已加载:
$ lsmod | grep lime
$
$ insmod ./lime-4.18.0-240.el8.x86_64.ko "path=../RHEL8.3_64bit.mem format=lime"
$
$ lsmod | grep lime
lime 16384 0
$
您应该看到提供给 path
命令的文件已创建,并且文件大小(毫不奇怪)与系统上的物理内存大小 (RAM) 相同。获得内存转储后,您可以使用 rmmod 命令删除内核模块:
$
$ ls -l ~/LiME/RHEL8.3_64bit.mem
-r--r--r--. 1 root root 4294544480 Apr 17 14:47 /root/LiME/RHEL8.3_64bit.mem
$
$ du -sh ~/LiME/RHEL8.3_64bit.mem
4.0G /root/LiME/RHEL8.3_64bit.mem
$
$ free -m
total used free shared buff/cache available
Mem: 3736 220 366 8 3149 3259
Swap: 4059 8 4051
$
$ rmmod lime
$
$ lsmod | grep lime
$
内存转储中有什么?
此转储文件只是原始数据,您可以使用下面的 file
命令看到。你无法手动理解它的意义。是的,那里有一些 ASCII 字符串,但您无法在编辑器中打开该文件并将其读出。 hexdump 输出显示最初的几个字节是 EmiL
;这是因为您的请求格式在上面的命令中是“lime”:
$ file ~/LiME/RHEL8.3_64bit.mem
/root/LiME/RHEL8.3_64bit.mem: data
$
$ hexdump -C ~/LiME/RHEL8.3_64bit.mem | head
00000000 45 4d 69 4c 01 00 00 00 00 10 00 00 00 00 00 00 |EMiL............|
00000010 ff fb 09 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 b8 fe 4c cd 21 44 00 32 20 00 00 2a 2a 2a 2a 2a |..L.!D.2 ..*****|
00000030 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************|
00000040 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 20 00 20 |************* . |
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000080 00 00 00 00 00 00 00 00 00 00 00 00 70 78 65 6c |............pxel|
00000090 69 6e 75 78 2e 30 00 00 00 00 00 00 00 00 00 00 |inux.0..........|
000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
$
第 2 部分:获取波动性并使用它来分析内存转储
现在您已经有了要分析的内存转储样本,请使用以下命令获取波动率软件。 Volatility 已在 Python 3 中重写,但本教程使用原始 Volatility 包,该包使用 Python 2。如果您想试验 Volatility 3,请从相应的 Git 存储库下载它,并在以下命令中使用 Python 3 而不是 Python 2 :
$ git clone https://github.com/volatilityfoundation/volatility.git
$
$ cd volatility/
$
$ ls
AUTHORS.txt contrib LEGAL.txt Makefile PKG-INFO pyinstaller.spec resources tools vol.py
CHANGELOG.txt CREDITS.txt LICENSE.txt MANIFEST.in pyinstaller README.txt setup.py volatility
$
Volatility 使用两个 Python 库来实现某些功能,因此请使用以下命令安装它们。否则,当您运行波动率工具时,您可能会看到一些导入错误;你可以忽略它们,除非你正在运行需要这些库的插件;在这种情况下,该工具将出错:
$ pip2 install pycrypto
$ pip2 install distorm3
列出 Volatility 的 Linux 配置文件
您要运行的第一个 Volatility 命令会列出可用的 Linux 配置文件。运行任何 Volatility 命令的主要入口点是 vol.py 脚本。使用 Python 2 解释器调用它并提供 --info
选项。要缩小输出范围,请查找以 Linux 开头的字符串。如您所见,列出的 Linux 配置文件并不多:
$ python2 vol.py --info | grep ^Linux
Volatility Foundation Volatility Framework 2.6.1
LinuxAMD64PagedMemory - Linux-specific AMD 64-bit address space.
$
构建您自己的 Linux 配置文件
Linux 发行版多种多样,并且针对各种架构而构建。这就是为什么配置文件至关重要——在提取信息之前,波动性必须了解从中获取内存转储的系统和架构。有 Volatility 命令可以查找此信息;然而,这种方法非常耗时。为了加快速度,请使用以下命令构建自定义 Linux 配置文件。
移至 Volatility 存储库中的 tools/linux
目录,然后运行 make
命令:
$ cd tools/linux/
$
$ pwd
/root/volatility/tools/linux
$
$ ls
kcore Makefile Makefile.enterprise module.c
$
$ make
make -C //lib/modules/4.18.0-240.el8.x86_64/build CONFIG_DEBUG_INFO=y M="/root/volatility/tools/linux" modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
<< snip >>
make[1]: Leaving directory '/usr/src/kernels/4.18.0-240.el8.x86_64'
$
您应该会看到一个新的 module.dwarf
文件。您还需要 /boot
目录中的 System.map
文件,因为它包含与当前运行的内核相关的所有符号:
$ ls
kcore Makefile Makefile.enterprise module.c module.dwarf
$
$ ls -l module.dwarf
-rw-r--r--. 1 root root 3987904 Apr 17 15:17 module.dwarf
$
$ ls -l /boot/System.map-4.18.0-240.el8.x86_64
-rw-------. 1 root root 4032815 Sep 23 2020 /boot/System.map-4.18.0-240.el8.x86_64
$
$
要创建自定义配置文件,请返回 Volatility 目录并运行以下命令。第一个参数提供自定义 .zip 以及您选择的文件名。我在名称中使用了操作系统和内核版本。下一个参数是上面创建的 module.dwarf 文件,最后一个参数是 /boot 目录中的 System.map 文件:
$
$ cd volatility/
$
$ zip volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip tools/linux/module.dwarf /boot/System.map-4.18.0-240.el8.x86_64
adding: tools/linux/module.dwarf (deflated 91%)
adding: boot/System.map-4.18.0-240.el8.x86_64 (deflated 79%)
$
您的自定义配置文件现已准备就绪,因此请验证 .zip 文件是在上面给出的位置创建的。如果您想知道 Volatility 是否检测到此自定义配置文件,请再次运行 --info
命令。这次,您应该看到下面列出的新配置文件:
$
$ ls -l volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip
-rw-r--r--. 1 root root 1190360 Apr 17 15:20 volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip
$
$
$ python2 vol.py --info | grep Redhat
Volatility Foundation Volatility Framework 2.6.1
LinuxRedhat8_3_4_18_0-240x64 - A Profile for Linux Redhat8.3_4.18.0-240 x64
$
$
开始使用波动性
现在您已准备好进行一些实际的内存取证。请记住,Volatility 由自定义插件组成,您可以针对内存转储运行这些插件以获取信息。该命令的一般格式为:
python2 vol.py -f <memory-dump-file-taken-by-Lime> <plugin-name> --profile=<name-of-our-custom-profile>
有了这些信息,运行 linux_banner 插件来查看是否可以从内存转储中识别正确的发行版信息:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_banner --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
Linux version 4.18.0-240.el8.x86_64 (mockbuild@vm09.test.com) (gcc version 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC)) #1 SMP Wed Sep 23 05:13:10 EDT 2020
$
查找 Linux 插件
效果很好,所以现在您可能好奇如何查找所有 Linux 插件的所有名称。有一个简单的技巧:运行 --info
命令和 grep
来获取 linux_
字符串。有多种插件可用于不同的用途。以下是部分列表:
$ python2 vol.py --info | grep linux_
Volatility Foundation Volatility Framework 2.6.1
linux_apihooks - Checks for userland apihooks
linux_arp - Print the ARP table
linux_aslr_shift - Automatically detect the Linux ASLR shift
<< snip >>
linux_banner - Prints the Linux banner information
linux_vma_cache - Gather VMAs from the vm_area_struct cache
linux_volshell - Shell in the memory image
linux_yarascan - A shell in the Linux memory image
$
使用 linux_psaux 插件进行内存转储时,检查系统上正在运行哪些进程。请注意列表中的最后一个命令:它是您在转储之前运行的 insmod
命令:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_psaux --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
Pid Uid Gid Arguments
1 0 0 /usr/lib/systemd/systemd --switched-root --system --deserialize 18
2 0 0 [kthreadd]
3 0 0 [rcu_gp]
4 0 0 [rcu_par_gp]
861 0 0 /usr/libexec/platform-python -Es /usr/sbin/tuned -l -P
869 0 0 /usr/bin/rhsmcertd
875 0 0 /usr/libexec/sssd/sssd_be --domain implicit_files --uid 0 --gid 0 --logger=files
878 0 0 /usr/libexec/sssd/sssd_nss --uid 0 --gid 0 --logger=files
<<< snip >>>
11064 89 89 qmgr -l -t unix -u
227148 0 0 [kworker/0:0]
227298 0 0 -bash
227374 0 0 [kworker/u2:1]
227375 0 0 [kworker/0:2]
227884 0 0 [kworker/0:3]
228573 0 0 insmod ./lime-4.18.0-240.el8.x86_64.ko path=../RHEL8.3_64bit.mem format=lime
228576 0 0
$
想了解系统的网络统计信息吗?运行linux_netstat插件来查找内存转储期间网络连接的状态:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_netstat --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
UNIX 18113 systemd/1 /run/systemd/private
UNIX 11411 systemd/1 /run/systemd/notify
UNIX 11413 systemd/1 /run/systemd/cgroups-agent
UNIX 11415 systemd/1
UNIX 11416 systemd/1
<< snip>>
$
接下来,使用 linux_mount 插件查看内存转储期间安装了哪些文件系统:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_mount --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec
cgroup /sys/fs/cgroup/pids cgroup rw,relatime,nosuid,nodev,noexec
systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime
sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime
/dev/mapper/rhel_kvm--03--guest11-root / xfs rw,relatime
tmpfs /dev/shm tmpfs rw,nosuid,nodev
selinuxfs /sys/fs/selinux selinuxfs rw,relatime
<< snip>>
cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,relatime,nosuid,nodev,noexec
cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,relatime,nosuid,nodev,noexec
bpf /sys/fs/bpf bpf rw,relatime,nosuid,nodev,noexec
cgroup /sys/fs/cgroup/memory cgroup ro,relatime,nosuid,nodev,noexec
cgroup /sys/fs/cgroup/cpuset cgroup rw,relatime,nosuid,nodev,noexec
mqueue /dev/mqueue mqueue rw,relatime
$
好奇加载了哪些内核模块? Volatility 也有一个插件,恰当地命名为 linux_lsmod:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_lsmod --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
ffffffffc0535040 lime 20480
ffffffffc0530540 binfmt_misc 20480
ffffffffc05e8040 sunrpc 479232
<< snip >>
ffffffffc04f9540 nfit 65536
ffffffffc0266280 dm_mirror 28672
ffffffffc025e040 dm_region_hash 20480
ffffffffc0258180 dm_log 20480
ffffffffc024bbc0 dm_mod 151552
$
想要查找存储在 Bash 历史记录中的用户运行的所有命令吗?运行 linux_bash 插件:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_bash --profile=LinuxRedhat8_3_4_18_0-240x64 -v
Volatility Foundation Volatility Framework 2.6.1
Pid Name Command Time Command
-------- -------------------- ------------------------------ -------
227221 bash 2021-04-17 18:38:24 UTC+0000 lsmod
227221 bash 2021-04-17 18:38:24 UTC+0000 rm -f .log
227221 bash 2021-04-17 18:38:24 UTC+0000 ls -l /etc/zzz
227221 bash 2021-04-17 18:38:24 UTC+0000 cat ~/.vimrc
227221 bash 2021-04-17 18:38:24 UTC+0000 ls
227221 bash 2021-04-17 18:38:24 UTC+0000 cat /proc/817/cwd
227221 bash 2021-04-17 18:38:24 UTC+0000 ls -l /proc/817/cwd
227221 bash 2021-04-17 18:38:24 UTC+0000 ls /proc/817/
<< snip >>
227298 bash 2021-04-17 18:40:30 UTC+0000 gcc prt.c
227298 bash 2021-04-17 18:40:30 UTC+0000 ls
227298 bash 2021-04-17 18:40:30 UTC+0000 ./a.out
227298 bash 2021-04-17 18:40:30 UTC+0000 vim prt.c
227298 bash 2021-04-17 18:40:30 UTC+0000 gcc prt.c
227298 bash 2021-04-17 18:40:30 UTC+0000 ./a.out
227298 bash 2021-04-17 18:40:30 UTC+0000 ls
$
想知道哪些进程打开了哪些文件?使用 linux_lsof 插件列出该信息:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_lsof --profile=LinuxRedhat8_3_4_18_0-240x64
Volatility Foundation Volatility Framework 2.6.1
Offset Name Pid FD Path
------------------ ------------------------------ -------- -------- ----
0xffff9c83fb1e9f40 rsyslogd 71194 0 /dev/null
0xffff9c83fb1e9f40 rsyslogd 71194 1 /dev/null
0xffff9c83fb1e9f40 rsyslogd 71194 2 /dev/null
0xffff9c83fb1e9f40 rsyslogd 71194 3 /dev/urandom
0xffff9c83fb1e9f40 rsyslogd 71194 4 socket:[83565]
0xffff9c83fb1e9f40 rsyslogd 71194 5 /var/log/messages
0xffff9c83fb1e9f40 rsyslogd 71194 6 anon_inode:[9063]
0xffff9c83fb1e9f40 rsyslogd 71194 7 /var/log/secure
<< snip >>
0xffff9c8365761f40 insmod 228573 0 /dev/pts/0
0xffff9c8365761f40 insmod 228573 1 /dev/pts/0
0xffff9c8365761f40 insmod 228573 2 /dev/pts/0
0xffff9c8365761f40 insmod 228573 3 /root/LiME/src/lime-4.18.0-240.el8.x86_64.ko
$
访问 Linux 插件脚本位置
通过读取内存转储并处理信息,您可以获得更多信息。如果您了解 Python 并且好奇如何处理这些信息,请转到存储所有插件的目录,选择您感兴趣的一个,然后看看 Volatility 如何获取此信息:
$ ls volatility/plugins/linux/
apihooks.py common.py kernel_opened_files.py malfind.py psaux.py
apihooks.pyc common.pyc kernel_opened_files.pyc malfind.pyc psaux.pyc
arp.py cpuinfo.py keyboard_notifiers.py mount_cache.py psenv.py
arp.pyc cpuinfo.pyc keyboard_notifiers.pyc mount_cache.pyc psenv.pyc
aslr_shift.py dentry_cache.py ld_env.py mount.py pslist_cache.py
aslr_shift.pyc dentry_cache.pyc ld_env.pyc mount.pyc pslist_cache.pyc
<< snip >>
check_syscall_arm.py __init__.py lsmod.py proc_maps.py tty_check.py
check_syscall_arm.pyc __init__.pyc lsmod.pyc proc_maps.pyc tty_check.pyc
check_syscall.py iomem.py lsof.py proc_maps_rb.py vma_cache.py
check_syscall.pyc iomem.pyc lsof.pyc proc_maps_rb.pyc vma_cache.pyc
$
$
我喜欢 Volatility 的原因之一是它提供了很多安全插件。这些信息很难手动获取:
linux_hidden_modules - Carves memory to find hidden kernel modules
linux_malfind - Looks for suspicious process mappings
linux_truecrypt_passphrase - Recovers cached Truecrypt passphrases
波动性还允许您在内存转储中打开 shell,因此您可以运行 shell 命令并获取相同的信息,而不是运行上面的所有命令:
$ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_volshell --profile=LinuxRedhat8_3_4_18_0-240x64 -v
Volatility Foundation Volatility Framework 2.6.1
Current context: process systemd, pid=1 DTB=0x1042dc000
Welcome to volshell! Current memory image is:
file:///root/LiME/RHEL8.3_64bit.mem
To get help, type 'hh()'
>>>
>>> sc()
Current context: process systemd, pid=1 DTB=0x1042dc000
>>>
下一步
内存取证是了解更多 Linux 内部结构的好方法。尝试 Volatility 的所有插件并详细研究它们的输出。然后考虑这些信息如何帮助您识别入侵或安全问题。深入研究插件的工作原理,甚至尝试改进它们。如果您没有找到适合您想做的事情的插件,请编写一个插件并将其提交给 Volatility,以便其他人也可以使用它。