diff --git a/SUMMARY.md b/SUMMARY.md index 18dcdbc..03f97ed 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -170,6 +170,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [6.2.5 re PicoCTF2014 Baleful](doc/6.2.5_re_picoctf2014_baleful.md) * [6.2.6 re SECCONCTF2017 printf_machine](doc/6.2.6_re_secconctf2017_printf_machine.md) * [6.2.7 re CodegateCTF2018 RedVelvet](doc/6.2.7_re_codegatectf2018_redvelvet.md) + * [6.2.8 re DefcampCTF2015 entry_language](doc/6.2.8_re_defcampctf2015_entry_language.md) * Web * [6.3.1 web HCTF2017 babycrack](doc/6.3.1_web_hctf2017_babycrack.md) * Crypto @@ -212,6 +213,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [8.22 Practical Memory Checking With Dr. Memory](doc/8.22_memory_checking.md) * [8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses](doc/8.23_current_anti-rop.md) * [8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization](doc/8.24_runtime_re-randomization.md) + * [8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis](doc/8.25_offsensive_techniques.md) * [九、附录](doc/9_appendix.md) * [9.1 更多 Linux 工具](doc/9.1_Linuxtools.md) * [9.2 更多 Windows 工具](doc/9.2_wintools.md) diff --git a/doc/1.4.3_javascript_basic.md b/doc/1.4.3_javascript_basic.md index 68a35de..2b1e4ef 100644 --- a/doc/1.4.3_javascript_basic.md +++ b/doc/1.4.3_javascript_basic.md @@ -203,7 +203,7 @@ node hello.js ``` 来执行文件。 -![](../pic/1.4.3_nodejs) +![](../pic/1.4.3_nodejs.png) ## Node.js 模块 diff --git a/doc/2.3.2_ollydbg.md b/doc/2.3.2_ollydbg.md index 59d76e1..04e690f 100644 --- a/doc/2.3.2_ollydbg.md +++ b/doc/2.3.2_ollydbg.md @@ -9,11 +9,14 @@ - F2:在光标选定位置按 F2 键设置或取消断点。 - F4:运行到光标选定位置处暂停。 - F7:单步步入:每次执行一条指令,遇到 call 等子程序时进入其中。 +- Shift+F7:与 F7 相同,但当调试程序发生异常而中止时,调试器首先尝试步入 - F8:单步步过,每次执行一条指令,遇到 call 等子程序时不进入其中。 - F9:运行,被调试软件继续运行,直到遇到下一个断点。 +- Ctrl+F2:重新启动被调试程序。 - Ctrl+F9:执行到返回,在执行到一个 ret 指令时暂停,常用于从当前函数快速返回到上一个函数。 -- Alt+F9:执行到用户代码,可用于从系统部分快速返回到被调试程序部分。 - Ctrl+G:查看任意地址的数据。 +- Alt+F9:执行到用户代码,可用于从系统部分快速返回到被调试程序部分。 +- Alt+F5:让 OllyDbg 窗口总在最前面。 ## 命令行插件 diff --git a/doc/3.1.7_heap_exploit_2.md b/doc/3.1.7_heap_exploit_2.md index 5dbaf3f..bf378b1 100644 --- a/doc/3.1.7_heap_exploit_2.md +++ b/doc/3.1.7_heap_exploit_2.md @@ -821,12 +821,12 @@ p4: 0x1e2b0a0 ~ 0x1e2b8e0 p3: 0x1e2b130 ~ 0x1e2b530 If we memset(p4, 'B', 0xd0), we have: -p4 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa -p3 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +p4 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +p3 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa If we memset(p3, 'C', 0x50), we have: -p4 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa -p3 = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +p4 = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa +p3 = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa ``` 这个比较简单,就是堆块重叠的问题。通过一个溢出漏洞,改写 unsorted bin 中空闲堆块的 size,改变下一次 malloc 可以返回的堆块大小。 diff --git a/doc/4.5_defense_rop.md b/doc/4.5_defense_rop.md index 6ef623f..9542777 100644 --- a/doc/4.5_defense_rop.md +++ b/doc/4.5_defense_rop.md @@ -50,7 +50,7 @@ blx r5 由于 update-load-branch 指令序列相比 return 指令更加稀少,所以需要把它作为 trampoline 重复利用。在构造 ROP 链时,选择以 trampoline 为目标的间接跳转指令结束的指令序列。当一个 gadget 执行结束后,跳转到 trampoline,trampoline 更新程序全局状态,并将程序控制交给下一个 gadget,这样就形成了 ROP 链。 -![](../pic/8.1.2_rop_without_ret.png) +![](../pic/8.2_rop_without_ret.png) ## 参考资料 diff --git a/doc/5.2.3_valgrind.md b/doc/5.2.3_valgrind.md index fdd1f50..3adceda 100644 --- a/doc/5.2.3_valgrind.md +++ b/doc/5.2.3_valgrind.md @@ -1 +1,25 @@ # 5.2.3 Valgrind + +- [简介](#简介) +- [使用方法](#使用方法) +- [VEX IR](#vex-ir) +- [参考资料](#参考资料) + + +## 简介 +Valgrind 是一个用于内存调试、内存泄漏检测以及性能分析的动态二进制插桩工具。Valgrind 由 core 以及基于 core 的其他调试工具组成。core 类似于一个框架,它模拟了一个 CPU 环境,并提供服务给其他工具,而其他工具以插件的形式利用 core 提供的服务完成各种特定的任务。 + + +## 使用方法 + + +## VEX IR +VEX IR 是 Valgrind 所使用的中间表示,供 DBI 使用,后来这一部分被分离出去作为 libVEX,libVEX 负责将机器码转换成 VEX IR,转换结果存放在 cache 中。 + +顺便,再简单提一下其他的类似用途的 IR 还有:BAP、REIL、LLVM、TCG 等。 + + +## 参考资料 +- [Valgrind: A Framework for Heavyweight Dynamic Binary Instrumentation](http://valgrind.org/docs/valgrind2007.pdf) +- [Optimizing Binary Code Produced by Valgrind](https://pdfs.semanticscholar.org/6761/acf36975d38fd5f616cb4798bfa3a92cbfa3.pdf) +- [libvex_ir.h](https://github.com/angr/vex/blob/dev/pub/libvex_ir.h) diff --git a/doc/5.3.1_angr.md b/doc/5.3.1_angr.md index a4ae15b..65b8509 100644 --- a/doc/5.3.1_angr.md +++ b/doc/5.3.1_angr.md @@ -1,13 +1,18 @@ # 5.3.1 angr +- [简介](#简介) - [安装](#安装) -- [使用 angr](#使用-angr) - - [入门](#入门) - - [加载二进制文件](#加载二进制文件) -- [angr 在 CTF 中的运用](#angr-在-ctf-中的运用) +- [使用方法](#使用方法) + - [快速入门](#快速入门) + - [二进制文件加载器](#二进制文件加载器) + - [求解器引擎](#求解器引擎) + - [VEX IR 翻译器](#vex-ir-翻译) + - [体系结构信息收集](#体系结构信息收集) +- [CTF 实例](#ctf-实例) - [参考资料](#参考资料) +## 简介 [angr](https://github.com/angr/angr) 是一个多架构的二进制分析平台,具备对二进制文件的动态符号执行能力和多种静态分析能力。在近几年的 CTF 中也大有用途。 @@ -29,7 +34,7 @@ $ sudo apt install python-dev libffi-dev build-essential virtualenvwrapper 4. cle 5. angr ``` -如: +例如下面这样: ```shell $ git clone https://github.com/angr/claripy $ cd claripy @@ -37,13 +42,11 @@ $ sudo pip install -r requirements.txt $ sudo python setup.py build $ sudo python setup.py install ``` -其他几个库也是一样的。 - 安装过程中可能会有一些奇怪的错误,可以到官方文档中查看。 -## 使用 angr -#### 入门 +## 使用方法 +#### 快速入门 使用 angr 的第一步是新建一个工程,几乎所有的操作都是围绕这个工程展开的: ```python >>> import angr @@ -111,7 +114,7 @@ WARNING | 2017-12-08 11:09:28,629 | cle.loader | The main binary is a position-i >>> block.capstone >>> block.capstone.pp() ->>> + >>> block.vex >>> block.vex.pp() @@ -201,7 +204,7 @@ WARNING | 2017-12-08 11:09:28,629 | cle.loader | The main binary is a position-i ``` #### 加载二进制文件 -angr 的二进制加载模块称为 CLE。主类为 `cle.loader.Loader`,它导入所有的对象文件并导出一个进程内存的抽象。类 `cle.backends` 是加载器的后端,根据二进制文件类型区分为 `cle.backends.elf`、`cle.backends.pe`、`cle.backends.macho` 等。 +我们知道 angr 是高度模块化的,接下来我们就分别来看看这些组成模块,其中用于二进制加载模块称为 CLE。主类为 `cle.loader.Loader`,它导入所有的对象文件并导出一个进程内存的抽象。类 `cle.backends` 是加载器的后端,根据二进制文件类型区分为 `cle.backends.elf`、`cle.backends.pe`、`cle.backends.macho` 等。 加载对象文件和细分类型如下: ```python @@ -234,184 +237,63 @@ angr 的二进制加载模块称为 CLE。主类为 `cle.loader.Loader`,它导 <.text | offset 0x12f0, vaddr 0x4012f0, size 0x33c9> ``` +#### 求解器引擎 -## angr 在 CTF 中的运用 -#### re DefcampCTF2015 entry_language -这是一题标准的密码验证题,输入一个字符串,程序验证对误。 -``` -$ file entry_language -defcamp_r100: 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.24, BuildID[sha1]=0f464824cc8ee321ef9a80a799c70b1b6aec8168, stripped -``` -``` -$ ./entry_language -Enter the password: ABCD -Incorrect password! -``` +#### VEX IR 翻译器 +angr 使用了 VEX 作为二进制分析的中间表示。VEX IR 是由 Valgrind 项目开发和使用的中间表示,后来这一部分被分离出去作为 libVEX,libVEX 用于将机器码转换成 VEX IR(更多内容参考章节5.2.3)。在 angr 项目中,开发了模块 [PyVEX](https://github.com/angr/pyvex) 作为 libVEX 的 Python 包装。当然也对 libVEX 做了一些修改,使其更加适用于程序分析。 -为了与 angr 的自动化做对比,我们先使用传统的方法,逆向算法求解,`main` 函数和验证函数 `fcn.004006fd` 如下: -``` -[0x00400610]> pdf @ main -/ (fcn) main 153 -| main (); -| ; var int local_110h @ rbp-0x110 -| ; var int local_8h @ rbp-0x8 -| ; DATA XREF from 0x0040062d (entry0) -| 0x004007e8 55 push rbp -| 0x004007e9 4889e5 mov rbp, rsp -| 0x004007ec 4881ec100100. sub rsp, 0x110 -| 0x004007f3 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40 -| 0x004007fc 488945f8 mov qword [local_8h], rax -| 0x00400800 31c0 xor eax, eax -| 0x00400802 bf37094000 mov edi, str.Enter_the_password: ; 0x400937 ; "Enter the password: " -| 0x00400807 b800000000 mov eax, 0 -| 0x0040080c e8affdffff call sym.imp.printf ; int printf(const char *format) -| 0x00400811 488b15500820. mov rdx, qword [obj.stdin] ; [0x601068:8]=0 -| 0x00400818 488d85f0feff. lea rax, [local_110h] -| 0x0040081f beff000000 mov esi, 0xff ; 255 -| 0x00400824 4889c7 mov rdi, rax -| 0x00400827 e8b4fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream) -| 0x0040082c 4885c0 test rax, rax -| ,=< 0x0040082f 7435 je 0x400866 -| | 0x00400831 488d85f0feff. lea rax, [local_110h] -| | 0x00400838 4889c7 mov rdi, rax -| | 0x0040083b e8bdfeffff call fcn.004006fd ; 调用验证函数 -| | 0x00400840 85c0 test eax, eax -| ,==< 0x00400842 7511 jne 0x400855 -| || 0x00400844 bf4c094000 mov edi, str.Nice_ ; 0x40094c ; "Nice!" -| || 0x00400849 e852fdffff call sym.imp.puts ; int puts(const char *s) -| || 0x0040084e b800000000 mov eax, 0 -| ,===< 0x00400853 eb16 jmp 0x40086b -| ||| ; JMP XREF from 0x00400842 (main) -| |`--> 0x00400855 bf52094000 mov edi, str.Incorrect_password_ ; 0x400952 ; "Incorrect password!" -| | | 0x0040085a e841fdffff call sym.imp.puts ; int puts(const char *s) -| | | 0x0040085f b801000000 mov eax, 1 -| |,==< 0x00400864 eb05 jmp 0x40086b -| ||| ; JMP XREF from 0x0040082f (main) -| ||`-> 0x00400866 b800000000 mov eax, 0 -| || ; JMP XREF from 0x00400864 (main) -| || ; JMP XREF from 0x00400853 (main) -| ``--> 0x0040086b 488b4df8 mov rcx, qword [local_8h] -| 0x0040086f 6448330c2528. xor rcx, qword fs:[0x28] -| ,=< 0x00400878 7405 je 0x40087f -| | 0x0040087a e831fdffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) -| | ; JMP XREF from 0x00400878 (main) -| `-> 0x0040087f c9 leave -\ 0x00400880 c3 ret -[0x00400610]> pdf @ fcn.004006fd -/ (fcn) fcn.004006fd 171 -| fcn.004006fd (int arg_bh); -| ; var int local_38h @ rbp-0x38 -| ; var int local_24h @ rbp-0x24 -| ; var int local_20h @ rbp-0x20 -| ; var int local_18h @ rbp-0x18 -| ; var int local_10h @ rbp-0x10 -| ; arg int arg_bh @ rbp+0xb -| ; CALL XREF from 0x0040083b (main) -| 0x004006fd 55 push rbp -| 0x004006fe 4889e5 mov rbp, rsp -| 0x00400701 48897dc8 mov qword [local_38h], rdi -| 0x00400705 c745dc000000. mov dword [local_24h], 0 -| 0x0040070c 48c745e01409. mov qword [local_20h], str.Dufhbmf ; 0x400914 ; "Dufhbmf" -| 0x00400714 48c745e81c09. mov qword [local_18h], str.pG_imos ; 0x40091c ; "pG`imos" -| 0x0040071c 48c745f02409. mov qword [local_10h], str.ewUglpt ; 0x400924 ; "ewUglpt" -| 0x00400724 c745dc000000. mov dword [local_24h], 0 -| ,=< 0x0040072b eb6e jmp 0x40079b -| | ; JMP XREF from 0x0040079f (fcn.004006fd) -| .--> 0x0040072d 8b4ddc mov ecx, dword [local_24h] -| :| 0x00400730 ba56555555 mov edx, 0x55555556 -| :| 0x00400735 89c8 mov eax, ecx -| :| 0x00400737 f7ea imul edx -| :| 0x00400739 89c8 mov eax, ecx -| :| 0x0040073b c1f81f sar eax, 0x1f -| :| 0x0040073e 29c2 sub edx, eax -| :| 0x00400740 89d0 mov eax, edx -| :| 0x00400742 01c0 add eax, eax -| :| 0x00400744 01d0 add eax, edx -| :| 0x00400746 29c1 sub ecx, eax -| :| 0x00400748 89ca mov edx, ecx -| :| 0x0040074a 4863c2 movsxd rax, edx -| :| 0x0040074d 488b74c5e0 mov rsi, qword [rbp + rax*8 - 0x20] -| :| 0x00400752 8b4ddc mov ecx, dword [local_24h] -| :| 0x00400755 ba56555555 mov edx, 0x55555556 -| :| 0x0040075a 89c8 mov eax, ecx -| :| 0x0040075c f7ea imul edx -| :| 0x0040075e 89c8 mov eax, ecx -| :| 0x00400760 c1f81f sar eax, 0x1f -| :| 0x00400763 29c2 sub edx, eax -| :| 0x00400765 89d0 mov eax, edx -| :| 0x00400767 01c0 add eax, eax -| :| 0x00400769 4898 cdqe -| :| 0x0040076b 4801f0 add rax, rsi ; '+' -| :| 0x0040076e 0fb600 movzx eax, byte [rax] -| :| 0x00400771 0fbed0 movsx edx, al -| :| 0x00400774 8b45dc mov eax, dword [local_24h] -| :| 0x00400777 4863c8 movsxd rcx, eax -| :| 0x0040077a 488b45c8 mov rax, qword [local_38h] -| :| 0x0040077e 4801c8 add rax, rcx ; '&' -| :| 0x00400781 0fb600 movzx eax, byte [rax] -| :| 0x00400784 0fbec0 movsx eax, al -| :| 0x00400787 29c2 sub edx, eax -| :| 0x00400789 89d0 mov eax, edx -| :| 0x0040078b 83f801 cmp eax, 1 ; 1 -| ,===< 0x0040078e 7407 je 0x400797 ; = 1 时跳转,验证成功 -| |:| 0x00400790 b801000000 mov eax, 1 ; 返回 1,验证失败 -| ,====< 0x00400795 eb0f jmp 0x4007a6 -| ||:| ; JMP XREF from 0x0040078e (fcn.004006fd) -| |`---> 0x00400797 8345dc01 add dword [local_24h], 1 ; i = i + 1 -| | :| ; JMP XREF from 0x0040072b (fcn.004006fd) -| | :`-> 0x0040079b 837ddc0b cmp dword [local_24h], 0xb ; [0xb:4]=-1 ; 11 -| | `==< 0x0040079f 7e8c jle 0x40072d ; i <= 11 时跳转 -| | 0x004007a1 b800000000 mov eax, 0 ; 返回 0 -| | ; JMP XREF from 0x00400795 (fcn.004006fd) -| `----> 0x004007a6 5d pop rbp -\ 0x004007a7 c3 ret -``` +一些用法如下: +```python +>>> import pyvex, archinfo +>>> bb = pyvex.IRSB('\xc3', 0x400400, archinfo.ArchAMD64()) # 将一个位于 0x400400 的 AMD64 基本块(\xc3,即ret)转成 VEX +>>> bb.pp() # 打印 +IRSB { + t0:Ity_I64 t1:Ity_I64 t2:Ity_I64 t3:Ity_I64 -整理后可以得到下面的伪代码: -```C -int fcn_004006fd(int *passwd) { - char *str_1 = "Dufhbmf"; - char *str_2 = "pG`imos"; - char *str_3 = "ewUglpt"; - for (int i = 0; i <= 11; i++) { - if((&str_3)[i % 3][2 * (1 / 3)] - *(i + passwd) != 1) { - return 1; - } - } - return 0; + 00 | ------ IMark(0x400400, 1, 0) ------ + 01 | t0 = GET:I64(rsp) + 02 | t1 = LDle:I64(t0) + 03 | t2 = Add64(t0,0x0000000000000008) + 04 | PUT(rsp) = t2 + 05 | t3 = Sub64(t2,0x0000000000000080) + 06 | ====== AbiHint(0xt3, 128, t1) ====== + NEXT: PUT(rip) = t1; Ijk_Ret } -``` -然后写出逆向脚本: -```python -str_list = ["Dufhbmf", "pG`imos", "ewUglpt"] -passwd = [] -for i in range(12): - passwd.append(chr(ord(str_list[i % 3][2 * (i / 3)]) - 1)) -print ''.join(passwd) + +>>> bb.statements[3] # 表达式 + +>>> bb.statements[3].pp() +t2 = Add64(t0,0x0000000000000008) + +>>> bb.statements[3].data # 数据 + +>>> bb.statements[3].data.pp() +Add64(t0,0x0000000000000008) + +>>> bb.statements[3].data.op # 操作符 +'Iop_Add64' + +>>> bb.statements[3].data.args # 参数 +[, ] +>>> bb.statements[3].data.args[0] + +>>> bb.statements[3].data.args[0].pp() +t0 + +>>> bb.next # 基本块末尾无条件跳转的目标 + +>>> bb.next.pp() +t1 + +>>> bb.jumpkind # 无条件跳转的类型 +'Ijk_Ret' ``` -逆向算法似乎也很简单,但如果连算法都不用逆的话,下面就是见证 angr 魔力的时刻,我们只需要指定让程序运行到 `0x400844`,即验证通过时的位置,而不用管验证的逻辑是怎么样的。完整的 exp 如下,其他文件在 [github](../src/others/5.3.1_angr) 相应文件夹中。 -```python -import angr +#### 体系结构信息收集 -project = angr.Project("entry_language", auto_load_libs=False) -@project.hook(0x400844) -def print_flag(state): - print "FLAG SHOULD BE:", state.posix.dump_fd(0) - project.terminate_execution() - -project.execute() -``` - -Bingo!!! -``` -$ python2 exp_angr.py -FLAG SHOULD BE: Code_Talkers -$ ./entry_language -Enter the password: Code_Talkers -Nice! -``` +## CTF 实例 +查看章节 6.2.8。 ## 参考资料 diff --git a/doc/6.1.18_pwn_hitbctf2017_sentosa.md b/doc/6.1.18_pwn_hitbctf2017_sentosa.md index 0d4ee5f..67939f5 100644 --- a/doc/6.1.18_pwn_hitbctf2017_sentosa.md +++ b/doc/6.1.18_pwn_hitbctf2017_sentosa.md @@ -307,9 +307,9 @@ projects 位于 `0x00202040`,proj_num 位于 `0x002020c0`。 | | ; JMP XREF from 0x00000c67 (sub.read_bf0) \ `---> 0x00000c8e call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) ``` -正如我们一开始猜测的,这个函数是有问题的,如果输入 0 作为 length,则 length-1(能读入的实际长度) 后得到一个负数,在循环判断时,负数永远不会等于一个正数,于是将读入任意长度的字符串(以'\n'结尾),造成缓冲区溢出。 +正如我们一开始猜测的,这个函数是有问题的,如果输入 0 作为 length,则 length-1(能读入的实际长度) 后得到一个负数,在循环判断时,负数永远不会等于一个正数,于是将读入任意长度的字符串(以`\n`结尾),造成缓冲区溢出。 -字符串末尾会被加上 '\x00',且开启了 Canary,暂时还没想到如何利用,继续往下看。另外特别注意 malloc 后得到的 project 的地址存放在 `rsp + 0x6a` 的位置。 +字符串末尾会被加上 `\x00`,且开启了 Canary,暂时还没想到如何利用,继续往下看。另外特别注意 malloc 后得到的 project 的地址存放在 `rsp + 0x6a` 的位置。 #### View all projects ``` diff --git a/doc/6.1.19_pwn_hitbctf2018_gundam.md b/doc/6.1.19_pwn_hitbctf2018_gundam.md index fd36749..7fa1cb0 100644 --- a/doc/6.1.19_pwn_hitbctf2018_gundam.md +++ b/doc/6.1.19_pwn_hitbctf2018_gundam.md @@ -315,7 +315,7 @@ struct gundam *factory[9]; ``` 另外 gundam->name 指向一块 0x100 大小的空间。gundam 的数量存放在 `0x0020208c`。 -从读入 name 的操作中我们发现,程序并没有在末尾设置 '\x00',可能导致信息泄漏(以'\x0a'结尾)。 +从读入 name 的操作中我们发现,程序并没有在末尾设置 `\x00`,可能导致信息泄漏(以`\x0a`结尾)。 #### Visit gundams ``` @@ -612,7 +612,7 @@ gdb-peda$ x/6gx 0x555555757b50-0x10 0x555555757b50: 0x0a41414141414141 0x00007ffff7dd2c78 0x555555757b60: 0x0000000000000000 0x0000000000000000 ``` -可以看到程序并没有在字符串后加 '\x00' 隔断,所以可以将 unsorted bin 的地址泄漏出来,然后通过计算得到 libc 基址。 +可以看到程序并没有在字符串后加 `\x00` 隔断,所以可以将 unsorted bin 的地址泄漏出来,然后通过计算得到 libc 基址。 ``` [*] libc base: 0x7ffff79f8000 diff --git a/doc/6.1.21_pwn_hitconctf2016_secret_holder.md b/doc/6.1.21_pwn_hitconctf2016_secret_holder.md index dedf7d3..eae77c6 100644 --- a/doc/6.1.21_pwn_hitconctf2016_secret_holder.md +++ b/doc/6.1.21_pwn_hitconctf2016_secret_holder.md @@ -181,7 +181,7 @@ Which Secret do you want to wipe? | `-> 0x00400a25 leave \ 0x00400a26 ret ``` -果然该函数使用 `calloc()` 为三种 secret 分别了不同大小的 chunk,small secret 属于 small chunk,big secret 和 huge secret 属于 large chunk。在分配前,会检查对应的 secret 是否已经存在,即每种 chunk 只能有一个,chunk 的指针放在 `.bss` 段上。另外其实读入 secret 的逻辑还是有问题的,它没有处理换行符,也没有在字符串末尾加 "\x00"。 +果然该函数使用 `calloc()` 为三种 secret 分别了不同大小的 chunk,small secret 属于 small chunk,big secret 和 huge secret 属于 large chunk。在分配前,会检查对应的 secret 是否已经存在,即每种 chunk 只能有一个,chunk 的指针放在 `.bss` 段上。另外其实读入 secret 的逻辑还是有问题的,它没有处理换行符,也没有在字符串末尾加 `\x00`。 #### Wipe secret ``` diff --git a/doc/6.1.23_pwn_bctf2016_bcloud.md b/doc/6.1.23_pwn_bctf2016_bcloud.md index 14922b1..01f4805 100644 --- a/doc/6.1.23_pwn_bctf2016_bcloud.md +++ b/doc/6.1.23_pwn_bctf2016_bcloud.md @@ -212,9 +212,9 @@ Synchronization success. | 0x08048707 leave \ 0x08048708 ret ``` -乍看之下似乎没有问题,在读入字符串末尾也加上了截断 "\x00"。 +乍看之下似乎没有问题,在读入字符串末尾也加上了截断 `\x00`。 -但是,注意观察读入字符串和 malloc 返回地址在栈上的位置关系。字符串其实地址 `local_5ch`,最多 0x40 个字节,返回地址位于 `local_5ch + 0x40`,所以如果我们正好读入 0x40 字节,则 "\x00" 会被放到 `local_5ch + 0x41` 的位置,然后正好被返回地址给覆盖掉了。由于函数 `strcpy()` 是以 "\x00" 来决定字符串结尾的,所以字符串连上返回地址会被一起复制到堆上。然后又被一起打印出来。于是我们就得到了堆地址。 +但是,注意观察读入字符串和 malloc 返回地址在栈上的位置关系。字符串其实地址 `local_5ch`,最多 0x40 个字节,返回地址位于 `local_5ch + 0x40`,所以如果我们正好读入 0x40 字节,则 `\x00` 会被放到 `local_5ch + 0x41` 的位置,然后正好被返回地址给覆盖掉了。由于函数 `strcpy()` 是以 `\x00` 来决定字符串结尾的,所以字符串连上返回地址会被一起复制到堆上。然后又被一起打印出来。于是我们就得到了堆地址。 继续看函数 `sub.memset_84e`: ``` @@ -303,7 +303,7 @@ Synchronization success. ``` 同样的,Host 的返回地址放在 `local_9ch + 0x88` 的位置,而字符串最多到 `local_9ch + 0x44 + 0x40`,中间还间隔了 0x4 字节,所以不存在漏洞。但是 Org 的返回地址放在 `local_9ch + 0x40`,正好位于字符串的后面,所以存在漏洞。同时 Host 的字符串又正好位于 Org 返回地址的后面,所以 strcpy 会将 Org 字符串,返回地址和 Host 字符串全都复制到 Org 的堆上,造成堆溢出。利用这个堆溢出我们可以修改 top chunk 的 size,即 house-of-force。 -当然这种漏洞有一定的几率不会成功,比如返回地址的低位本来就是 "\x00" 的时候,就恰好截断了。 +当然这种漏洞有一定的几率不会成功,比如返回地址的低位本来就是 `\x00` 的时候,就恰好截断了。 #### New note ``` diff --git a/doc/6.1.3_pwn_xdctf2015_pwn200.md b/doc/6.1.3_pwn_xdctf2015_pwn200.md index 59a1f9c..adfcba8 100644 --- a/doc/6.1.3_pwn_xdctf2015_pwn200.md +++ b/doc/6.1.3_pwn_xdctf2015_pwn200.md @@ -689,7 +689,7 @@ payload_1 += p32(leave_ret_addr) io.send(payload_1) ``` -从这里开始,后面的 paylaod 都是通过 read 函数读入的,所以必须为 100 字节长。首先,调用 write@plt 函数打印出与 base_addr 偏移 80 字节处的字符串 "/bin/sh",以验证栈转移成功。注意由于 `.dynstr` 中的字符串都是以 "\x00" 结尾的,所以伪造字符串为 "bin/sh\x00"。 +从这里开始,后面的 paylaod 都是通过 read 函数读入的,所以必须为 100 字节长。首先,调用 write@plt 函数打印出与 base_addr 偏移 80 字节处的字符串 "/bin/sh",以验证栈转移成功。注意由于 `.dynstr` 中的字符串都是以 `\x00` 结尾的,所以伪造字符串为 `bin/sh\x00`。 ```python payload_2 = "AAAA" # new ebp payload_2 += p32(write_plt) diff --git a/doc/6.2.8_re_defcampctf2015_entry_language.md b/doc/6.2.8_re_defcampctf2015_entry_language.md new file mode 100644 index 0000000..f1b468d --- /dev/null +++ b/doc/6.2.8_re_defcampctf2015_entry_language.md @@ -0,0 +1,190 @@ +# 6.2.8 re DefcampCTF2015 entry_language + +- [题目解析](#题目解析) +- [参考资料](#参考资料) + + +[下载文件](../src/writeup/6.2.8_re_defcampctf2015_entry_language) + +## 题目解析 +这是一题标准的密码验证题,输入一个字符串,程序验证对误。 +``` +$ file entry_language +defcamp_r100: 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.24, BuildID[sha1]=0f464824cc8ee321ef9a80a799c70b1b6aec8168, stripped +``` +``` +$ ./entry_language +Enter the password: ABCD +Incorrect password! +``` + +为了与 angr 的自动化做对比,我们先使用传统的方法,逆向算法求解,`main` 函数和验证函数 `fcn.004006fd` 如下: +``` +[0x00400610]> pdf @ main +/ (fcn) main 153 +| main (); +| ; var int local_110h @ rbp-0x110 +| ; var int local_8h @ rbp-0x8 +| ; DATA XREF from 0x0040062d (entry0) +| 0x004007e8 55 push rbp +| 0x004007e9 4889e5 mov rbp, rsp +| 0x004007ec 4881ec100100. sub rsp, 0x110 +| 0x004007f3 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40 +| 0x004007fc 488945f8 mov qword [local_8h], rax +| 0x00400800 31c0 xor eax, eax +| 0x00400802 bf37094000 mov edi, str.Enter_the_password: ; 0x400937 ; "Enter the password: " +| 0x00400807 b800000000 mov eax, 0 +| 0x0040080c e8affdffff call sym.imp.printf ; int printf(const char *format) +| 0x00400811 488b15500820. mov rdx, qword [obj.stdin] ; [0x601068:8]=0 +| 0x00400818 488d85f0feff. lea rax, [local_110h] +| 0x0040081f beff000000 mov esi, 0xff ; 255 +| 0x00400824 4889c7 mov rdi, rax +| 0x00400827 e8b4fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream) +| 0x0040082c 4885c0 test rax, rax +| ,=< 0x0040082f 7435 je 0x400866 +| | 0x00400831 488d85f0feff. lea rax, [local_110h] +| | 0x00400838 4889c7 mov rdi, rax +| | 0x0040083b e8bdfeffff call fcn.004006fd ; 调用验证函数 +| | 0x00400840 85c0 test eax, eax +| ,==< 0x00400842 7511 jne 0x400855 +| || 0x00400844 bf4c094000 mov edi, str.Nice_ ; 0x40094c ; "Nice!" +| || 0x00400849 e852fdffff call sym.imp.puts ; int puts(const char *s) +| || 0x0040084e b800000000 mov eax, 0 +| ,===< 0x00400853 eb16 jmp 0x40086b +| ||| ; JMP XREF from 0x00400842 (main) +| |`--> 0x00400855 bf52094000 mov edi, str.Incorrect_password_ ; 0x400952 ; "Incorrect password!" +| | | 0x0040085a e841fdffff call sym.imp.puts ; int puts(const char *s) +| | | 0x0040085f b801000000 mov eax, 1 +| |,==< 0x00400864 eb05 jmp 0x40086b +| ||| ; JMP XREF from 0x0040082f (main) +| ||`-> 0x00400866 b800000000 mov eax, 0 +| || ; JMP XREF from 0x00400864 (main) +| || ; JMP XREF from 0x00400853 (main) +| ``--> 0x0040086b 488b4df8 mov rcx, qword [local_8h] +| 0x0040086f 6448330c2528. xor rcx, qword fs:[0x28] +| ,=< 0x00400878 7405 je 0x40087f +| | 0x0040087a e831fdffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) +| | ; JMP XREF from 0x00400878 (main) +| `-> 0x0040087f c9 leave +\ 0x00400880 c3 ret +[0x00400610]> pdf @ fcn.004006fd +/ (fcn) fcn.004006fd 171 +| fcn.004006fd (int arg_bh); +| ; var int local_38h @ rbp-0x38 +| ; var int local_24h @ rbp-0x24 +| ; var int local_20h @ rbp-0x20 +| ; var int local_18h @ rbp-0x18 +| ; var int local_10h @ rbp-0x10 +| ; arg int arg_bh @ rbp+0xb +| ; CALL XREF from 0x0040083b (main) +| 0x004006fd 55 push rbp +| 0x004006fe 4889e5 mov rbp, rsp +| 0x00400701 48897dc8 mov qword [local_38h], rdi +| 0x00400705 c745dc000000. mov dword [local_24h], 0 +| 0x0040070c 48c745e01409. mov qword [local_20h], str.Dufhbmf ; 0x400914 ; "Dufhbmf" +| 0x00400714 48c745e81c09. mov qword [local_18h], str.pG_imos ; 0x40091c ; "pG`imos" +| 0x0040071c 48c745f02409. mov qword [local_10h], str.ewUglpt ; 0x400924 ; "ewUglpt" +| 0x00400724 c745dc000000. mov dword [local_24h], 0 +| ,=< 0x0040072b eb6e jmp 0x40079b +| | ; JMP XREF from 0x0040079f (fcn.004006fd) +| .--> 0x0040072d 8b4ddc mov ecx, dword [local_24h] +| :| 0x00400730 ba56555555 mov edx, 0x55555556 +| :| 0x00400735 89c8 mov eax, ecx +| :| 0x00400737 f7ea imul edx +| :| 0x00400739 89c8 mov eax, ecx +| :| 0x0040073b c1f81f sar eax, 0x1f +| :| 0x0040073e 29c2 sub edx, eax +| :| 0x00400740 89d0 mov eax, edx +| :| 0x00400742 01c0 add eax, eax +| :| 0x00400744 01d0 add eax, edx +| :| 0x00400746 29c1 sub ecx, eax +| :| 0x00400748 89ca mov edx, ecx +| :| 0x0040074a 4863c2 movsxd rax, edx +| :| 0x0040074d 488b74c5e0 mov rsi, qword [rbp + rax*8 - 0x20] +| :| 0x00400752 8b4ddc mov ecx, dword [local_24h] +| :| 0x00400755 ba56555555 mov edx, 0x55555556 +| :| 0x0040075a 89c8 mov eax, ecx +| :| 0x0040075c f7ea imul edx +| :| 0x0040075e 89c8 mov eax, ecx +| :| 0x00400760 c1f81f sar eax, 0x1f +| :| 0x00400763 29c2 sub edx, eax +| :| 0x00400765 89d0 mov eax, edx +| :| 0x00400767 01c0 add eax, eax +| :| 0x00400769 4898 cdqe +| :| 0x0040076b 4801f0 add rax, rsi ; '+' +| :| 0x0040076e 0fb600 movzx eax, byte [rax] +| :| 0x00400771 0fbed0 movsx edx, al +| :| 0x00400774 8b45dc mov eax, dword [local_24h] +| :| 0x00400777 4863c8 movsxd rcx, eax +| :| 0x0040077a 488b45c8 mov rax, qword [local_38h] +| :| 0x0040077e 4801c8 add rax, rcx ; '&' +| :| 0x00400781 0fb600 movzx eax, byte [rax] +| :| 0x00400784 0fbec0 movsx eax, al +| :| 0x00400787 29c2 sub edx, eax +| :| 0x00400789 89d0 mov eax, edx +| :| 0x0040078b 83f801 cmp eax, 1 ; 1 +| ,===< 0x0040078e 7407 je 0x400797 ; = 1 时跳转,验证成功 +| |:| 0x00400790 b801000000 mov eax, 1 ; 返回 1,验证失败 +| ,====< 0x00400795 eb0f jmp 0x4007a6 +| ||:| ; JMP XREF from 0x0040078e (fcn.004006fd) +| |`---> 0x00400797 8345dc01 add dword [local_24h], 1 ; i = i + 1 +| | :| ; JMP XREF from 0x0040072b (fcn.004006fd) +| | :`-> 0x0040079b 837ddc0b cmp dword [local_24h], 0xb ; [0xb:4]=-1 ; 11 +| | `==< 0x0040079f 7e8c jle 0x40072d ; i <= 11 时跳转 +| | 0x004007a1 b800000000 mov eax, 0 ; 返回 0 +| | ; JMP XREF from 0x00400795 (fcn.004006fd) +| `----> 0x004007a6 5d pop rbp +\ 0x004007a7 c3 ret +``` + +整理后可以得到下面的伪代码: +```C +int fcn_004006fd(int *passwd) { + char *str_1 = "Dufhbmf"; + char *str_2 = "pG`imos"; + char *str_3 = "ewUglpt"; + for (int i = 0; i <= 11; i++) { + if((&str_3)[i % 3][2 * (1 / 3)] - *(i + passwd) != 1) { + return 1; + } + } + return 0; +} +``` +然后写出逆向脚本: +```python +str_list = ["Dufhbmf", "pG`imos", "ewUglpt"] +passwd = [] +for i in range(12): + passwd.append(chr(ord(str_list[i % 3][2 * (i / 3)]) - 1)) +print ''.join(passwd) +``` + +逆向算法似乎也很简单,但如果连算法都不用逆的话,下面就是见证 angr 魔力的时刻,我们只需要指定让程序运行到 `0x400844`,即验证通过时的位置,而不用管验证的逻辑是怎么样的。 + +完整的脚本如下: +```python +import angr + +project = angr.Project("entry_language", auto_load_libs=False) + +@project.hook(0x400844) +def print_flag(state): + print "FLAG SHOULD BE:", state.posix.dump_fd(0) + project.terminate_execution() + +project.execute() +``` + +Bingo!!! +``` +$ python2 exp_angr.py +FLAG SHOULD BE: Code_Talkers +$ ./entry_language +Enter the password: Code_Talkers +Nice! +``` + + +## 参考资料 +- https://ctftime.org/task/1691 diff --git a/doc/6.3.1_web_hctf2017_babycrack.md b/doc/6.3.1_web_hctf2017_babycrack.md index ae8a1ff..d3af73b 100644 --- a/doc/6.3.1_web_hctf2017_babycrack.md +++ b/doc/6.3.1_web_hctf2017_babycrack.md @@ -135,37 +135,7 @@ flag hctf{?\_rev3rse\_iz\_s0\_?} print binascii.a2b_hex(str(hex(strings))[2:-1]) ``` -结果: -``` - qK┒ - h4r - _; - Vn - L钔V - sr姘 - j[瘶 - aDx€ - X-Ah - O - P - u? - l傡 - ck祕 - ZT~b - Q=GJ - w羆 - n? - e掤t - \{籠 - Sd凞 - JMM, - p裦 - g?n - ^ⅧV - U嬃> -``` - -可以看到大多数字符都没有意义,除了 h4r 让人遐想联翩,可惜还是不全,但是结合已经分析出的 flag,猜测应该是 h4rd。 +从结果中可以看到大多数字符都没有意义,除了 h4r 让人遐想联翩,可惜还是不全,但是结合已经分析出的 flag,猜测应该是 h4rd。 flag hctf{??\_rev3rse\_iz\_s0\_h4rd?} ``` diff --git a/doc/6_writeup.md b/doc/6_writeup.md index 014da6a..c33238d 100644 --- a/doc/6_writeup.md +++ b/doc/6_writeup.md @@ -38,6 +38,7 @@ * [6.2.5 re PicoCTF2014 Baleful](6.2.5_re_picoctf2014_baleful.md) * [6.2.6 re SECCONCTF2017 printf_machine](6.2.6_re_secconctf2017_printf_machine.md) * [6.2.7 re CodegateCTF2018 RedVelvet](6.2.7_re_codegatectf2018_redvelvet.md) + * [6.2.8 re DefcampCTF2015 entry_language](6.2.8_re_defcampctf2015_entry_language.md) * Web * [6.3.1 web HCTF2017 babycrack](6.3.1_web_hctf2017_babycrack.md) * Crypto diff --git a/doc/8.25_offsensive_techniques.md b/doc/8.25_offsensive_techniques.md new file mode 100644 index 0000000..522fbb2 --- /dev/null +++ b/doc/8.25_offsensive_techniques.md @@ -0,0 +1 @@ +# 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis diff --git a/doc/8_academic.md b/doc/8_academic.md index b10df89..3b20497 100644 --- a/doc/8_academic.md +++ b/doc/8_academic.md @@ -27,3 +27,4 @@ * [8.22 Practical Memory Checking With Dr. Memory](8.22_memory_checking.md) * [8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses](8.23_current_anti-rop.md) * [8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization](8.24_runtime_re-randomization.md) +* [8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis](8.25_offsensive_techniques.md) diff --git a/src/others/5.3.1_angr/entry_language b/src/writeup/6.2.8_re_defcampctf2015_entry_language/entry_language similarity index 100% rename from src/others/5.3.1_angr/entry_language rename to src/writeup/6.2.8_re_defcampctf2015_entry_language/entry_language diff --git a/src/others/5.3.1_angr/exp_angr.py b/src/writeup/6.2.8_re_defcampctf2015_entry_language/exp_angr.py similarity index 100% rename from src/others/5.3.1_angr/exp_angr.py rename to src/writeup/6.2.8_re_defcampctf2015_entry_language/exp_angr.py diff --git a/src/others/5.3.1_angr/exp_re.py b/src/writeup/6.2.8_re_defcampctf2015_entry_language/exp_re.py similarity index 100% rename from src/others/5.3.1_angr/exp_re.py rename to src/writeup/6.2.8_re_defcampctf2015_entry_language/exp_re.py