攻击取证

取证人员通常是在发现缺陷事件后,或者是为了确定是否发生了“事件”而被叫来的人。他们通常需要受影响机器的 RAM (笔者注: Random Access Memory,随机存取存储器)的快照,以便捕获仅临时存储在内存中的加密密钥或其他信息。幸运的是,一个有才华的开发团队已经创建了一个完整的 Python 框架 Volatility ,它适合这个任务,被称为高级内存取证框架。事件响应者、取证人员和恶意软件分析师也可以将 Volatility 用于其他各种任务,包括检查内核对象、检查和转储进程信息等。

(笔者注: Volatility 是一款开源内存取证框架,能够对导出的内存镜像进行分析,通过获取内核数据结构,使用插件获取内存的详细情况以及系统的运行状态。特点:开源: Python 编写,易于和基于 python 的主机防御框架集成;支持多平台:Windows,Mac,Linux 全支持;易于扩展:通过插件来扩展 Volatility 的分析能力)

尽管 Volatility 是防御方的软件,但任何足够强大的工具都可以用于进攻或防守。我们将使用 Volatility 对目标用户执行侦察,并编写自己的攻击性插件来搜索在虚拟机(VM)上运行的防御能力较弱的进程。

假设你潜入一台机器,发现用户使用虚拟机进行敏感工作。很有可能用户还制作了虚拟机的快照作为安全措施以防出现任何错误问题,这更是好机会。我们将使用 Volatility 内存分析框架来分析快照,以了解 VM 是如何工作的以及哪些进程正在运行。我们还将调查可能存在的漏洞,以便进一步利用这些漏洞进行攻击。

我们开始吧!

安装

Volatility 已经存在了好几年,刚刚经历了一次彻底的改写。现在不仅代码库建立在 Python 3 上,而且整个框架都经过了重构,因此组件也是独立的;运行插件所需的所有情况都是自包含的。

让我们创建一个虚拟环境来处理 Volatility 。对于本例,我们在 Windows 机器上的 PowerShell 终端使用 Python 3。如果您也在 Windows 机器上工作,请确保安装了 git 。你可以在 https://git-scm.com/downloads/ 下载。

1
2
3
4
5
6
7
[1] PS> python3 -m venv vol3
PS> vol3/Scripts/Activate.ps1
PS> cd vol3/
[2] PS> git clone https://github.com/volatilityfoundation/volatility3.git
PS> cd volatility3/
PS> python setup.py install
[3] PS> pip install pycryptodome

首先,我们创建一个名为 vol3 的新虚拟环境并将其激活[1]。接下来,我们进入虚拟环境目录并克隆 Volatility 3 的GitHub 存储库[2],将其安装到虚拟环境中,最后安装 pycryptodome [3],稍后我们将需要它。

要查看 Volatility 提供的插件和选项列表,请在 Windows 上使用以下命令:

1
PS> vol --help

在 Linux 或 Mac 上,使用虚拟环境中的 Python 可执行文件,如下所示:

1
$> python vol.py --help

