This commit is contained in:
firmianay 2018-05-29 21:51:00 +08:00
parent f34cb95061
commit 070603e235
20 changed files with 302 additions and 228 deletions

View File

@ -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)

View File

@ -203,7 +203,7 @@ node hello.js
```
来执行文件。
![](../pic/1.4.3_nodejs)
![](../pic/1.4.3_nodejs.png)
## Node.js 模块

View File

@ -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 窗口总在最前面。
## 命令行插件

View File

@ -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 可以返回的堆块大小。

View File

@ -50,7 +50,7 @@ blx r5
由于 update-load-branch 指令序列相比 return 指令更加稀少,所以需要把它作为 trampoline 重复利用。在构造 ROP 链时,选择以 trampoline 为目标的间接跳转指令结束的指令序列。当一个 gadget 执行结束后,跳转到 trampolinetrampoline 更新程序全局状态,并将程序控制交给下一个 gadget这样就形成了 ROP 链。
![](../pic/8.1.2_rop_without_ret.png)
![](../pic/8.2_rop_without_ret.png)
## 参考资料

View File

@ -1 +1,25 @@
# 5.2.3 Valgrind
- [简介](#简介)
- [使用方法](#使用方法)
- [VEX IR](#vex-ir)
- [参考资料](#参考资料)
## 简介
Valgrind 是一个用于内存调试、内存泄漏检测以及性能分析的动态二进制插桩工具。Valgrind 由 core 以及基于 core 的其他调试工具组成。core 类似于一个框架,它模拟了一个 CPU 环境,并提供服务给其他工具,而其他工具以插件的形式利用 core 提供的服务完成各种特定的任务。
## 使用方法
## VEX IR
VEX IR 是 Valgrind 所使用的中间表示,供 DBI 使用,后来这一部分被分离出去作为 libVEXlibVEX 负责将机器码转换成 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)

View File

@ -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
<CapstoneBlock for 0x4013b0>
>>> block.capstone.pp()
>>>
>>> block.vex
<pyvex.block.IRSB object at 0x7fe526b98670>
>>> 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 项目开发和使用的中间表示,后来这一部分被分离出去作为 libVEXlibVEX 用于将机器码转换成 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] # 表达式
<pyvex.stmt.WrTmp object at 0x7f38f1ef84b0>
>>> bb.statements[3].pp()
t2 = Add64(t0,0x0000000000000008)
>>> bb.statements[3].data # 数据
<pyvex.expr.Binop object at 0x7f38f1ef8460>
>>> bb.statements[3].data.pp()
Add64(t0,0x0000000000000008)
>>> bb.statements[3].data.op # 操作符
'Iop_Add64'
>>> bb.statements[3].data.args # 参数
[<pyvex.expr.RdTmp object at 0x7f38f1f77cb0>, <pyvex.expr.Const object at 0x7f38f1f77098>]
>>> bb.statements[3].data.args[0]
<pyvex.expr.RdTmp object at 0x7f38f1f77cb0>
>>> bb.statements[3].data.args[0].pp()
t0
>>> bb.next # 基本块末尾无条件跳转的目标
<pyvex.expr.RdTmp object at 0x7f38f3cb6f38>
>>> 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。
## 参考资料

View File

@ -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
```

View File

@ -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

View File

@ -181,7 +181,7 @@ Which Secret do you want to wipe?
| `-> 0x00400a25 leave
\ 0x00400a26 ret
```
果然该函数使用 `calloc()` 为三种 secret 分别了不同大小的 chunksmall secret 属于 small chunkbig secret 和 huge secret 属于 large chunk。在分配前会检查对应的 secret 是否已经存在,即每种 chunk 只能有一个chunk 的指针放在 `.bss` 段上。另外其实读入 secret 的逻辑还是有问题的,它没有处理换行符,也没有在字符串末尾加 "\x00"
果然该函数使用 `calloc()` 为三种 secret 分别了不同大小的 chunksmall secret 属于 small chunkbig secret 和 huge secret 属于 large chunk。在分配前会检查对应的 secret 是否已经存在,即每种 chunk 只能有一个chunk 的指针放在 `.bss` 段上。另外其实读入 secret 的逻辑还是有问题的,它没有处理换行符,也没有在字符串末尾加 `\x00`
#### Wipe secret
```

View File

@ -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
```

View File

@ -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)

View File

@ -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

View File

@ -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?}
```

View File

@ -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

View File

@ -0,0 +1 @@
# 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis

View File

@ -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)