Heap Overflow Vulnerability in Citrix NetScaler Gateway (CVE-2017-7219)

After presenting my findings on the Swisscom router at the CybSecConference last year, I started looking for a new product to analyze. I quickly found that it’s possible to download virtual “demo” appliances of Citrix products, so I went on to download a Netscaler VPX, which at the time was at version 11.10 (b50.10). The goal as always was to discover a way to compromise the device remotely, which is what led me to discover a heap overflow vulnerability which allows an authenticated user to compromise the device with root privileges. During the research, I (un)fortunately wasn’t able to find a way to exploit the flaw without credentials.


A heap overflow in the “ping” functionality allows an authenticated VPN user to trigger a use-after-free condition in order to execute arbitrary commands on the appliance. (CVE-2017-7219)

The following Metasploit module can be used to exploit the vulnerability (use at your own risk…), though it will probably only function against the version that was analyzed.


As mentioned above, I began by downloading the virtual appliance and started it up on my machine. Since I’ve never used or configured a Netscaler appliance in the past, it took a while to get things going and configuring it in some kind of standard mode.

Once the appliance is started, it is possible to log into the console with the standard nsroot account. This gives access to a “limited” shell, but Citrix were nice enough to add a shell command which gives root access to the box, so I used this to extract the filesystem and analyze what was going on.

Going through the various files on the system, I found one that seemed promising which was named /netscaler/nsvpnd. As it’s name hints at, it is used to handle requests sent to the VPN web interface. Though only authenticated requests seem to get here, as authentication itself is performed by another binary on the system.

One of the requests that is performed by the nsvpnd binary is the ping request.

This results in the following HTTP request:

POST /cvpn/aHR0cDovLzEyNy4wLjAuMQ/fs/ping HTTP/1.1
Cookie: NSC_AAAC=b2f85f0b72ef21c82eac5ac4d314a4170af182cd945525d5f4f58455e445a4a42; NSC_FSSO=0
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 41


Apparently the /cvpn/aHR0cDovLzEyNy4wLjAuMQ part of the URL is not actually required, so it can safely be removed. In any case, this request is eventually handled by one of two vulnerable functions that contain an unbounded strcpy with our host parameter, as shown below.

This is where the overflow happens, though we are not overwriting a stack variable, but one of the members of a struct, which is expected to be at most 256 bytes long. Our parameter on the other hand can go up to 512 bytes, which is what allows us to overflow this buffer.

So it is possible to write up to 256 bytes after the host member of the structure, therefore overwriting any other members of the structure after the host, which is where things get interesting. One of the following members is actually a pointer to another structure (a parameter list) on the heap which was previously allocated and eventually gets free‘d by the application when the request has completely been processed.  This means we can essentially free an arbitrary memory location.

Before going any further, a quick analysis of the system and binary show that FreeBSD uses jemalloc instead of dlmalloc, the heap is not executable, but the stack is, and ASLR is not enabled (this was 2016 after all). Another thing that was helpful in exploiting this particular issue is that all requests to the web interface are handled by one single process, which means we can actually interact many times with the process by sending multiple HTTP requests if required.

At this point, my idea to gain code execution was the following:

  1. Find a function pointer somewhere in the application, as well as the size that was used to allocate that memory
  2. Free the memory address of this function pointer
  3. On the next malloc of the appropriate size, the same address should be returned
  4. Overwrite the function pointer with user-controlled data (a pointer to my shellcode) when it is copied to this memory address
  5. Trigger the function pointer to call my shellcode

The only remaining problem is getting a shellcode to some predictable location. Thankfully, as mentioned earlier, ASLR is disabled, the stack is executable and the value we send in our host parameter is actually stored on the stack! All we need is to get this address and plug it into the function pointer to get the shellcode to execute. Obviously, despite ASLR being disabled, the stack will not always be exactly at the same place, so I used a super-l33t technique consisting of pre-pending my shellcode with lots of NOPs (because 2016).