在本章中,我们将从命令行使用 Volatility ,但是您可能会遇到各种各样的框架。例如,请参阅 Volatility 的 Volumetric 项目,一个免费的基于 web 的 Volatility GUI( https://github.com/volatilityfoundation/volumetric/ )。您可以深入到 Volumetric 项目中的代码示例,看看如何在自己的程序中使用 Volatility 。此外,您还可以使用 volshell 接口,它为您提供了对 Volatility 框架的访问,并作为一个普通的交互式 Python shell 工作。

在下面的示例中,我们将使用 Volatility 命令行。为了节省空间,将输出编辑为仅显示已讨论的输出,因此请注意,您的输出将有更多的行和列。

现在让我们深入研究一些代码,看看框架内部:

1
2
3
4
5
6
7
8
9
PS> cd volatility/framework/plugins/windows/
PS> ls
_init__.py driverscan.py memmap.py psscan.py vadinfo.py
bigpools.py filescan.py modscan.py pstree.py vadyarascan.py
cachedump.py handles.py modules.py registry/ verinfo.py
callbacks.py hashdump.py mutantscan.py ssdt.py virtmap.py
cmdline.py info.py netscan.py strings.py
dlllist.py lsadump.py poolscanner.py svcscan.py
driverirp.py malfind.py pslist.py symlinkscan.py

这个清单显示了 Windows plugin 目录中的 Python 文件。我们强烈建议您花一些时间查看这些文件中的代码。您将看到形成 Volatility 插件结构的循环模式。这将有助于你理解这个框架,但更重要的是,它会让你了解一个防御者的心态和意图。通过了解防御者的能力以及他们是如何完成目标的,你将使自己成为一个更有能力的黑客,并更好地了解如何保护自己不被发现。

现在我们已经准备好了分析框架,我们需要一些内存图像来分析。最简单的方法是拍摄自己的 Windows 10 虚拟机的快照。

首先,打开 Windows 虚拟机并启动一些进程(例如,记事本、计算器和浏览器);我们将检查内存并跟踪这些进程是如何启动的。然后,使用您选择的虚拟机监控程序拍摄快照。在管理程序存储虚拟机的目录中,您将看到名称以 .vmem 或 .mem 结尾的新快照文件。让我们开始侦察吧!

请注意,您还可以在网上找到许多内存图像。本章中我们看到的一个图像是 PassMark Software 提供的([https://www .osforensics.com/tools/volatility-workbench.html/](https://www .osforensics.com/tools/volatility-workbench.html/))。Volatility Foundation 网站也有一些图像可以使用(https://github.com/volatilityfoundation/volatility/wiki/Memory-Samples/

通用侦察

让我们大致了解一下我们正在分析的机器。 windows.info 插件可以显示内存示例的操作系统和内核信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[1] PS>vol -f WinDev2007Eval-Snapshot4.vmem windows.info
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
Variable Value
Kernel Base 0xf80067a18000
DTB 0x1aa000
primary 0 WindowsIntel32e
memory_layer 1 FileLayer
KdVersionBlock 0xf800686272f0
Major/Minor 15.19041
MachineType 34404
KeNumberProcessors 1
SystemTime 2020-09-04 00:53:46
NtProductType NtProductWinNt
NtMajorVersion 10
NtMinorVersion 0
PE MajorOperatingSystemVersion 10
PE MinorOperatingSystemVersion 0
PE Machine 34404

我们使用 -f 选项和要使用的 Windows 插件, windows.info ,指定快照文件名[1]。 Volatility 读取并分析内存文件,并输出有关此 Windows 计算机的通识信息。我们可以看到,我们正在处理一个 Windows 10.0 虚拟机 ,它有一个单处理器和一个内存层。

在查看插件代码时,您可能会发现在内存映像文件上尝试几个插件很有实践教育意义。花时间阅读代码并查看相应的输出将向您展示代码应该如何工作以及防御者大体上的心态。

接下来是 registry.printkey 插件,可以打印注册表中某个键的值。注册表有大量有价值的信息, Volatility 提供了一种找到我们想要的所有值的方法。在这里,我们寻找已安装的服务。键 /ControlSet001/Services 显示 Service Control Manager (服务控制管理器)数据库,其中列出了所有已安装的服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.registry.printkey --key 'ControlSet001\Services'
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
... Key Name Data Volatile
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services .NET CLR Data False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services Appinfo False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services applockerfltr False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services AtomicAlarmClock False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services Beep False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services fastfat False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services MozillaMaintenance False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services NTDS False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services Ntfs False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services ShellHWDetection False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services SQLWriter False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services Tcpip False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services Tcpip6 False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services terminpt False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services W32Time False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services WaaSMedicSvc False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services WacomPen False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services Winsock False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services WinSock2 False
\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services WINUSB False

此输出显示计算机上已安装服务的列表(因空间原因而缩略了)。

用户侦察

现在让我们对虚拟机的用户进行一些侦察。 cmdline 插件列出了创建快照时每个进程正在运行的命令行参数。这些进程给我们一个关于用户行为和意图的提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.cmdline
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
PID Process Args
72 Registry Required memory at 0x20 is not valid (process exited?)
340 smss.exe Required memory at 0xa5f1873020 is inaccessible (swapped)
564 lsass.exe C:\Windows\system32\lsass.exe
624 winlogon.exe winlogon.exe
2160 MsMpEng.exe "C:\ProgramData\Microsoft\Windows Defender\platform\4.18.2008.9-0\
MsMpEng.exe"
4732 explorer.exe C:\Windows\Explorer.EXE
4848 svchost.exe C:\Windows\system32\svchost.exe -k ClipboardSvcGroup -p
4920 dllhost.exe C:\Windows\system32\DllHost.exe /Processid:{AB8902B4-09CA-4BB6-B78D-
A8F59079A8D5}
5084 StartMenuExper "C:\Windows\SystemApps\Microsoft.Windows. . ."
5388 MicrosoftEdge. "C:\Windows\SystemApps\Microsoft.MicrosoftEdge_. . ."
6452 OneDrive.exe "C:\Users\Administrator\AppData\Local\Microsoft\OneDrive\OneDrive.exe"
/background
6484 FreeDesktopClo "C:\Program Files\Free Desktop Clock\FreeDesktopClock.exe"
7092 cmd.exe "C:\Windows\system32\cmd.exe" [1]
3312 notepad.exe notepad [2]
3824 powershell.exe "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
6448 Calculator.exe "C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_. . ."
6684 firefox.exe "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
6432 PowerToys.exe "C:\Program Files\PowerToys\PowerToys.exe"
7124 nc64.exe Required memory at 0x2d7020 is inaccessible (swapped)
3324 smartscreen.ex C:\Windows\System32\smartscreen.exe -Embedding
4768 ipconfig.exe Required memory at 0x840308e020 is not valid (process exited?)

该列表显示进程 ID 、进程名和命令行以及启动进程的参数。您可以看到,大多数进程都是由系统本身启动的,很可能是在引导时启动的。 cmd.exe [1]和 notepad.exe [2]进程是用户启动进程的典型。

让我们用 pslist 插件更深入地研究正在运行的进程,它列出了拍摄快照时正在运行的进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.pslist
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64
4 0 System 0xa50bb3e6d040 129 - N/A False
72 4 Registry 0xa50bb3fbd080 4 - N/A False
6452 4732 OneDrive.exe 0xa50bb4d62080 25 - 1 True
6484 4732 FreeDesktopClo 0xa50bbb847300 1 - 1 False
6212 556 SgrmBroker.exe 0xa50bbb832080 6 - 0 False
1636 556 svchost.exe 0xa50bbadbe340 8 - 0 False
7092 4732 cmd.exe 0xa50bbbc4d080 1 - 1 False
3312 7092 notepad.exe 0xa50bbb69a080 3 - 1 False
3824 4732 powershell.exe 0xa50bbb92d080 11 - 1 False
6448 704 Calculator.exe 0xa50bb4d0d0c0 21 - 1 False
4036 6684 firefox.exe 0xa50bbb178080 0 - 1 True
6432 4732 PowerToys.exe 0xa50bb4d5a2c0 14 - 1 False
4052 4700 PowerLauncher. 0xa50bb7fd3080 16 - 1 False
5340 6432 Microsoft.Powe 0xa50bb736f080 15 - 1 False
8564 4732 python-3.8.6-a 0xa50bb7bc2080 1 - 1 True
7124 7092 nc64.exe 0xa50bbab89080 1 - 1 False
3324 704 smartscreen.ex 0xa50bb4d6a080 7 - 1 False
7364 4732 cmd.exe 0xa50bbd8a8080 1 - 1 False
8916 2136 cmd.exe 0xa50bb78d9080 0 - 0 False
4768 8916 ipconfig.exe 0xa50bba7bd080 0 - 0 False

这里我们看到了实际的进程及其内存偏移量。为了节省空间,所以省略了一些列。下面列出了几个有趣的进程,包括我们在 cmdline 插件的输出中看到的 cmd 和 notepad 进程。

最好将进程看作一个层次结构,这样我们就可以知道是什么进程启动了其他进程。为此,我们将使用到 pstree 插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.pstree
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64
4 0 System 0xa50bba7bd080 129 N/A False
* 556 492 services.exe 0xa50bba7bd080 8 0 False
** 2176 556 wlms.exe 0xa50bba7bd080 2 0 False
** 1796 556 svchost.exe 0xa50bba7bd080 13 0 False
** 776 556 svchost.exe 0xa50bba7bd080 15 0 False
** 8 556 svchost.exe 0xa50bba7bd080 18 0 False
*** 4556 8 ctfmon.exe 0xa50bba7bd080 10 1 False
*** 5388 704 MicrosoftEdge. 0xa50bba7bd080 35 1 False
*** 6448 704 Calculator.exe 0xa50bba7bd080 21 1 False
*** 3324 704 smartscreen.ex 0xa50bba7bd080 7 1 False
** 2136 556 vmtoolsd.exe 0xa50bba7bd080 11 0 False
*** 8916 2136 cmd.exe 0xa50bba7bd080 0 0 False
**** 4768 8916 ipconfig.exe 0xa50bba7bd080 0 0 False
* 4704 624 userinit.exe 0xa50bba7bd080 0 1 False
** 4732 4704 explorer.exe 0xa50bba7bd080 92 1 False
*** 6432 4732 PowerToys.exe 0xa50bba7bd080 14 1 False
**** 5340 6432 Microsoft.Powe 0xa50bba7bd080 15 1 False
*** 7364 4732 cmd.exe 0xa50bba7bd080 1 - False
**** 2464 7364 conhost.exe 0xa50bba7bd080 4 1 False
*** 7092 4732 cmd.exe 0xa50bba7bd080 1 - False
**** 3312 7092 notepad.exe 0xa50bba7bd080 3 1 False
**** 7124 7092 nc64.exe 0xa50bba7bd080 1 1 False
*** 8564 4732 python-3.8.6-a 0xa50bba7bd080 1 1 True
**** 1036 8564 python-3.8.6-a 0xa50bba7bd080 5 1 True

现在我们得到了一个更清晰的画面。每行中的星号表示进程的父子关系。例如, userinit 进程(PID 4704)生成了 explorer.exe 进程。同样, explorer.exe 进程(PID 4732)启动 cmd.exe 进程(PID 7092)。从这个进程中,用户启动了 notepad.exe 和另一个名为 nc64.exe 的进程。

现在让我们用 hashdump 插件检查密码:

1
2
3
4
5
6
7
8
9
10
11
PS> vol -f WinDev2007Eval-7d959ee5.vmem windows.hashdump
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
User rid lmhash nthash
Administrator 500 aad3bXXXXXXaad3bXXXXXX fc6eb57eXXXXXXXXXXX657878
Guest 501 aad3bXXXXXXaad3bXXXXXX 1d6cfe0dXXXXXXXXXXXc089c0
DefaultAccount 503 aad3bXXXXXXaad3bXXXXXX 1d6cfe0dXXXXXXXXXXXc089c0
WDAGUtilityAccount 504 aad3bXXXXXXaad3bXXXXXX ed66436aXXXXXXXXXXX1bb50f
User 1001 aad3bXXXXXXaad3bXXXXXX 31d6cfe0XXXXXXXXXXXc089c0
tim 1002 aad3bXXXXXXaad3bXXXXXX afc6eb57XXXXXXXXXXX657878
admin 1003 aad3bXXXXXXaad3bXXXXXX afc6eb57XXXXXXXXXXX657878

输出显示帐户用户名及其密码的 LM 和 NT 散列。攻击者的一个共同目标是在入侵后恢复 Windows 机器上的密码哈希值。这些散列可以脱机破解以试图恢复目标的密码,也可以用于传递散列攻击以获得对其他网络资源的访问。无论目标用户是只在虚拟机上执行高风险操作的偏执用户,还是试图将其用户的某些活动包含到虚拟机的企业用户,查看系统上的虚拟机或快照都是在获得对主机硬件的访问权限后尝试恢复这些散列的最佳时机。

Volatility 使得这一复苏过程极为容易。

我们已经混淆了输出中的散列。您可以使用自己的输出作为散列破解工具的输入,以找到进入 VM 的方法。有几个在线哈希破解网站;或者,你可以在你的 kali 机上使用 John the Ripper 。

(笔者注: John the Ripper 是一个快速的密码破解工具,用于在已知密文的情况下尝试破解出明文,支持目前大多数的加密算法,如 DES、MD4、MD5 等。它支持多种不同类型的系统架构,包括 Unix、Linux、Windows、DOS 模式、 BeOS 和 OpenVMS ,主要目的是破解不够牢固的 Unix/Linux 系统密码。除了在各种 Unix 系统上最常见的几种密码哈希类型之外,它还支持 Windows LM 散列,以及社区增强版本中的许多其他哈希和密码。它是一款开源软件。Kali中自带John。)

漏洞侦察

现在,让我们使用 Volatility 来发现目标 VM 是否存在可以利用的漏洞。 malfind 插件检查可能包含注入代码的进程内存范围。 Potential 是这里的关键词——插件正在寻找具有读、写和执行权限的内存区域。调查这些进程是值得的,因为它们可能帮助我们能够利用一些已经可用的恶意软件。或者,我们可以用自己的恶意软件覆盖这些区域。

1
2
3
4
5
6
7
8
9
10
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.malfind
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
PID Process Start VPN End VPN Tag Protection CommitCharge
1336 timeserv.exe 0x660000 0x660fff VadS PAGE_EXECUTE_READWRITE 1
2160 MsMpEng.exe 0x16301690000 0x1630179cfff VadS PAGE_EXECUTE_READWRITE 269
2160 MsMpEng.exe 0x16303090000 0x1630318ffff VadS PAGE_EXECUTE_READWRITE 256
2160 MsMpEng.exe 0x16304a00000 0x16304bfffff VadS PAGE_EXECUTE_READWRITE 512
6484 FreeDesktopClo 0x2320000 0x2320fff VadS PAGE_EXECUTE_READWRITE 1
5340 Microsoft.Powe 0x2c2502c0000 0x2c2502cffff VadS PAGE_EXECUTE_READWRITE 15

我们遇到了一些潜在的问题。 timeserv.exe 进程(PID 1336)是 FreeDesktopClock (PID 6484)免费软件的一部分。只要他们被安装到 C:\Program Files 目录下,这些不一定是问题。否则,该进程可能是伪装成时钟的恶意软件。

使用搜索引擎,你会发现进程 MsMpEng.exe (PID 2160)是一个防恶意软件服务。尽管这些进程包含可写和可执行的内存区域,但它们看起来并不危险。也许我们可以通过在这些内存区域中编写 shellcode 来使这些进程变得危险,所以值得注意它们。

netscan 插件提供了快照时计算机拥有的所有网络连接的列表,如下所示。任何看起来可疑的东西我们都可以用来发动袭击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.netscan
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
Offset Proto LocalAddr LocalPort ForeignAdd ForeignPort State PID Owner
0xa50bb7a13d90 TCPv4 0.0.0.0 4444 0.0.0.0 0 LISTENING 7124 nc64.exe [1]
0xa50bb9f4c310 TCPv4 0.0.0.0 7680 0.0.0.0 0 LISTENING 1776 svchost.exe
0xa50bb9f615c0 TCPv4 0.0.0.0 49664 0.0.0.0 0 LISTENING 564 lsass.exe
0xa50bb9f62190 TCPv4 0.0.0.0 49665 0.0.0.0 0 LISTENING 492 wininit.exe
0xa50bbaa80b20 TCPv4 192.168.28.128 50948 23.40.62.19 80 CLOSED [2]
w0xa50bbabd2010 TCPv4 192.168.28.128 50954 23.193.33.57 443 CLOSED
0xa50bbad8d010 TCPv4 192.168.28.128 50953 99.84.222.93 443 CLOSED
0xa50bbaef3010 TCPv4 192.168.28.128 50959 23.193.33.57 443 CLOSED
0xa50bbaff7010 TCPv4 192.168.28.128 50950 52.179.224.121 443 CLOSED
0xa50bbbd240a0 TCPv4 192.168.28.128 139 0.0.0.0 0 LISTENING

我们看到一些来自本地机器(192.168.28.128)的连接,显然到一些 web 服务器[2];这些连接现在已经关闭。更重要的是标记为 LISTENING 的连接。那些属于可识别的 Windows 进程( svchost , lsass , wininit )的进程可能没有问题,但 nc64.exe 进程是未知的[1]。它正在侦听端口4444,通过使用第2章中的 netcat 替代品来探测该端口对它进行更深入的研究也是很有价值的。

volshell 接口

(笔者注: volshell ,内存镜像中的shell )

除了命令行界面,您还可以使用 volshell 命令在自定义 Python shell 中使用 Volatility 。这为您提供了 Volatility 的所有功能以及完整的 Python shell 。下面是一个在 Windows 映像上使用 volshell 利用 pslist 插件的例子:

1
2
3
4
5
6
7
8
9
PS> volshell -w -f WinDev2007Eval-7d959ee5.vmem [1]
>>> from volatility.plugins.windows import pslist [2]
>>> dpo(pslist.PsList, primary=self.current_layer, nt_symbols=self.config['nt_symbols']) [3]
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64
4 0 System 0xa50bb3e6d040 129 - N/A False
72 4 Registry 0xa50bb3fbd080 4 - N/A False
6452 4732 OneDrive.exe 0xa50bb4d62080 25 - 1 True
6484 4732 FreeDesktopClo 0xa50bbb847300 1 - 1 False
...

在这个简短的示例中,我们使用 -w 选项来告诉 Volatility 我们正在分析一个 Windows 图像,使用 -f 选项来指定图像本身[1]。一旦进入 volshell 接口,我们就可以像使用普通的 Python shell 一样使用它。也就是说,您可以像往常一样导入包或编写函数,但是现在您还在 shell 中嵌入了 Volatility 。我们导入 pslist 插件[2]并利用 dpo 函数显示插件[3]的输出。

你可以通过输入 volshell --help 找到更多关于使用 volshell 的信息。

自定义 Volatility 插件

我们刚刚看到了如何使用 Volatility 插件来分析存在漏洞的 VM 快照,通过检查正在使用的命令和进程来分析用户,并转储密码散列。但是由于您可以编写自己的自定义插件,所以只有您的想象力才能限制您可以用 Volatility 做什么。如果您需要根据从标准插件找到的线索中获得额外的信息,那么您可以制作自己的插件。

只要您遵循 Volatility 团队的模式,就可以很容易地创建插件。您甚至可以让您的新插件调用其他插件,从而使您的工作更加容易。

让我们来看看一个典型插件的框架:

1
2
3
4
5
6
7
8
9
imports . . .
[1] class CmdLine(interfaces.plugin.PluginInterface):
@classmethod
[2] def get_requirements(cls):
pass
[3] def run(self):
pass
[4] def generator(self, procs):
pass

这里的主要步骤是创建从 PluginInterface [1]继承的新类,定义插件的需求[2],定义 run 类函数[3],以及定义 generator 类函数[4]。 generator 类函数是可选的,但是将它与 run 类函数分离是您将在许多插件中看到的有用模式。通过分离它并将其作为 Python 生成器使用,您可以获得更快的结果,并使代码更容易理解。

让我们遵循这个通用模式来创建一个自定义插件,该插件将检查不受 address space layout randomization (ASLR,地址空间布局随机化)保护的进程。 ASLR 混合了脆弱进程的地址空间,这影响了堆、栈和其他操作系统分配的虚拟内存位置。这意味着攻击者无法确定在攻击时受害进程的地址空间是如何布置的。 Windows Vista 是第一个支持 ASLR 的 Windows 版本。在 Windows XP 等较旧的内存镜像中,默认情况下不会启用 ASLR 保护。现在,在最新的电脑 (Windows 10) 中,几乎所有的进程都受到了保护。

ASLR 并不意味着攻击者就一定失败了,但它使工作变得更加复杂。作为侦察进程的第一步,我们将创建一个插件来检查流程是否受到 ASLR 的保护。

让我们开始吧。创建一个名为 plugins 的目录。在该目录下,创建一个名为 windows 目录,以存放用于 Windows 机器的自定义插件。如果您创建插件以 Mac 或 Linux 机器为目标,请分别创建一个名为 maclinux 的目录。

现在,在 plugins/windows 目录下,让我们编写我们的 ASLR 检查插件, aslrcheck.py :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Search all processes and check for ASLR protection
#
from typing import Callable, List
from volatility.framework import constants, exceptions, interfaces, renderers
from volatility.framework.configuration import requirements
from volatility.framework.renderers import format_hints
from volatility.framework.symbols import intermed
from volatility.framework.symbols.windows import extensions
from volatility.plugins.windows import pslist
import io
import logging
import os
import pefile
vollog = logging.getLogger(__name__)
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGE_FILE_RELOCS_STRIPPED = 0x0001

我们首先处理所需的导入,再加上用于分析 Portable Executable file (PE,可移植可执行文件)的 pefile 库。现在让我们编写一个辅助函数来进行分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[1] def check_aslr(pe):
pe.parse_data_directories([
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
dynamic = False
stripped = False
[2] if (pe.OPTIONAL_HEADER.DllCharacteristics &
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE):
dynamic = True
[3] if pe.FILE_HEADER.Characteristics & IMAGE_FILE_RELOCS_STRIPPED:
stripped = True
[4] if not dynamic or (dynamic and stripped):
aslr = False
else:
aslr = True
return aslr

我们将一个 PE 文件对象传递给 check_aslr 函数[1],解析它,然后检查它是否已经用 DYNAMIC 基础设置编译[2],以及是否存在文件重定位数据段[3]。如果它不是动态的,或者可能编译为动态的但没有重定位数据段,那么 PE 文件也就不受 ASLR 保护[4]。

check_aslr 辅助函数已经准备好了,让我们创建我们的 AslrCheck 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[1] class AslrCheck(interfaces.plugins.PluginInterface):
@classmethod
def get_requirements(cls):
return [
[2] requirements.TranslationLayerRequirement(
name='primary', description='Memory layer for the kernel',
architectures=["Intel32", "Intel64"]),
[3] requirements.SymbolTableRequirement(
name="nt_symbols", description="Windows kernel symbols"),
[4] requirements.PluginRequirement(
name='pslist', plugin=pslist.PsList, version=(1, 0, 0)),
[5] requirements.ListRequirement(name = 'pid',
element_type = int,
description = "Process ID to include (all others are excluded)",
optional = True),
]

创建插件的第一步是从 PluginInterface 对象继承[1]。接下来,定义需求。通过查看其他插件,您可以很好地了解自己需要什么。每个插件都需要内存层,我们首先定义这个需求[2]。除了内存层,我们还需要符号表[3]。您会发现几乎所有插件都会使用这两个需求。

为了从内存中获取所有进程并从进程中重新创建PE文件[4],我们还需要 pslist 插件。然后,我们将从每个进程传递重新创建的 PE 文件,并检查它是否有 ASLR 保护。

我们可能想要检查给定进程 ID 的单个进程,因此我们创建另一个可选设置,允许我们传入一个进程 ID 列表,以限制只检查那些进程[5]。

1
2
3
4
5
6
7
8
@classmethod
def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[interfaces.objects.ObjectInterface], bool]:
filter_func = lambda _: False
pid_list = pid_list or []
filter_list = [x for x in pid_list if x is not None]
if filter_list:
filter_func = lambda x: x.UniqueProcessId not in filter_list
return filter_func

为了处理可选的进程 ID ,我们使用一个类函数创建一个过滤函数,该函数为列表中的每个进程 ID 返回 False ;也就是说,我们询问过滤函数的问题是是否过滤掉一个进程,所以只有 PID 不在列表中时才返回 True :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def _generator(self, procs):
pe_table_name = intermed.IntermediateSymbolTable.create( [1]
self.context,
self.config_path,
"windows",
"pe",
class_types=extensions.pe.class_types)
procnames = list()
for proc in procs:
procname = proc.ImageFileName.cast("string",
max_length=proc.ImageFileName.vol.count, errors='replace')
if procname in procnames:
continue
procnames.append(procname)
proc_id = "Unknown"
try:
proc_id = proc.UniqueProcessId
proc_layer_name = proc.add_process_layer()
except exceptions.InvalidAddressException as e:
vollog.error(f"Process {proc_id}: invalid address {e} in layer {e.layer_name}")
continue
peb = self.context.object( [2]
self.config['nt_symbols'] + constants.BANG + "_PEB",
layer_name = proc_layer_name,
offset = proc.Peb)
try:
dos_header = self.context.object(
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
offset=peb.ImageBaseAddress,
layer_name=proc_layer_name)
except Exception as e:
continue
pe_data = io.BytesIO()
for offset, data in dos_header.reconstruct():
pe_data.seek(offset)
pe_data.write(data)
pe_data_raw = pe_data.getvalue() [3]
pe_data.close()
try:
pe = pefile.PE(data=pe_data_raw) [4]
except Exception as e:
continue
aslr = check_aslr(pe) [5]
yield (0, (proc_id, [6]
procname,
format_hints.Hex(pe.OPTIONAL_HEADER.ImageBase),
aslr,
))

我们创建了一个名为 pe_table_name [1]的特殊数据结构,用于循环内存中的每个进程。然后我们得到与每个进程相关联的Process Environment Block (PEB,进程环境块)内存区域,并将其放入对象中[2]。 PEB 是当前进程的数据结构,它包含了关于进程的大量信息。我们向该区域写入一个类文件对象 (pe_data) [3],使用 pefile 库创建一个 PE 对象[4],并将其传递给 check_aslr 辅助函数[5]。最后,我们生成包含进程 ID 、进程名、进程内存地址的信息元组,以及来自 ASLR 保护检查[6]的 Boolean (布尔值)结果。

现在我们创建 run 类函数,它不需要任何参数,因为所有的设置都被填充到 config 对象中:

1
2
3
4
5
6
7
8
9
10
11
12
def run(self):
1 procs = pslist.PsList.list_processes(self.context,
self.config["primary"],
self.config["nt_symbols"],
filter_func =
self.create_pid_filter(self.config.get('pid', None)))
2 return renderers.TreeGrid([
("PID", int),
("Filename", str),
("Base", format_hints.Hex),
("ASLR", bool)],
self._generator(procs))

我们使用 pslist 插件获取进程列表[1],并使用 TreeGrid 渲染器从生成器返回数据[2]。许多插件都使用 TreeGrid 渲染器。它确保我们为每个分析的进程得到一行结果。

(笔者注: TreeGrid 是一种 DHTML 控件,其完全使用 JavaScript 语言编写,用以在HTML页面上展示具有层次结构的数据项 ,其核心技术为多叉树。)

Kicking the Tires

让我们看一看在 Volatility 网站上提供的其中一个有效映像: Malware - Cridex 。对于您的自定义插件,提供 -p 选项与您的 plugins 文件夹路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS>vol -p .\plugins\windows -f cridex.vmem aslrcheck.AslrCheck
Volatility 3 Framework 1.2.0-beta.1
Progress: 0.00 Scanning primary2 using PdbSignatureScanner
PID Filename Base ASLR
368 smss.exe 0x48580000 False
584 csrss.exe 0x4a680000 False
608 winlogon.exe 0x1000000 False
652 services.exe 0x1000000 False
664 lsass.exe 0x1000000 False
824 svchost.exe 0x1000000 False
1484 explorer.exe 0x1000000 False
1512 spoolsv.exe 0x1000000 False
1640 reader_sl.exe 0x400000 False
788 alg.exe 0x1000000 False
1136 wuauclt.exe 0x400000 False

如您所见,这是一台 Windows XP 机器,在任何进程上都没有 ASLR 保护。

下面是纯净的全新的 Windows 10 机器的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PS>vol -p .\plugins\windows -f WinDev2007Eval-Snapshot4.vmem aslrcheck.AslrCheck
Volatility 3 Framework 1.2.0-beta.1
Progress: 33.01 Scanning primary2 using PdbSignatureScanner
PID Filename Base ASLR
316 smss.exe 0x7ff668020000 True
428 csrss.exe 0x7ff796c00000 True
500 wininit.exe 0x7ff7d9bc0000 True
568 winlogon.exe 0x7ff6d7e50000 True
592 services.exe 0x7ff76d450000 True
600 lsass.exe 0x7ff6f8320000 True
696 fontdrvhost.ex 0x7ff65ce30000 True
728 svchost.exe 0x7ff78eed0000 True
Volatility was unable to read a requested page:
Page error 0x7ff65f4d0000 in layer primary2_Process928 (Page Fault at entry 0xd40c9d88c8a00400
in page entry)

* Memory smear during acquisition (try re-acquiring if possible)
* An intentionally invalid page lookup (operating system protection)
* A bug in the plugin/volatility (re-run with -vvv and file a bug)

No further results will be produced

这里没什么可看的。每个列出的进程都受到 ASLR 的保护。然而,我们也看到了 memory smear ,内存污染。当拍摄内存映像时,内存存储的内容发生变化时,就会发生 memory smear 。这会导致内存表描述与内存本身不匹配;或者,虚拟内存指针可能引用无效数据。黑客攻击就会困难。正如错误描述所说,您可以尝试重新获取映像(寻找或创建一个新的快照)。

(笔者注: memory smear ,在内存采集过程中,运行中系统对数据的修改。)

让我们检查 PassMark Windows 10 样本内存映像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS>vol -p .\plugins\windows -f WinDump.mem aslrcheck.AslrCheck
Volatility 3 Framework 1.2.0-beta.1
Progress: 0.00 Scanning primary2 using PdbSignatureScanner
PID Filename Base ASLR
356 smss.exe 0x7ff6abfc0000 True
2688 MsMpEng.exe 0x7ff799490000 True
2800 SecurityHealth 0x7ff6ef1e0000 True
5932 GoogleCrashHan 0xed0000 True
5380 SearchIndexer. 0x7ff6756e0000 True
3376 winlogon.exe 0x7ff65ec50000 True
6976 dwm.exe 0x7ff6ddc80000 True
9336 atieclxx.exe 0x7ff7bbc30000 True
9932 remsh.exe 0x7ff736d40000 True
2192 SynTPEnh.exe 0x140000000 False
7688 explorer.exe 0x7ff7e7050000 True
7736 SynTPHelper.ex 0x7ff7782e0000 True

几乎所有进程都受到保护。只有一个进程 SynTPEnh.exe 不受 ASLR 保护。在线搜索显示,这是 Synaptics Pointing Device 的一个软件组件,可能用于触摸屏。只要这个进程被安装在 c:\Program Files 中,就没有问题的,但它可能值得以后去弄清楚。

在本章中,您看到了您可以利用 Volatility 框架的强大功能来查找关于用户行为和连接的更多信息,以及分析任何进程运行内存中的数据。您可以使用这些信息来更好地理解目标用户和机器,以及理解防御者的心态。

Onward! (前进吧)

现在您应该已经注意到, Python 是一种非常适合黑客编程的语言,特别是当您考虑到许多可用的库和基于 Python 的框架时。虽然黑客拥有大量的工具,但没有什么能替代编写自己的工具,因为这能让你更深入地了解其他工具在做什么。

继续前进,为您的特殊需求编写一个自定义工具。无论它是 Windows 的 SSH 客户端, web scraper (笔者注: Web Scraper ,一个插件,轻量数据爬取利器),还是命令和控制系统, Python 都帮你搞定了。