Last December NIST announced this vulnerability CVE-2019-19781 and soon after that Citrix published this page CVE-2019-19781 – Vulnerability in Citrix Application Delivery Controller, Citrix Gateway, and Citrix SD-WAN WANOP appliance and released a verification tool supporting clients for finding out if they were vulnerable or not: CVE-2019-19781 – Verification Tool
A verification tool is addressed to an IT specialist who works these solutions and needs to quickly assess if the software or appliance is affected by this vulnerability.
Let me say that Citrix is a market leader and this is not an article that wants to diminish or criticize any of their products in any way. It’s wonderful that the source code is made available by the vendor and you can see and learn what the code is actually doing.
This is a sort of code review and my personal suggestion on how to achieve similar results with no code at all, having in mind that their verification tool just works, but it is making a simple check more complicated that actually is.
Let’s look the source code
And this is the code they provide:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
#!/usr/bin/env python import subprocess import socket import argparse from argparse import RawTextHelpFormatter ips = [] ip_count = 0 hit_count = 0 results = [] NSIP_RESPONSE_MSG = r"You don't have permission to access /vpns/" # Function to load IPs and FQDNs from Input File def get_ip_from_txt(filename): global ips with open(filename, 'r') as f: lines = f.readlines() for line in lines: line = line.strip() ips.append(line) # Function to check for valid IP or valid Host def check_valid_host(ip): try: socket.getaddrinfo(ip, 443, 0, 0, socket.IPPROTO_TCP) except BaseException: return False return True # Function to check CVE-2019-19781 exploitability def send_requests(): global ip_count global hit_count global ips global results for ip in ips: if check_valid_host(ip): pass else: print("Host Invalid: {}".format(str(ip))) continue ip_count = ip_count + 1 cmd = "curl -m 3 -k -X GET --path-as-is https://" + \ ip + "/vpn/../t/../vpns/./cfg/smb.conf" + " 2>&1" try: response = "" response = subprocess.check_output(cmd, shell=True) if ("[global]") and ("encrypt passwords") and ( "name resolve order") in str(response): hit_count += 1 results.append(ip) elif NSIP_RESPONSE_MSG in str(response.decode("utf-8", errors="ignore")): hit_count += 1 results.append(ip) except subprocess.CalledProcessError: print("IP address unreachable: {}".format(str(ip))) if __name__ == "__main__": # Parsing Command Line Arguments parser = argparse.ArgumentParser( description='Tool to scan ADC and Gateway for CVE-2019-19781 exploitability.\nVersion 2.0', formatter_class=RawTextHelpFormatter) parser.add_argument( '--in_file', help='Input file with list of IPs and/or FQDNs, one per line.', dest='ip_file', required="True") parser.add_argument( '--out_file', help='Output file containing vulnerable IPs and FQDNs.', dest='output_file', required=True) args = parser.parse_args() # Read input file for list of IP Addresses or FQDNs ip_file = args.ip_file output_file = args.output_file get_ip_from_txt(ip_file) # Test exploitability for CVE-2019-19781 send_requests() # Publish results in Output File print( "Out of {} IP addresses, {} found to be vulnerable".format( ip_count, hit_count)) print("Writing output to {} ".format(output_file)) with open(output_file, 'a') as out: for result in results: out.write("{}\n".format(result)) |
Let’s put on our developer’s hat
I guess the goal was using a popular programming language that could be run on any platform, Python is a perfect candidate for that even if is not installed by default on Windows it’s straightforward to install it via the Windows Store even without admin privileges under the user profile.
I also understand the choice of writing the code using just the standard libraries and not using a friendly library like Requests . This script is also requires a couple of mandatory arguments an input file for your target hosts and output file for the results of the scan results.
So if you look of what this script does in 110 lines of python code you end up with a wrapper of CURL written in python so it will require 2 dependencies: python and curl installed on the operating system, needs you to read the help to populate the text file.
Do we have any alternative?
Yes, simply using curl instead.
On every OS is simply required to run (replace <IP_address> with your target server FQDN or IP) :
1 |
curl -k -I --path-as-is https://<IP_address>/vpn/../vpns/cfg/smb.conf |
This oneliner performs the same checks with a little bit fancier user prompt and response (for Linux or macOS or WSL on Windows ):
1 |
read -p "Please type the FQDN or IP of the Citrix server (ADC/NetscalerGateway/SD-WAN WANOP) you want to check: " TARGET;curl -vk –path-as-is https://$TARGET/vpn/../vpns/ 2>&1 | grep "You don’t have permission to access /vpns/" >/dev/null && echo "VULNERABLE: $TARGET" || echo "MITIGATED: $TARGET" |
Why I prefer this approach?
First of all simplicity and practicality.
The reason why I’m re-writing this tool is that curl is a very popular tool that everybody uses or at least should know that exists.