So we can now break down each step and look at how we can achieve them:

  1. With some reverse engineering and debugging, I found one function pointer that was always allocated at address 0x2840a0c0. This function seems to be used to decode parameters sent in the HTTP requests. The memory address is initially allocated at 0x08097fb9 with a call to malloc(32).
  2. Use the overflow to overwrite the pointer to the parameter list with 0x2840a0c0. The address is then free‘d when the request has finished being processed. Here, we also need to take a note of where our host parameter is located on the stack, as this is where we will store our shellcode.
  3. While searching through the binary’s code, I found one place where a malloc is called with a length which can be specified by the VPN user directly. This is when providing the username and password to log into a SMB server. There may be other parts of the code that could be exploited in a more reliable manner, but this is the first I found and decided to go with it. The only problem is that it means we need to initiate a SMB login to a server that is accessible to the Netscaler appliance.
  4. As long as our password is between 16 and 32 characters, the previously free‘d address is returned and we can therefore overwrite the function pointer with the value of our password. It must therefore be the address of our shellcode, which we discovered was placed on the stack when performing the ping.
  5. The function pointer is actually called at regular intervals by the application while processing data, so we can just wait until it is called to get our code executed.

As you’ve probably deduced by now, in order to exploit the vulnerability, we are going to use two separate HTTP requests. The first one is used to put the shellcode on the stack and trigger the overflow, while the second is used to overwrite the function pointer with the address of our shellcode and actually execute our payload. This is summarised here:

Request 1 (ping host)

→ Start of host value contains shellcode which is conveniently placed on the stack

→ Use overflow to overwrite pointer to parameter list with 0x2840a0c0

→ When the request has been entirely processed, the program frees the address 0x2840a0c0

Request 2 (smb login)

→ Specify a password parameter of length between 16 and 32, this forces malloc to return the address that was previously freed.

→ Password value (shellcode location) overwrites function pointer that was previously located there

→ While processing the request, the overwritten function pointer is called, executing the shellcode

So that’s pretty much it. The following MSF module, should be able to exploit the flaw, but use it at your own risk. I’ve only tested it on a controlled lab environment.

For those of you who participated in our Insomni’hack teaser this year, you’ll notice many similarities with “The Great Escape – part 2” challenge, as it was very much inspired by this flaw.


  • 08.12.2016: Initial report sent to Citrix
  • 09.12.2016: Case opened by Citrix to investigate the issue
  • 14.12.2016: Vulnerability acknowledged and reproduced by Citrix team
  • February-March 2017: Rollout of fixed Netscaler versions
  • 12.04.2017: Release of security bulletin: https://support.citrix.com/article/CTX222657


SCRT is proud to announce the opening of its new office in Bern

To sustain its growth and continue to maintain relations of proximity with its customers and partners, SCRT is glad to announce the opening of its new office in Bern.

SCRT is one of the leaders in Information Security in Switzerland, with headquarters in Préverenges (Vaud) and a branch office in Bordeaux (France). This new SCRT location will facilitate the provision of professional IT Security Services to new and existing clients in the German speaking part of Switzerland.

Ideally located in the Monbijou district, close to the train station, this office offers a dynamic, welcoming and optimal setting for our customers and staff. SCRT will continue to offer the same services at our new address in Bern and ensure our well known proximity and reactivity all over Switzerland.

We look forward to welcoming you!

SecureIT Valais – Workshop Buffer Overflow

La première édition de SecureIT s’est déroulée vendredi le 17 février à Sierre. L’événement organisé par l’AVPC (Association Valaisanne pour la Promotion de la Cybersécurité) en collaboration avec la HES-SO Valais-Wallis, Parti Pirate et le groupe de hackers étiques Fourchette Bombe, a rassemblé près de 300 participants.

J’y ai présenté un workshop sur l’exploitation d’un Buffer Overflow, vulnérabilité très ancienne et pourtant largement exploitée même de nos jours. Le but du workshop était de faire une introduction à l’exploitation de cette vulnérabilité, sans toutefois avoir la prétention d’un cours complet sur le sujet.

Voici les slides ainsi que l’exercice complet de mon workshop.

