After presenting my findings on the Swisscom router at the [CybSecConference](https://www.cybersecurityalliance.ch/tracks-2016/2016/9/13/c2-reverse-engineering-swisscoms-centro-grande-modems) 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.
#### TL;DR;
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](http://cve.mitre.org/cgi-bin/cvename.cgi?nameCVE-2017-7219))
The following [Metasploit module](http://ogb2rw42s.bkt.clouddn.com/images/netscaler_heap_overflow.rb) can be used to exploit the vulnerability (use at your own risk…), though it will probably only function against the version that was analyzed.
#### DETAILS
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.
![](http://ogb2rw42s.bkt.clouddn.com/images/Screenshot-from-2017-04-18-09-50-58.png)
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.
![](http://ogb2rw42s.bkt.clouddn.com/images/citrix1.png)
This results in the following HTTP request:
```
POST /cvpn/aHR0cDovLzEyNy4wLjAuMQ/fs/ping HTTP/1.1
Host:
[...]
Cookie: NSC_AAACb2f85f0b72ef21c82eac5ac4d314a4170af182cd945525d5f4f58455e445a4a42; NSC_FSSO0
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 41
host127.0.0.1&nsc_noncekPIueluG6ubF37J0
```
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.
![](http://ogb2rw42s.bkt.clouddn.com/images/Screenshot-from-2017-04-18-10-24-51.png)
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 _NOP_s (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 _free_s 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 _free_d.
→ 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](http://ogb2rw42s.bkt.clouddn.com/images/netscaler_heap_overflow.rb), 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.
#### TIMELINE
* 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
暂无评论