这台靶机的攻击过程主要是经典的缓冲区溢出利用,与 OSCP 2021 版教材中的 Windows 缓冲区溢出章节的利用部分相似。因此,值得亲自动手实践。
鉴于我的个人水平有限,如果在阅读过程中遇到任何难以理解的部分,可以观看红队笔记团队的 Brainpan1 的那期视频来帮助理解,本文就是对它的拙劣模仿:「红队笔记」靶机精讲:Brainpan1 - 缓冲区溢出典型利用过程
靶机地址:/entry/sunset-dawn3,436/
(相关资料图)
主机发现
这里用 Nmap 来发现目标主机
发现目标主机 IP:
确认目标之后,扫描它的所有 TCP 端口,检查端口开放情况
可以发现它开放了两个不常见的 TCP 端口:2100 和 6812。
那么就继续进行详细扫描来探测端口的信息。
详细扫描
接下来对这两个端口进行详细的 TCP 端口扫描,尝试识别开放的端口、对应的服务和版本信息,以及主机的操作系统信息。
2100 是 FTP 端口,服务软件版本是 pyftpdlib ,里面有一个叫 的程序,并且允许匿名登录。
6812 端口没扫描出什么信息,应该不是常见的应用。
漏洞扫描
尝试 Nmap 的漏洞脚本扫描,看看能否得到一些信息
没有得到更多的信息,那么就先登录 FTP 看看
连接 ftp 服务
使用 anonymous 作为用户名,密码留空直接按回车登录即可。
进来之后发现确实只有一个 的文件,那么就先用 binary 命令,将传输模式设置为二进制模式后再用 get 命令将文件下载到本地
因为这里要下载的文件 是一个可执行文件,它包含了二进制数据。FTP 服务器以 ASCII 模式进行传输时可能会导致文件内容损坏,进而影响文件的可执行性。因此,通过 binary 命令切换到二进制模式后可以确保文件正确地传输并保持其完整性。
但是从红线的部分可以看出来,当前靶机上的 ftp 服务已经是以 binary 模式进行的文件传输,所以这里不输入 binary 命令也是可以的
在下载了文件之后,为了安全性和方便我们后续的分析,可以用下面几个命令来检查文件。
用 file 命令查看当前文件类型
从输出中可以看到一些信息:
PE32 是一种用于 32 位 Windows 操作系统的可执行文件格式
"(console)" 表示这是一个控制台应用程序
"Intel 80386",表示它是 x86 架构
总的来说这就是一个 32 位的 windows 控制台程序。
用 binwalk 命令检查是否有捆绑
能看到有一个 PE 程序和三段加密的数据块,不好确认这些加密数据里是否有捆绑的程序。但是考虑到这是一台靶机的缘故,所以也不用太担心会出现捆绑恶意软件的情况出现。
使用 strings 命令输出可打印的字符
输出了很多内容,全部看下来会很费时间。先往下走,如果发现走不通了再回头来慢慢检查。
运行程序
既然 是 Windows 下的应用程序,那么就把它拿到 Windows 下运行看看
这里用的是另外一台 Windows 7 的虚拟机来运行它,IP 是:
双击运行程序之后会弹出一个控制台窗口,显示正在等待传入的连接
虽然没明说说是服务是哪个端口,但是可以从靶机开放的端口来猜测,有极大可能就是 6812
用 netstat 命令查看这台 Windows 7 开放了哪些端口
从输出结果来看,确实可以发现开启了 6812 这个端口,佐证了我们之前的猜测。
尝试用 nc 去连接这台 Windows 7 的 6812 端口,并且发送 123 和 1234
终端输出 New connection,说明二者成功建立了连接
虽然输出的信息有问题,但是不影响我们后续的操作。
既然程序支持输入数据,我们可以尝试输入大量数据,观察是否会导致崩溃。如果程序崩溃了,可能存在缓冲区溢出漏洞。
识别缓冲区溢出
有两种方法可以发送数据进行测试。一种方法是编写脚本来向目标端口发送数据。另一种方法是直接使用 nc 命令来发送数据。
如果选择使用 Python 脚本来发送数据,建议使用 Python2 进行编写。因为使用 Python3 发送的数据可能会出现编码问题,导致利用失败。(但我并不清楚其中的具体原因,希望有经验的师傅可以在评论区里解答这个问题。)
接下来,我们可以直接通过命令行来实现这个目标。从 100 开始,逐渐增加数据量,如 200、400、600...... 直到目标程序崩溃。
在尝试发送 600 个字符 'A' 到目标端口时,目标程序发生了崩溃。
综上所述,我们目前得到了以下信息:
目标系统只开放了两个 TCP 端口,分别是 FTP 和 程序所使用的端口。
我们获得了一个名为 的程序,并成功运行它。该程序会在主机上开启一个 6812 端口,允许远程输入数据。
在向目标端口输入 600 个字符时,程序发生崩溃。
综合上述三点信息,可以推测这台靶机的设计初衷可能是让我们分析 程序,并利用缓冲区溢出漏洞来攻击该靶机。
动态分析
Immunity Debugger
这里用到一个叫 Immunity Debugger 的工具,其主要用途涵盖了动态分析、逆向工程以及漏洞研究等领域。该工具支持利用 Python 脚本编写自定义脚本,以执行从简单的信息收集到复杂的分析操作等各种自动化任务。
官网地址:/products/debugger/
这个工具实质上充当了 CPU 和程序之间的一个中继站,通过这个中继站,我们能够观察、拦截和处理程序的行为。这让我们有机会深入了解程序的运作方式以及发现潜在的漏洞。
简单介绍下四个窗口的功能,当使用它运行程序后就会看到如下的界面
左上角的窗口里显示的是程序汇编代码
右上角的窗口里展示的是寄存器的信息
左下交的窗口里展示的是内存信息
右下角的窗口里展示的是堆栈信息
定位 EIP 的位置
EIP(Extended Instruction Pointer)是 x86体系结构中的一个寄存器,用于存储下一条将要执行的指令的内存地址。
运行 Immunity 后点击文件图标(或者按 F3 用快捷键)来打开目标程序
打开程序之后,点击三角按钮运行程序(或者按 F9 用快捷键来运行程序)
当看到 显示了提示信息,并且右下角的运行状态为 “Running” 时就代表运行成功
那么此时再向目标端口发送 600 个 A
在执行后,我们可以观察到 EIP 寄存器的值变为了 "41414141",这对应于 ASCII 值为 41 的字符 "A"。
从这一步我们可以推断,我们输入的这 600 个 "A" 已经成功地溢出到了 EIP 寄存器中,导致 EIP 寄存器的值变为了 "41414141",也就是 4 个 "A",即 "AAAA"。
现在因为我们成功地修改了 EIP 的值,所以可以确认这个程序有缓冲区溢出漏洞的存在。
因此,我们的下一步目标是确定需要输入多少个 "A" 才能溢出到 EIP 寄存器,从而修改 EIP 的值,这将影响 CPU 执行的下一条指令。
进行下一步操作之前,点击叉来关闭当前应用(或者用快捷键 Alt+F2 来关闭)
成功运行之后就可以进行下一步操作了
这里会用到 msf 的一个工具:msf-pattern_create ,可以用它来生成一段连续不重复的字符串
然后将这段字符串发送给 windows7 的 6812 端口,也就是 的服务端口
发送之后,可以看到 EIP 的值变成了 "35724134",也就就是 "\x35\x72\x41\x34"
然后就可以用另一个工具 msf-pattern_offset 来寻找偏移量
它计算偏移量为 524,这意味着从第 524 个字符开始,数据将会溢出到 EIP 寄存器中。
计算这个偏移量的过程如下:
在 x86 架构中,采用小端字节序。这意味着低位字节存储在内存的低地址处,而高位字节存储在内存的高地址处。因此,"\x35\x72\x41\x34" 的存储顺序实际上应该是反过来的,它的值为:"\x34\x41\x72\x35"。
将这个 "\x34\x41\x72\x35" 转换为 ASCII 码,得到的是 "4Ar5"。这正好是我们使用 "msf-pattern_create -l 600" 生成的字符串的一部分。
接下来就是是确定 "4Ar5" 这段字符串从源字符串的第几个字符开始出现的。将这段字符放入文本编辑器中,选中 "4Ar5" 之前的字符,就可以发现在它之前的字符串的总长度是 524。
因此,我们可以尝试发送如下内容:
524 个 "A" 作为填充,以达到溢出之前的目的,加上 4 个 "B" 用来覆盖 EIP 寄存器,再加上72 个 "C" 用来填充堆栈
要注意的是,每次发送数据之前,都需要关闭 Immunity Debugger 中的 程序,并重新运行它,确保程序的正常运行。
发送完成后,我们可以观察到 EIP 寄存器的值变为了 "42424242",即 "BBBB",这表明我们成功地将数据精准地溢出到了 EIP 寄存器中。
因此,我们现在成功地定位了 EIP 的位置,下一步就是测试 ESP 的容量。
测试 ESP 的容量
ESP(Extended Stack Pointer)是 x86架构中的一个寄存器,通常被用于指示栈的当前顶部。
在后续的操作中,我们将会把 Shellcode 存放在栈中。标准的 Shellcode 大小通常约为 500 个字节左右。因此,我们需要测试一下 ESP 寄存器的容量,看看是否足够容纳这 500 个字节的 Shellcode。如果程序没有崩溃,就说明可以继续进行下一步。
将 C 的数量改为 500,并发送数据到目标地址:
500 个 C 发送过去之后,可以看到堆栈从 "28FCF0" 开始
从 "28FEDC" 结束
计算中间的字节数量
最终确认有效空间为 492 字节,是足够我们存放 Shellcode 的。
查找不兼容字符(坏字符)
不同的程序存在不同的不兼容字符,我们需要找出那些不能被利用的不兼容字符,并将它们筛选出来。这样可以确保在生成 Shellcode 时不会因为这些不兼容字符的存在而导致执行异常。
举例来说,"\x00" 就是一个坏字符,在系统底层中它被视为字符串的终止符或结束标志。因此,如果 Shellcode 中包含 "\x00",就可能导致程序异常终止。
我们可以使用工具来生成所有可能的字符,然后用这些字符来进行测试。以下链接提供了一个工具,可以生成不兼容字符,用于指导编码器(如 shikata-ga-nai)将这些字符转换为其他字符:
badchars:/cytopia/badchars
将项目克隆到本地,然后进入 badchars 目录运行程序
执行程序之后,能看到他生成了除 "\x00" 以外的所有字符,下面就将它进我们的测试命令中,发送给目标
发送完成后追踪 ESP 的内存位置
可以看到从地址 "28FCF0" 开始的 "01"、"02"、"03" 到最后的 "FF" ,并没有缺失一个字符
这说明目标程序并没有不兼容的字符,也就是没有坏字符("\x00" 除外)
下面就可以定位 ESP 的位置了
定位 ESP 的位置
由于 ESP 寄存器中的地址是不确定的,我们需要找到 ESP 的地址,并将它覆盖到 EIP 寄存器中。这样,当 CPU 执行到 EIP 所指向的地址时,它实际上会执行 ESP 寄存器中存储的地址。而 ESP 指向的内存位置已被我们的 Shellcode 所覆盖,因此 CPU 将会执行我们的 Shellcode。
为了找到 ESP 寄存器的地址,我们需要使用一个叫做 msf-nasm_shell 的工具。这个工具能够将汇编语言代码转换为相应的十六进制格式,以便我们在漏洞利用中使用。
在上面的内容中,我们对 "jmp esp" 的十六进制格式进行了查询,发现它十六进制格式为 FFE4,相应的操作码为 "\xFF\xE4"。
"jmp esp" 是一个汇编指令,其作用是跳转到存储在 ESP 寄存器(栈指针)中的地址所指向的位置,也就是我们通常用来存放 Shellcode 的位置。
接下来,我们将在 "" 这个程序中寻找包含 "jmp esp" 指令的位置,从而获取 ESP 寄存器所指向的位置。
需要注意的是,在寻找时我们不能直接使用 "jmp esp" 作为关键字,而是要使用其对应的操作码:"\xFF\xE4"。
此时,我们需要使用 Immunity Debugger 的名为 "mona" 的插件。默认情况下该插件是未安装的,需手动下载并安装。
然后将里面的 复制到:
C:\Program Files (x86)\Immunity Inc\Immunity Debugger\PyCommands 这个目录里
再次启动 Immunity 即可成功加载 mona
在 Immunity 里启动 之后,通过左下角输入命令,列出所有的模块
执行成功后就能看到类似下面的页面
从 Module info 这个列表中可以看出来有四种安全机制:
Rebase(重定位):重定位是一种操作系统和程序加载器的特性,允许将共享的库和可执行文件加载到内存中的不同位置,以防止地址空间的冲突。重定位可以提高系统的安全性,防止一些攻击,如基于地址的攻击。
SafeSEH(安全异常处理链表):在 Windows 操作系统中,SafeSEH 是一种保护机制,用于检测和阻止异常处理链表被利用用于漏洞利用。它可以帮助防止 SEH(Structured Exception Handling)覆盖攻击。
ASLR(地址空间布局随机化):ASLR 是一种操作系统的安全特性,通过随机化加载程序和库的内存位置,来防止攻击者预测这些位置。这可以减少缓冲区溢出和代码注入等攻击的成功率。
NXCompat(不可执行内存保护):NXCompat 是一种硬件和操作系统级别的安全特性,阻止内存中的数据被当作代码执行。这可以防止缓冲区溢出等漏洞被利用来执行恶意代码。
但这四种安全机制对于 来讲都是 False,也就是没有启用,所以我们可以继续进行操作。
确定了可以继续利用之后就可以开始找目标的 "jmp esp" 的位置了
执行命令后,我们找到了一个指针的信息,可以从中看到该指针指向地址为:"0x52501513"。
现在,我们可以使用这个地址来覆盖 EIP 寄存器,从而引导 CPU 执行栈中的 Shellcode。
但需要注意的是,在 x86 架构中采用的是小端字节序。因此,我们需要将这个地址反转,变为 "13155052",即:"\x13\x15\x50\x52"。
那么现在我们的发送的数据逻辑如下:
这里用 msfvenom 生成一个 windows 的 Shellcode
这个命令生成了一个大小为 351 字节的 Shellcode。考虑到堆栈空间有 492 字节,这个大小是足够的。
然而,需要注意的是,"shikata_ga_nai" 编码器会在执行代码之前生成一个用于解码的解码器,因此会占用一些字节。为了给解码器预留空间,这里使用 "\x90" 字节作为填充,确保有足够的空间供解码器使用。如果不预留空间,后续的 Shellcode 可能有部分会被覆盖,导致无法正常执行。
"\x90" 表示 NOP 指令,在汇编语言中,NOP 指令通常用作占位符或延时操作。在许多情况下 NOP 指令不会对程序的执行产生实际影响,它主要用于填充指令序列,以实现指令地址对齐或缓解特定条件。
然后就可以将生成的 Shellcode 和填充的 NOP 指令组合起来。
数据发送之后就可以成功地收到反弹回来的 Shell。测试结束,接下来就是对靶机进行漏洞利用了。
如果不出意外的话,只需将 Shellcode 这部分从 Windows 改为 Linux 即可成功利用缓冲区溢出漏洞获得靶机上的立足点。
生成 Linux 下的 Shellcode
这次 Shellcode 只有 95 字节,比 Windows 下的要小不少。
将它组合进命令,发送给靶机
不出意外的成功反弹回来了一个 Shell,并且在检查当前用户身份的时候,发现反弹回来的 Shell 就是 root 权限,省去了提权的部分。
用 "crontab -l" 命令可以发现计划任务中有一个每分钟都用 wine 来运行 这个程序的任务
这样就可以解释为什么 exe 文件可以在 linux 上运行了。
最后,欢迎各位师傅捉虫
标签: