📊
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
  • 题目解析
  • 参考资料

Was this helpful?

  1. 六、题解篇
  2. Reverse

6.2.1 re XHPCTF2017 dont_panic

PreviousReverseNext6.2.2 re ECTF2016 tayy

Last updated 3 years ago

Was this helpful?

题目解析

第一步当然是 file 啦:

$ file dont_panic
dont_panic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

64 位,静态编译,而且 stripped。

看一下段吧:

$ readelf -S dont_panic
There are 13 section headers, starting at offset 0xfa388:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000401000  00001000
       000000000007ae40  0000000000000000  AX       0     0     16
  [ 2] .rodata           PROGBITS         000000000047c000  0007c000
       0000000000033f5b  0000000000000000   A       0     0     32
  [ 3] .typelink         PROGBITS         00000000004b0080  000b0080
       0000000000000b4c  0000000000000000   A       0     0     32
  [ 4] .itablink         PROGBITS         00000000004b0bd0  000b0bd0
       0000000000000038  0000000000000000   A       0     0     8
  [ 5] .gosymtab         PROGBITS         00000000004b0c08  000b0c08
       0000000000000000  0000000000000000   A       0     0     1
  [ 6] .gopclntab        PROGBITS         00000000004b0c20  000b0c20
       0000000000044d5d  0000000000000000   A       0     0     32
  [ 7] .noptrdata        PROGBITS         00000000004f6000  000f6000
       0000000000002608  0000000000000000  WA       0     0     32
  [ 8] .data             PROGBITS         00000000004f8620  000f8620
       0000000000001cf0  0000000000000000  WA       0     0     32
  [ 9] .bss              NOBITS           00000000004fa320  000fa310
       000000000001a908  0000000000000000  WA       0     0     32
  [10] .noptrbss         NOBITS           0000000000514c40  000fa310
       00000000000046a0  0000000000000000  WA       0     0     32
  [11] .note.go.buildid  NOTE             0000000000400fc8  00000fc8
       0000000000000038  0000000000000000   A       0     0     4
  [12] .shstrtab         STRTAB           0000000000000000  000fa310
       0000000000000073  0000000000000000           0     0     1

我们发现一些奇怪的东西,.gosymtab、.gopclantab,Google 一下才知道,它其实是一个用 Go 语言编写的程序。好吧,运行它:

$ ./dont_panic
usage: ./dont_panic flag
$ ./dont_panic abcd
Nope.
$ xxd -g1 dont_panic | grep Nope.
000a5240: 3e 45 72 72 6e 6f 45 72 72 6f 72 4e 6f 70 65 2e  >ErrnoErrorNope.
$ objdump -d dont_panic | grep a524b
  47ba23:       48 8d 05 21 98 02 00    lea    0x29821(%rip),%rax        # 0x4a524b

字符串“Nope.”应该是判断错误时的输出,我们顺便找到了使用它的地址为 0x47ba23,接下来在去 r2 里看吧,经过一番搜索,找到了最重要的函数 fcn.0047b8a0:

[0x0047ba23]> pdf @ fcn.0047b8a0
/ (fcn) fcn.0047b8a0 947
|   fcn.0047b8a0 ();
|              ; JMP XREF from 0x0047bc4e (fcn.0047b8a0)
|       .-> 0x0047b8a0      64488b0c25f8.  mov rcx, qword fs:[0xfffffffffffffff8]
|       :   0x0047b8a9      488d442490     lea rax, [rsp - 0x70]
|       :   0x0047b8ae      483b4110       cmp rax, qword [rcx + 0x10] ; [0x10:8]=-1 ; 16
|      ,==< 0x0047b8b2      0f8691030000   jbe 0x47bc49
|      |:   0x0047b8b8      4881ecf00000.  sub rsp, 0xf0
|      |:   0x0047b8bf      4889ac24e800.  mov qword [local_e8h], rbp
|      |:   0x0047b8c7      488dac24e800.  lea rbp, [local_e8h]        ; 0xe8 ; 232
|      |:   0x0047b8cf      488b05f2ec07.  mov rax, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
|      |:   0x0047b8d6      4883f802       cmp rax, 2                  ; 2
|     ,===< 0x0047b8da      0f8530020000   jne 0x47bb10
|     ||:      ; JMP XREF from 0x0047bc3d (fcn.0047b8a0)
|    .----> 0x0047b8e0      488b05d9ec07.  mov rax, qword [0x004fa5c0] ; [0x4fa5c0:8]=0
|    :||:   0x0047b8e7      488b0ddaec07.  mov rcx, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
|    :||:   0x0047b8ee      4883f901       cmp rcx, 1                  ; 1
|   ,=====< 0x0047b8f2      0f8611020000   jbe 0x47bb09
|   |:||:   0x0047b8f8      488b4810       mov rcx, qword [rax + 0x10] ; [0x10:8]=-1 ; 16
|   |:||:   0x0047b8fc      48894c2450     mov qword [local_50h], rcx
|   |:||:   0x0047b901      488b4018       mov rax, qword [rax + 0x18] ; [0x18:8]=-1 ; 24
|   |:||:   0x0047b905      4889442448     mov qword [local_48h], rax
|   |:||:   0x0047b90a      4883f82a       cmp rax, 0x2a               ; '*' ; 42 ; 判断密码长度是否大于 42
|  ,======< 0x0047b90e      0f8c0f010000   jl 0x47ba23                 ; 若小于,失败
|  ||:||:   0x0047b914      31d2           xor edx, edx
|  ||:||:   0x0047b916      31db           xor ebx, ebx
|  ||:||:   0x0047b918      4889542438     mov qword [local_38h], rdx  ; 密码字符串 provided_flag 的下标 i
|  ||:||:   0x0047b91d      885c2436       mov byte [local_36h], bl
|  ||:||:   0x0047b921      4839c2         cmp rdx, rax                ; 比较下标 i 与密码长度
| ,=======< 0x0047b924      7d72           jge 0x47b998                ; 若大于或等于,成功
| |||:||:      ; JMP XREF from 0x0047b996 (fcn.0047b8a0)
| --------> 0x0047b926      0fb63411       movzx esi, byte [rcx + rdx] ; 循环终点
| |||:||:   0x0047b92a      4080fe80       cmp sil, 0x80               ; 128
| ========< 0x0047b92e      0f83a0010000   jae 0x47bad4
| |||:||:   0x0047b934      400fb6f6       movzx esi, sil
| |||:||:   0x0047b938      488d7a01       lea rdi, [rdx + 1]          ; 1 ; 下标 + 1
| |||:||:      ; JMP XREF from 0x0047bb04 (fcn.0047b8a0)               ; 密码逐位判断逻辑
| --------> 0x0047b93c      48897c2440     mov qword [local_40h], rdi
| |||:||:   0x0047b941      01f3           add ebx, esi
| |||:||:   0x0047b943      885c2437       mov byte [local_37h], bl    ; bl 代表 provided_flag[i]
| |||:||:   0x0047b947      881c24         mov byte [rsp], bl
| |||:||:   0x0047b94a      e811feffff     call fcn.0047b760           ; 该函数会对 bl 做一些处理
| |||:||:   0x0047b94f      0fb6442408     movzx eax, byte [local_8h]  ; [0x8:1]=255 ; 8 ; eax 是上面函数的返回值,即 mapanic(provided_flag[i])
| |||:||:   0x0047b954      488b4c2438     mov rcx, qword [local_38h]  ; [0x38:8]=-1 ; '8' ; 56
| |||:||:   0x0047b959      4883f92a       cmp rcx, 0x2a               ; '*' ; 42 ; 判断 rcx 是否大于等于 0x2a
| ========< 0x0047b95d      0f836a010000   jae 0x47bacd                ; 如果大于或等于,跳转
| |||:||:   0x0047b963      488d15f6a807.  lea rdx, 0x004f6260         ; 读入 constant_binary_blob
| |||:||:   0x0047b96a      0fb60c0a       movzx ecx, byte [rdx + rcx] ; ecx 代表 constant_binary_blob[i]
| |||:||:   0x0047b96e      38c8           cmp al, cl                  ; 比较 mapanic(provided_flag[i]) 和 constant_binary_blob[i]
| ========< 0x0047b970      0f85ad000000   jne 0x47ba23                ; 如果不等于,失败
| |||:||:   0x0047b976      488b442448     mov rax, qword [local_48h]  ; [0x48:8]=-1 ; 'H' ; 72
| |||:||:   0x0047b97b      488b4c2450     mov rcx, qword [local_50h]  ; [0x50:8]=-1 ; 'P' ; 80
| |||:||:   0x0047b980      488b542440     mov rdx, qword [local_40h]  ; [0x40:8]=-1 ; '@' ; 64
| |||:||:   0x0047b985      0fb65c2437     movzx ebx, byte [local_37h] ; [0x37:1]=255 ; '7' ; 55
| |||:||:   0x0047b98a      4889542438     mov qword [local_38h], rdx
| |||:||:   0x0047b98f      885c2436       mov byte [local_36h], bl
| |||:||:   0x0047b993      4839c2         cmp rdx, rax
| ========< 0x0047b996      7c8e           jl 0x47b926                 ; 循环起点
| |||:||:      ; JMP XREF from 0x0047b924 (fcn.0047b8a0)
| `-------> 0x0047b998      488d05d5c902.  lea rax, 0x004a8374         ; "Seems like you got a flag." ; 成功
|  ||:||:   0x0047b99f      48898424a800.  mov qword [local_a8h], rax
|  ||:||:   0x0047b9a7      48c78424b000.  mov qword [local_b0h], 0x1c ; [0x1c:8]=-1 ; 28
|  ||:||:   0x0047b9b3      48c744245800.  mov qword [local_58h], 0
|  ||:||:   0x0047b9bc      48c744246000.  mov qword [local_60h], 0
|  ||:||:   0x0047b9c5      488d05b4e300.  lea rax, 0x00489d80
|  ||:||:   0x0047b9cc      48890424       mov qword [rsp], rax
|  ||:||:   0x0047b9d0      488d8c24a800.  lea rcx, [local_a8h]        ; 0xa8 ; 168
|  ||:||:   0x0047b9d8      48894c2408     mov qword [local_8h], rcx
|  ||:||:   0x0047b9dd      e80efff8ff     call fcn.0040b8f0
|  ||:||:   0x0047b9e2      488b442410     mov rax, qword [local_10h]  ; [0x10:8]=-1 ; 16
|  ||:||:   0x0047b9e7      488b4c2418     mov rcx, qword [local_18h]  ; [0x18:8]=-1 ; 24
|  ||:||:   0x0047b9ec      4889442458     mov qword [local_58h], rax
|  ||:||:   0x0047b9f1      48894c2460     mov qword [local_60h], rcx
|  ||:||:   0x0047b9f6      488d442458     lea rax, [local_58h]        ; 0x58 ; 'X' ; 88
|  ||:||:   0x0047b9fb      48890424       mov qword [rsp], rax
|  ||:||:   0x0047b9ff      48c744240801.  mov qword [local_8h], 1
|  ||:||:   0x0047ba08      48c744241001.  mov qword [local_10h], 1
|  ||:||:   0x0047ba11      e84a8effff     call fcn.00474860
|  ||:||:   0x0047ba16      48c704240000.  mov qword [rsp], 0
|  ||:||:   0x0047ba1e      e88d1efeff     call fcn.0045d8b0
|  ||:||:      ; JMP XREF from 0x0047b90e (fcn.0047b8a0)
|  ||:||:      ; JMP XREF from 0x0047b970 (fcn.0047b8a0)
| -`------> 0x0047ba23      488d05219802.  lea rax, 0x004a524b         ; "Nope." ; 失败
|   |:||:   0x0047ba2a      488984248800.  mov qword [local_88h], rax
|   |:||:   0x0047ba32      48c784249000.  mov qword [local_90h], 5
|   |:||:   0x0047ba3e      48c784249800.  mov qword [local_98h], 0
|   |:||:   0x0047ba4a      48c78424a000.  mov qword [local_a0h], 0
|   |:||:   0x0047ba56      488d0523e300.  lea rax, 0x00489d80
|   |:||:   0x0047ba5d      48890424       mov qword [rsp], rax
|   |:||:   0x0047ba61      488d84248800.  lea rax, [local_88h]        ; 0x88 ; 136
|   |:||:   0x0047ba69      4889442408     mov qword [local_8h], rax
|   |:||:   0x0047ba6e      e87dfef8ff     call fcn.0040b8f0
|   |:||:   0x0047ba73      488b442410     mov rax, qword [local_10h]  ; [0x10:8]=-1 ; 16
|   |:||:   0x0047ba78      488b4c2418     mov rcx, qword [local_18h]  ; [0x18:8]=-1 ; 24
|   |:||:   0x0047ba7d      488984249800.  mov qword [local_98h], rax
|   |:||:   0x0047ba85      48898c24a000.  mov qword [local_a0h], rcx
|   |:||:   0x0047ba8d      488d84249800.  lea rax, [local_98h]        ; 0x98 ; 152
|   |:||:   0x0047ba95      48890424       mov qword [rsp], rax
|   |:||:   0x0047ba99      48c744240801.  mov qword [local_8h], 1
|   |:||:   0x0047baa2      48c744241001.  mov qword [local_10h], 1
|   |:||:   0x0047baab      e8b08dffff     call fcn.00474860
|   |:||:   0x0047bab0      48c704240100.  mov qword [rsp], 1
|   |:||:   0x0047bab8      e8f31dfeff     call fcn.0045d8b0
|   |:||:   0x0047babd      488bac24e800.  mov rbp, qword [local_e8h]  ; [0xe8:8]=-1 ; 232
|   |:||:   0x0047bac5      4881c4f00000.  add rsp, 0xf0
|   |:||:   0x0047bacc      c3             ret
|   |:||:      ; JMP XREF from 0x0047b95d (fcn.0047b8a0)
| --------> 0x0047bacd      e8ee8dfaff     call fcn.004248c0
|   |:||:   0x0047bad2      0f0b           ud2
|   |:||:      ; JMP XREF from 0x0047b92e (fcn.0047b8a0)
| --------> 0x0047bad4      48890c24       mov qword [rsp], rcx
|   |:||:   0x0047bad8      4889442408     mov qword [local_8h], rax
|   |:||:   0x0047badd      4889542410     mov qword [local_10h], rdx
|   |:||:   0x0047bae2      e869b8fcff     call fcn.00447350
|   |:||:   0x0047bae7      8b742418       mov esi, dword [local_18h]  ; [0x18:4]=-1 ; 24
|   |:||:   0x0047baeb      488b7c2420     mov rdi, qword [local_20h]  ; [0x20:8]=-1 ; 32
|   |:||:   0x0047baf0      488b442448     mov rax, qword [local_48h]  ; [0x48:8]=-1 ; 'H' ; 72
|   |:||:   0x0047baf5      488b4c2450     mov rcx, qword [local_50h]  ; [0x50:8]=-1 ; 'P' ; 80
|   |:||:   0x0047bafa      488b542438     mov rdx, qword [local_38h]  ; [0x38:8]=-1 ; '8' ; 56
|   |:||:   0x0047baff      0fb65c2436     movzx ebx, byte [local_36h] ; [0x36:1]=255 ; '6' ; 54
| ========< 0x0047bb04      e933feffff     jmp 0x47b93c
|   |:||:      ; JMP XREF from 0x0047b8f2 (fcn.0047b8a0)
|   `-----> 0x0047bb09      e8b28dfaff     call fcn.004248c0
|    :||:   0x0047bb0e      0f0b           ud2
|    :||:      ; JMP XREF from 0x0047b8da (fcn.0047b8a0)
|    :`---> 0x0047bb10      488d054c9802.  lea rax, 0x004a5363         ; "usage:"
|    : |:   0x0047bb17      4889442478     mov qword [local_78h], rax
|    : |:   0x0047bb1c      48c784248000.  mov qword [local_80h], 6
|    : |:   0x0047bb28      488d056a9602.  lea rax, 0x004a5199         ; "flag"
|    : |:   0x0047bb2f      4889442468     mov qword [local_68h], rax
|    : |:   0x0047bb34      48c744247004.  mov qword [local_70h], 4
|    : |:   0x0047bb3d      488dbc24b800.  lea rdi, [local_b8h]        ; 0xb8 ; 184
|    : |:   0x0047bb45      0f57c0         xorps xmm0, xmm0
|    : |:   0x0047bb48      4883c7f0       add rdi, 0xfffffffffffffff0
|    : |:   0x0047bb4c      48896c24f0     mov qword [rsp - 0x10], rbp
|    : |:   0x0047bb51      488d6c24f0     lea rbp, [rsp - 0x10]
|    : |:   0x0047bb56      e8851afdff     call fcn.0044d5e0
|    : |:   0x0047bb5b      488b6d00       mov rbp, qword [rbp]
|    : |:   0x0047bb5f      488d051ae200.  lea rax, 0x00489d80
|    : |:   0x0047bb66      48890424       mov qword [rsp], rax
|    : |:   0x0047bb6a      488d4c2478     lea rcx, [local_78h]        ; 0x78 ; 'x' ; 120
|    : |:   0x0047bb6f      48894c2408     mov qword [local_8h], rcx
|    : |:   0x0047bb74      e877fdf8ff     call fcn.0040b8f0
|    : |:   0x0047bb79      488b442410     mov rax, qword [local_10h]  ; [0x10:8]=-1 ; 16
|    : |:   0x0047bb7e      488b4c2418     mov rcx, qword [local_18h]  ; [0x18:8]=-1 ; 24
|    : |:   0x0047bb83      48898424b800.  mov qword [local_b8h], rax
|    : |:   0x0047bb8b      48898c24c000.  mov qword [local_c0h], rcx
|    : |:   0x0047bb93      488b052eea07.  mov rax, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
|    : |:   0x0047bb9a      488b0d1fea07.  mov rcx, qword [0x004fa5c0] ; [0x4fa5c0:8]=0
|    : |:   0x0047bba1      4885c0         test rax, rax
|    :,===< 0x0047bba4      0f8698000000   jbe 0x47bc42
|    :||:   0x0047bbaa      48894c2408     mov qword [local_8h], rcx
|    :||:   0x0047bbaf      488d05cae100.  lea rax, 0x00489d80
|    :||:   0x0047bbb6      48890424       mov qword [rsp], rax
|    :||:   0x0047bbba      e831fdf8ff     call fcn.0040b8f0
|    :||:   0x0047bbbf      488b442410     mov rax, qword [local_10h]  ; [0x10:8]=-1 ; 16
|    :||:   0x0047bbc4      488b4c2418     mov rcx, qword [local_18h]  ; [0x18:8]=-1 ; 24
|    :||:   0x0047bbc9      48898424c800.  mov qword [local_c8h], rax
|    :||:   0x0047bbd1      48898c24d000.  mov qword [local_d0h], rcx
|    :||:   0x0047bbd9      488d05a0e100.  lea rax, 0x00489d80
|    :||:   0x0047bbe0      48890424       mov qword [rsp], rax
|    :||:   0x0047bbe4      488d4c2468     lea rcx, [local_68h]        ; 0x68 ; 'h' ; 104
|    :||:   0x0047bbe9      48894c2408     mov qword [local_8h], rcx
|    :||:   0x0047bbee      e8fdfcf8ff     call fcn.0040b8f0
|    :||:   0x0047bbf3      488b442418     mov rax, qword [local_18h]  ; [0x18:8]=-1 ; 24
|    :||:   0x0047bbf8      488b4c2410     mov rcx, qword [local_10h]  ; [0x10:8]=-1 ; 16
|    :||:   0x0047bbfd      48898c24d800.  mov qword [local_d8h], rcx
|    :||:   0x0047bc05      48898424e000.  mov qword [local_e0h], rax
|    :||:   0x0047bc0d      488d8424b800.  lea rax, [local_b8h]        ; 0xb8 ; 184
|    :||:   0x0047bc15      48890424       mov qword [rsp], rax
|    :||:   0x0047bc19      48c744240803.  mov qword [local_8h], 3
|    :||:   0x0047bc22      48c744241003.  mov qword [local_10h], 3
|    :||:   0x0047bc2b      e8308cffff     call fcn.00474860
|    :||:   0x0047bc30      48c704240100.  mov qword [rsp], 1
|    :||:   0x0047bc38      e8731cfeff     call fcn.0045d8b0
|    `====< 0x0047bc3d      e99efcffff     jmp 0x47b8e0
|     ||:      ; JMP XREF from 0x0047bba4 (fcn.0047b8a0)
|     `---> 0x0047bc42      e8798cfaff     call fcn.004248c0
|      |:   0x0047bc47      0f0b           ud2
|      |:      ; JMP XREF from 0x0047b8b2 (fcn.0047b8a0)
|      `--> 0x0047bc49      e872f3fcff     call fcn.0044afc0
\       `=< 0x0047bc4e      e94dfcffff     jmp fcn.0047b8a0

根据我们的分析(详见注释),密码判断逻辑应该如下:

for (int i=0; i<length(provided_flag[i]); i++) {
    if (main_mapanic(provided_flag[i]) != constant_binary_blob[i]) {
        badboy();
        exit();
    }
    goodboy();
}

如果要硬着头皮调试的话当然也可以,但我们这里采取暴力破解的办法。还记得章节 5.2 里说的 pin 吗,”由于程序具有循环、分支等结构,每次运行时执行的指令数量不一定相同,于是我们可是使用 Pin 来统计执行指令的数量,从而对程序进行分析”。这里就是这样,程序对输入的密码逐位判断,如果错误,就跳出来,所以根据我们密码正确字节数的不同,程序会执行有明显差异的次数。我们还讲过一个官方示例 inscount0.cpp,我们针对这一题稍微做一点修改,如下:

#include <iostream>
#include <fstream>
#include "pin.H"

ofstream OutFile;

// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;

// This function is called before every instruction is executed
VOID docount(void *ip) {
    if ((long int)ip == 0x0047b96e) icount++;   // 0x0047b960: compare mapanic(provided_flag[i]) with constant_binary_blob[i]
}

// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
    // Insert a call to docount before every instruction, no arguments are passed
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END);  // IARG_INST_PTR: Type: ADDRINT. The address of the instrumented instruction.
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
    "o", "inscount.out", "specify output file name");

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
    // Write to a file since cout and cerr maybe closed by the application
    OutFile.setf(ios::showbase);
    OutFile << "Count " << icount << endl;
    OutFile.close();
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool counts the number of dynamic instructions executed" << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
/*   argc, argv are the entire command line: pin -t <toolname> -- ...    */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    OutFile.open(KnobOutputFile.Value().c_str());

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);

    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);

    // Start the program, never returns
    PIN_StartProgram();

    return 0;
}

