所以有很多渗透测试工具可以在系统上执行内存转储。它们究竟是如何工作的——究竟发生了什么?
系统内存转储如何工作?
整个系统或单个可执行文件的核心转储是该进程内存的全部内容,或者在系统的情况下,所有内容的内存都写入文件。这样的转储中可能有很多数据 - 这是我目前正在运行的 gvim 内存空间的缩写形式:
pmap 19133
19133: gvim fsstate.py
0000000000400000 2184K r-x-- /usr/bin/gvim ; program code
0000000000821000 100K rw--- /usr/bin/gvim ; program code again
000000000083a000 52K rw--- [ anon ]
0000000000a39000 56K rw--- /usr/bin/gvim ; more program code
0000000002469000 3592K rw--- [ anon ]
0000003d53c00000 16K r-x-- /lib64/libuuid.so.1.3.0 ; shared object
0000003d53c04000 2044K ----- /lib64/libuuid.so.1.3.0
0000003d53e03000 4K rw--- /lib64/libuuid.so.1.3.0
0000003d55000000 28K r-x-- /usr/lib64/libSM.so.6.0.1
0000003d55007000 2044K ----- /usr/lib64/libSM.so.6.0.1
...
00007f46d66c7000 20K r---- /usr/share/locale/en_GB/LC_MESSAGES/gdk-pixbuf.mo
00007f46d66cc000 4K r---- /usr/share/locale/en_GB/LC_MESSAGES/libc.mo
00007f46d66cd000 56K r---- /usr/share/locale/en_GB/LC_MESSAGES/gtk20.mo
00007f46d66db000 28K r--s- /usr/lib64/gconv/gconv-modules.cache
00007f46d66e5000 4K rw--- [ anon ]
00007fffc9ed9000 132K rw--- [ stack ] ; stack!
00007fffc9f9b000 4K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
从那个地址空间,共享对象不会被转储,但其他所有东西,堆栈,堆,都会被转储。地址将与某些版本的共享对象相关,并且可能与 ASLR 一起,它们可能不会排队处理。内核转储可以配置为包含不同数量的信息,从小转储到所有信息。
这告诉你什么?系统或程序在被转储时的确切状态 - 它在堆栈上的状态(以及它可能在哪个函数中)。大多数核心转储还包含其他有用的信息,例如转储时处理器寄存器中的内容。
就你如何计算正在发生的事情而言,x86 (x64) 上的 EIP 或 RIP 会告诉你进程正在执行的确切内存地址。rbp 或 ebp 会告诉你你的函数在堆栈上的什么位置。您希望在那里找到局部变量。您还可以在核心转储中查找所有系统调用及其参数。
所有这些都可以告诉您某个特定时间点的流程正在发生什么。它也不仅仅是笔测试的工具。可以将核心转储作为调试应用程序的一种形式进行分析,尤其是在很难或不可能运行调试器的情况下。例如,您可能会分析内核核心转储以找出内核崩溃的原因(当它恐慌时它在做什么?)而不是附加调试器并重试 - 它还允许其他人将转储发送给您进行分析。
这对渗透测试有何帮助?调试有助于渗透测试的方式相同。你可以看到内存中加载了什么,当前状态是什么。当您进行转储时,您可以计算出进程可能在做什么,也许最关键的是,存储在内存中任何可执行文件(堆、堆栈等)中的任何数据也将最终出现在核心转储中,包括以明文形式存储在内存中的密码.
分析工具有什么作用?寻找指示潜在漏洞(或错误,如果您在调试之后)的模式和结构。对此的完整解释可能超出了本框的范围 - 有许多书籍专门介绍分析内存转储的技术。其中大部分还取决于系统和架构。适用于 Windows x86 的技术与适用于 MIPS 上的 Linux 的技术不同。
作为可能指示错误的示例,以下是 AviD 将非常欣赏的示例:
push rbp
mov rbp, rsp
sub rsp, 32
mov QWORD PTR [rbp-24], rdi ; this is a char* argument stored on stack
mov rdx, QWORD PTR [rbp-24] ; and this is a pointer loading it into a register
lea rax, [rbp-16] ; this is only 8 bytes further down
mov rcx, QWORD PTR [rdx] ; load contents of rdx into rcx
mov QWORD PTR [rax], rcx ; put those 8 bytes to rbp-16
mov rcx, QWORD PTR [rdx+8] ; load next 8 butes
mov QWORD PTR [rax+8], rcx ; write to rbp-8
mov rcx, QWORD PTR [rdx+16]
mov QWORD PTR [rax+16], rcx ; write to rbp
mov rcx, QWORD PTR [rdx+24]
mov QWORD PTR [rax+24], rcx ; write to rbp+8
mov rcx, QWORD PTR [rdx+32]
mov QWORD PTR [rax+32], rcx ; write to rbp+16
mov rcx, QWORD PTR [rdx+40]
mov QWORD PTR [rax+40], rcx ; write to rbp+24
mov rcx, QWORD PTR [rdx+48]
mov QWORD PTR [rax+48], rcx ; write to rbp+32
mov rdx, QWORD PTR [rdx+56]
mov QWORD PTR [rax+56], rdx ; write to rbp+40
lea rax, [rbp-16]
如您所见,该函数在堆栈上可用于局部变量的第一个空间是rbp-16
及以下。除此之外,我们刚刚愉快地用一大堆数据破坏了堆栈帧,可能导致程序崩溃。但是,此代码很可能表明可能存在漏洞。在故障转储中寻找类似的模式是它们有用的原因,而且rip
在这种情况下,它会准确地告诉你是哪个内存访问导致了问题,因为它会指向有问题的指令(理论上)。
如果您想知道,有问题的代码只是memcpy
从一个无界缓冲区到一个小得多的目标空间。对 memcpy 的函数调用进行了优化。
首先,安全领域内存转储的主要用户有两个——取证和漏洞利用编写人员。渗透测试人员,不是那么多 - 当然这取决于渗透测试;)
转储内存工作的常用工具是打开内存伪设备并将所有内容读取到文件中。
例如,在 Windows 中使用 dd 你会做类似的事情dd if=\\.\Device\PhysicalMemory of=memory.bin bs=4096
(pre win-2003,之后你需要一个内核驱动程序)
这同样适用于linux,例如 dd if=/dev/mem of=/home/john/mem.bin bs=1024
这当然是最简单的方法。其他获取内存的方法是使用 Ninefingers 提到的崩溃转储、LiveKd 转储、如果我们谈论的是 VM,则通过管理程序获取内存,如果存在则获取休眠文件,甚至通过防火墙或其他 DMA 接口获取内存。当然,还有冷启动攻击。
将内存放在文件上,您可以做很多事情,这取决于您想要什么。常见的一种是搜索字符串,例如密码令牌、私钥等,这是最简单的一种。
如果您想做更多的事情,通常需要做更多的工作,但使用商业或开源工具可以自动化并取得不同程度的成功。主要思想是转储中的物理内存将分散在页面中。为了理解它,您通过启发式搜索页表来定位,并尝试重建内存的逻辑视图。完成后,您可以执行其他搜索并为各种正在运行的进程、打开的文件、网络连接等定位结构。这实际上取决于您的目标是什么。
如果您列出哪些工具会有所帮助,因为存在差异 - 例如整个内存与特定区域。我想不出我经常使用的任何转储内存的攻击工具。
但简单地说,通过内存转储,您不仅可以查看应用程序代码,还可以查看变量值。
内存只是被复制到磁盘,类似于在正常使用中将内存页面交换到磁盘的方式,或者工具直接读取内存。
如果您使用的是 Windows,请使用 SysInternals 套件中的 Process Explorer。只需右键单击一个进程并选择转储该进程的内存。