Mot de passe pour l’archive: scrt

SecureIT – Valais

Voici les slides de ma présentation de Vendredi dernier pour Secure-IT. J’y ai présenté quelques-unes des techniques les plus communément exploitées en test d’intrusion pour compromettre un domaine Windows ainsi que les différentes remédiations possibles.

Pour ceux qui n’auraient pas le temps de parcourir la totalité des slides, voici un bref résumé des recommendations:

  • Désactiver WPAD et les protocoles de résolutions de noms LLMNR et NetBios
  • Utiliser un système come LAPS pour gérer les mots de passe des administrateurs locaux
  • Limiter l’utilisation des comptes privilégiés (surtout les “admins du domaine”)
  • Utiliser AppLocker pour empêcher l’exécution de programmes non autorisés
  • Améliorer le filtrage réseau entre les VLANs internes et même au sein du même VLAN (Firewall local)
  • Limiter les privilèges utilisés par les applications (notamment Tomcat et serveurs SQL)
  • Utiliser un système de corrélation de logs pour pouvoir détecter les anomalies sur le réseau

L’utilisation de tests d’intrusion de type “Red Team” ou “Purple Team” permet ensuite de valider la pertinence des logs et des alertes remontées, ceci dans le but d’accélérer la réponse à incident pour pouvoir palier à une réelle attaque.

Joomla! Admin user creation (3.4.4 → 3.6.3)

On October 25th, Joomla! was updated to version 3.6.4 to address two vulnerabilities :

CVE-2016-8869 concerning registration with elevated privileges.
CVE-2016-8870 concerning account creation while registration is disabled.

In this post, we wanted to quickly discuss the vulnerability and its impact on vulnerable installations.

Upon patch-diffing the two versions, we noticed that an entire method had been removed from the components/com_users/controllers/user.php file : the register method from the UsersControllerUser class.


Normally, the register method used by Joomla! is the one from the UsersControllerRegistration class, in components/com_users/controllers/registration.php.

The deleted one is most likely a leftover from old patches, and doesn’t enforce a check on whether or not user registration is enabled (as opposed to the UsersControllerRegistration.register method).

Moreover, the $data array is supposed to be sanitized in the first line below, but the unsanitized value is then used in the register function at the end of this snippet, allowing us to submit custom data such as group and uid values.


We can call this method by posting our registration values on the index.php?option=com_users&task=User.register URL.

POST /index.php?option=com_users&task=User.register HTTP/1.1
 Host: localhost
 Connection: keep-alive
 Accept-Encoding: gzip, deflate
 Accept: */*
 User-Agent: python-requests/2.11.1
 Cookie: 96b8cb33d84fb0aa459957bcad81cf90=go86e62fsve2a3jaqdmk6h6oq4
 Content-Length: 284
 Content-Type: application/x-www-form-urlencoded

user[password1]=exploit&user[username]=exploit&user[email2][email protected]&user[password2]=exploit&user[name]=exploit&user[email1][email protected]&user[groups][]=7&7c48521fa302676bada83d0e344011f2=1

The newly created user is then found on the server  :


For a valid request, we need to retrieve a CSRF Token and post it with a value = 1.

We are able to specify a custom user[id] value. If that id pre-exists in the database, the corresponding user will be overwritten during the registration.

Additionally, we can get high privileges by posting an array of user[groups][] values that will be assigned to the account. The default group id for Administrators is 7.

However, the only way to get the SuperAdmin group (8 by default) is to overwrite a pre-existing SuperAdmin user by specifying his user id.

Note that if user registration is disabled, the new/overwritten user will be blocked from logging in resulting in a denial of service for the SuperAdmin account.

In order to find and compromise a SuperAdmin account, it is possible to bruteforce all user ids and try to create a user with all possible groups. This will ensure that only the existing SuperAdmin accounts are overwritten (only the SuperAdmin ids can be overwritten to have SuperAdmin rights).

To create an admin account when the Administrator group id isn’t 7, it is possible to assign all the group ids from 1-99 (but leave the SuperAdmin group id out).

Download the PoC