主要是修改了两个地方:

// This function is called before every instruction is executed
VOID docount(void *ip) {
    if ((long int)ip == 0x0047b96e) icount++;   // 0x0047b960: compare mapanic(provided_flag[i]) with constant_binary_blob[i]
}

该函数会在每条指令执行之前被调用,判断是否是我们需要的 0x0047b96e 地址处的指令。

然后由于函数 docount 需要一个参数,所以 Instruction 函数也要修改,加入指令的地址 IARG_INST_PTR:

// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
    // Insert a call to docount before every instruction, no arguments are passed
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END);  // IARG_INST_PTR: Type: ADDRINT. The address of the instrumented instruction.
}

好,接下来 make 并执行。其实我们是知道 flag 结构的,”hxp{...}“ ,总共 42 个字节。

$ cp dont_panic.cpp source/tools/MyPintool
[MyPinTool]$ make obj-intel64/dont_panic.so TARGET=intel64
[MyPinTool]$ ../../../pin -t obj-intel64/dont_panic.so -o inscount.out -- ~/dont_panic "hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}" ; cat inscount.out
Nope.
Count 5

注意,这里的 5 是执行次数,匹配正确的个数是 5-1=4,即 "hxp{"。但是最后一次是例外,因为完全匹配成功后直接跳转返回,不会再进行匹配。

