diff --git a/doc/4.14_glibc_tcache.md b/doc/4.14_glibc_tcache.md index c33d915..e724d09 100644 --- a/doc/4.14_glibc_tcache.md +++ b/doc/4.14_glibc_tcache.md @@ -277,19 +277,85 @@ tcache_get (size_t tc_idx) ``` 可以看到注释部分,它假设调用者已经对参数进行了有效性检查,然而由于对 tcache 的操作在 free 和 malloc 中往往都处于很靠前的位置,导致原来的许多有效性检查都被无视了。这样做虽然有利于提升执行效率,但对安全性造成了负面影响。 +#### tcache_dup +```c +#include +#include + +int main() { + void *p1 = malloc(0x10); + printf("1st malloc(0x10): %p\n", p1); + printf("Freeing the first one\n"); + free(p1); + printf("Freeing the first one again\n"); + free(p1); + printf("2nd malloc(0x10): %p\n", malloc(0x10)); + printf("3rd malloc(0x10): %p\n", malloc(0x10)); +} +``` +``` +$ ./tcache_dup +1st malloc(0x10): 0x56088c39f260 +Freeing the first one +Freeing the first one again +2nd malloc(0x10): 0x56088c39f260 +3rd malloc(0x10): 0x56088c39f260 +``` +tcache_dup 与 fastbin_dup 类似,但其实更加简单,因为它并不局限于 fastbin,只要在 tcache chunk 范围内的都可以,而且 double-free 也不再需要考虑 top 的问题,直接 free 两次就可以了。然后我们就可以得到相同的 chunk。 + +第一次 free 后: +``` +gdb-peda$ x/4gx 0x0000555555756260-0x10 +0x555555756250: 0x0000000000000000 0x0000000000000021 +0x555555756260: 0x0000000000000000 0x0000000000000000 +gdb-peda$ vmmap heap +Start End Perm Name +0x0000555555756000 0x0000555555777000 rw-p [heap] +gdb-peda$ x/10gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000000001 0x0000000000000000 <-- counts +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000555555756260 0x0000000000000000 <-- entries +``` +chunk 被放入相应的 tcache bin 中,可以看到该 tcache bin 的 counts 被设为 1,表示有 1 个 chunk,入口为 0x0000555555756260。 + +第二次 free 后: +``` +gdb-peda$ x/4gx 0x0000555555756260-0x10 +0x555555756250: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [double freed] +0x555555756260: 0x0000555555756260 0x0000000000000000 +gdb-peda$ x/10gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000000002 0x0000000000000000 <-- counts +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000555555756260 0x0000000000000000 <-- entries +``` +counts 变成 2,入口不变,表示 tcache bin 已经有两个 chunk 了,虽然是相同的。 + +两次 malloc 后: +``` +gdb-peda$ x/10gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000000000 0x0000000000000000 <-- counts +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000555555756260 0x0000000000000000 +``` +于是我们得到了两个指向同一块内存区域的指针。 + #### tcache_house_of_spirit #### tcache_overlapping_chunks #### tcache_poisoning -#### tcache_fastbin_dup - 这一节的代码可以在[这里](../src/Others/4.14_glibc_tcache)找到。其他的一些情况可以参考章节 3.3.6。 ## CTF 实例 -在最近的 CTF 中,已经开始尝试使用 libc-2.26,比如章节 6.1.15 中的例子。 +在最近的 CTF 中,已经开始尝试使用 libc-2.26,比如章节 6.1.15、6.1.19 中的例子。 ## 参考资料 diff --git a/doc/6.1.17_pwn_secconctf2016_jmper.md b/doc/6.1.17_pwn_secconctf2016_jmper.md index 547bccf..f256b85 100644 --- a/doc/6.1.17_pwn_secconctf2016_jmper.md +++ b/doc/6.1.17_pwn_secconctf2016_jmper.md @@ -418,7 +418,7 @@ struct student { char *name; } student; -struct student my_class[0x1e]; +struct student *my_class[0x1e]; ``` 漏洞就是在读入 memo 和 name 的时候都存在的 one-byte overflow,其中 memo 会覆盖掉 name 指针的低字节。考虑可以将 name 指针改成其它地址,并利用修改 name 的功能修改地址上的内容。 @@ -557,7 +557,7 @@ firmy from pwn import * -context.log_level = 'debug' +# context.log_level = 'debug' io = process(['./jmper'], env={'LD_PRELOAD':'./libc-2.19.so'}) elf = ELF('jmper') diff --git a/doc/6.1.19_pwn_hitbctf2018_gundam.md b/doc/6.1.19_pwn_hitbctf2018_gundam.md index 18ebb32..dabfcb1 100644 --- a/doc/6.1.19_pwn_hitbctf2018_gundam.md +++ b/doc/6.1.19_pwn_hitbctf2018_gundam.md @@ -19,9 +19,522 @@ $ 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. ``` +保护全开。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',可能导致信息泄漏。 + +#### 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 diff --git a/src/Others/4.14_glibc_tcache/tcache_dup.c b/src/Others/4.14_glibc_tcache/tcache_dup.c new file mode 100644 index 0000000..d834423 --- /dev/null +++ b/src/Others/4.14_glibc_tcache/tcache_dup.c @@ -0,0 +1,13 @@ +#include +#include + +int main() { + void *p1 = malloc(0x10); + printf("1st malloc(0x10): %p\n", p1); + printf("Freeing the first one\n"); + free(p1); + printf("Freeing the first one again\n"); + free(p1); + printf("2nd malloc(0x10): %p\n", malloc(0x10)); + printf("3rd malloc(0x10): %p\n", malloc(0x10)); +} diff --git a/src/writeup/6.1.17_pwn_secconctf2016_jmper/exp.py b/src/writeup/6.1.17_pwn_secconctf2016_jmper/exp.py index 7e2c7f2..347e1ee 100644 --- a/src/writeup/6.1.17_pwn_secconctf2016_jmper/exp.py +++ b/src/writeup/6.1.17_pwn_secconctf2016_jmper/exp.py @@ -2,7 +2,7 @@ from pwn import * -context.log_level = 'debug' +# context.log_level = 'debug' io = process(['./jmper'], env={'LD_PRELOAD':'./libc-2.19.so'}) elf = ELF('jmper')