和预期结果一样,下面写个脚本来自动化这一过程:

import os

def get_count(flag):
    os.system("../../../pin -t obj-intel64/dont_panic.so -o inscount.out -- ~/dont_panic " + "\"" + flag + "\"")
    with open("inscount.out") as f:
        count = int(f.read().split(" ")[1])
    return count

charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*'"

flag = list("hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}")
count = 0
while count != 42:
    for i in range(4, 41):  # only compare "a" in "hex{}"
        for c in charset:
            flag[i] = c
            # print("".join(flag))
            count = get_count("".join(flag))
            if count == i+2:
                break
    print("".join(flag))

可惜就是速度有点慢,大概跑了一个小时吧。。。

hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}
$ ./dont_panic "hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}"
Seems like you got a flag...

参考资料里的 gdb 脚本就快得多:

import gdb

CHAR_SUCCESS = 0x47B976
NOPE = 0x47BA23
gdb.execute("set pagination off")
gdb.execute("b*0x47B976") #Success for a given character
gdb.execute("b*0x47BA23") #Block displaying "Nope"
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*{}'"
flag = list('A'*42) #junk
for i in range(0,len(flag)) :
	for c in charset:
		flag[i] = c
		# the number of times we need to hit the
		# success bp for the previous correct characters
		success_hits = i
		gdb.execute("r " + '"' + "".join(flag) + '"')
		while success_hits > 0 :
			gdb.execute('c')
			success_hits -= 1
		#we break either on success or on fail
		rip = int(gdb.parse_and_eval("$rip"))
		if rip == CHAR_SUCCESS:
			break #right one. To the next character
		if rip == NOPE: #added for clarity
			continue
print("".join(flag))

在最后一篇参考资料里,介绍了怎样还原 Go 二进制文件的函数名,这将大大简化我们的分析。

参考资料

Pin Tutorial
Reversing GO binaries like a pro
HXP CTF 2017 - "dont_panic" Reversing 100 Writeup
write-up for dont_panic
下载文件
题目解析
参考资料