Controlling Kerio Control – When Your Firewall Turns Against You

research

This blog post describes two different attacks which can be used to compromise companies which use Kerio Control in their network. Kerio Control is a hardware appliance which can be used as network firewall, router and VPN gateway. Both attacks spawn a reverse shell on Kerio Control. Since both attack payloads are delivered via CSRF (cross site request forgery) or XSS (cross site scripting) no ports must be open from the Internet.

Controlling Kerio Control – SEC Consult

About the vendor: “Protect your network from viruses, malware and malicious activity with Kerio Control, the easy-to-administer yet powerful all-in-one security solution. Kerio Control brings together next-generation firewall capabilities – including a network firewall and router, intrusion detection and prevention (IPS), gateway anti-virus, VPN, and web content and application filtering.”
Source: http://www.kerio.com/products/kerio-control

 

The following figure shows an example network of the attack scenario:

Kerio control image - SEC Consult

The attacker doesn’t directly attack the firewall system, instead a victim is tricked to visit his website. The website includes malicious content (images, forms, JavaScript) which instruct the victim’s browser to send all requests from the internal network directly to Kerio Control. Because of this not a single port must be open from the Internet on Kerio Control.

Kerio Control implements weak protections against these types of attacks which can be bypassed easily. Multiple critical security vulnerabilities have been identified by René Freingruber and Raschin Tavakoli of SEC Consult, which are documented in our technical security advisory.

For instance, Kerio Control uses a 6-year-old PHP binary which contains a countless number of memory corruption vulnerabilities. For demonstration, we picked one such memory corruption bug and wrote an exploit for it. The exploit is delivered blind (the website can’t read responses from requests sent to the internal network because of SOP restrictions – same-origin-policy).

It’s also important to note that it’s possible to brute-force the internal Kerio Control credentials via the below described method, even if the system is not accessible from the Internet. This is achieved via a side channel and a normally minor information leak vulnerability. It is a very good example why also internal systems should be secured by strong credentials and seemingly minor security issues have to be fixed.

The first attack vector heavily abuses two PHP scripts, both scripts are not referenced by any other file in Kerio Control and contain two different(!) seemingly deliberate(?) CSRF check bypasses. These two scripts contain multiple vulnerabilities which attackers need for establishing reverse root shells into company networks: Remote code execution with root privileges, CSRF check bypasses (makes it exploitable from the Internet), XSS (could be used to defeat SOP), vulnerable code exploitable for heap spraying (to defeat ASLR). Moreover, one additional script can be used to defeat ASLR which leaks heap and stack pointers.

The second attack vector doesn’t use a memory corruption vulnerability, instead we use a publicly known vulnerability in Kerio Control which has not been fixed in the past 12 months (reported by Raschin Tavakoli, now employee at SEC Consult). The full exploit is publicly available on exploit-db (https://www.exploit-db.com/exploits/38450/), only the used XSS (cross site scripting) vulnerability was fixed. To demonstrate that the issue still exists we searched for two other XSS vulnerabilities to turn the XSS vulnerabilities again to a remotely exploitable reverse root shell. However, this attack has the pre-condition that the attacked victim is currently logged in as administrator (or that we can obtain administrator credentials via the brute-force attack). Kerio’s response to this reported vulnerability (and to the vulnerability that the webserver is running with root privileges) was: “I [they] do not consider this a vulnerability”. It will therefore not be fixed by Kerio.

We have just discovered another XSS 0-day vulnerability which can again be used to obtain a reverse root shell on the most current Kerio Control system (version 9.1.3 build 1408). We have contacted the vendor again regarding this issue.

It’s also important to note, that the first attack scenario was not fixed by updating the 6-year-old PHP binary, instead only the problem of unserialize was fixed superficially. Kerio Control still uses PHP version 5.2.13.

 

Kerio YouTube screenshot - SEC Consult

Remote Identification Of Kerio Control Systems

This step itself is not based on a vulnerability, however, it is a mandatory step during the overall attack. First, the attacker tricks a victim (from the internal network of the attacked company) to visit his malicious website. The website then performs the complete attack. For this the website must know the internal IP address of Kerio Control. Therefore, the first action is to somehow identify and obtain this IP address.

This can easily be done by adding images on the website which point to an image stored on Kerio Control. The perfect target for this is the logo stored at <KerioIP>:4081/nonauth/gfx/kerio_logo.gif. The attacker can add JavaScript event listeners for onload and onerror on his evil website to detect if the image was loaded or not. If the image was loaded, the attacker knows that the scanned IP address hosts Kerio Control.

A simple JavaScript scan script is shown below:

for (i = 1; i < 255; i++) {
   ip_to_check = “192.168.20.” + i.toString();
 my_image = document.createElement("img");
   my_image.src = "https://" + ip_to_check + 
       ":4081/nonauth/gfx/kerio_logo.gif";
   my_image.onerror=kerio_not_alive;     // callback
   my_image.onload=kerio_alive;          // callback
   document.getElementById("invisible_area").appendChild(my_image);
 }

 

For such an attack the attacker must know the internal IP range. This information can be obtained via different techniques (e.g. WebRTC leak, e-mail headers, misconfigured DNS server, information disclosure vulnerabilities on the website, social engineering) or can just be brute-forced (but this would add an additional delay to the overall attack).

Session Verification

For the first attack scenario at least a session as standard user is required (the second attack scenario requires that the victim is an administrator). The reason for this is that the vulnerability resides in code which can only be executed by authenticated users. Three situations can occur:

  1. The attacked victim is currently logged in as standard user (low privileges).
  2. The attacked victim is currently logged in as administrator (high privileges).
  3. The attacked victim is currently not logged in (no session).

Situation one and two are perfectly valid and can be used to obtain a reverse shell. Situation three is a problem because a valid session is mandatory for the attack. In such a case the attacker can start a remote brute-force attack via CSRF to identify weak credentials. This is even possible if no ports are open from the Internet because the complete attack is conducted via the web browser of the victim and the victim is connected to the internal network.

For such a brute-force attack the attacker needs a way to identify if the last tested credentials are valid or not. This is typically not possible because the attacker can’t read the response of a request which was sent to another domain / internal IP address (because of SOP – Same-Origin-Policy). For example, the attacker can send a valid login request to Kerio Control (because the login is not protected against CSRF), however, he can’t read the response and therefore doesn’t know if the credentials were valid or not.

One solution is to just assume that the credentials are valid and continue the exploit. If the exploit works, the credentials were valid. However, with this approach the complete attack would take too long and the victim may close the website before the attack finished.

A better solution is to use a side channel which bypasses SOP, namely image loading combined with JavaScript event handlers. This is possible because the URL to the user image only returns a valid image if the user is logged in. That means that the attacker can send a login request and after that embed an image pointing to <KerioIP>:4081/internal/photo with JavaScript event handlers installed for onloadand onerror. If the onerrorevent handler triggers, the credentials were wrong, if the onloadevent handler triggers, the credentials were valid.

The following HTML code demonstrates this:

<img src=”https://<Kerio-IP>:4081/internal/photo” onerror=not_logged_in(); onload=logged_in();></img>

 

Attack Vector One – Bypass Of CSRF Protections

The next step is to trigger the vulnerabilities within Kerio Control. For this two vulnerable scripts on the system will be exploited. The first script sets the unserialize input (we are going to abuse the PHP unserializefunction) and the second script actually invokes unserialize on the malicious input. Both scripts are stored inside the authenticated area which means that the scripts are protected by a CSRF protection. The CSRF protection should ensure that a remote attacker can’t force an action in the context of the attacked user session. In the case of the login request it was trivial because the login was just not protected by a CSRF protection.

In the case of the two scripts the CSRF protections must be bypassed, but it turns out, that this is trivial as well. The most common method is to use a XSS vulnerability, but for the first attack vector we will use another technique (the XSS technique will be described when discussing the second attack vector). Now we are going to directly exploit the vulnerable PHP code of Kerio Control.

The following PHP code can be found in the first vulnerable script:

1: $p_session->getCsrfToken(&$p_securityHash);
2: $p_postedHash = $_GET[‘k_securityHash’] || $_POST[‘k_securityHash’];
3: if (” == $p_postedHash || ($p_postedHash != $p_securityHash)) {
4: exit();
5: }

The problem with the above code is, that the code is PHP code. Luckily for us, sadly for the programmer, the code is not JavaScript code which means that it has a completely different behavior than the programmer may have intended (except if it was deliberately backdoored). At line 2 the programmer wants to get either the GET or POST parameter k_securityHash, but || is a logical operator in PHP. The result is therefore either the boolean value trueor false. At line 3 the programmer compares this result against the correct security hash (p_securityHash). The comparison is done by != , however, in PHP the operator != applies type juggling first (loose comparison). Correct would be a strict comparison via !==.

If we set k_securityHash to any value (either in GET or POST), we compare the boolean value truewith a string. PHP will then interpret the string as boolean which is always trueand then conclude that trueequals truewhich means the check will always be bypassed if we set k_securityHashto any value.

So the first CSRF protection bypass was trivial. Let’s now focus on the CSRF protection in the second script:

1: $p_session->getCsrfToken(&$p_securityHash);
2: $p_hash = $_GET[‘k_securityHash’];
3: …
4: if (!$p_session || (” == $p_hash && $p_hash != $p_securityHash)) {
5: $p_page = new p_Page();
6: $p_page->p_jsCode(‘window.top.location = “index.php”;’);
7: $p_page->p_showPageCode();
8: die(); 

9: }

Now line 2 doesn’t contain the problem with the logical operator, however, line 4 contains the problem with loose comparison again. But there is also another detail, which differs…

The first code used || as a condition link, whereas this time the code uses &&. The condition can only become trueif $p_hash (the GET parameter) is empty and the GET hash does not equal the correct hash. That means that it’s again enough to set k_securityHash to any value because then $p_hashis not empty.

 

Both CSRF check bypasses make the exploit working from the Internet to obtain reverse shells into the attacked company network.

Attack Vector One – The Exploitation

During analysis of Kerio Control we identified that Kerio uses a 6-year-old PHP binary (version 5.2.13). This version contains a countless number of publicly known vulnerabilities, including many memory corruptions leading to full code execution. To demonstrate the issue, we picked a commonly attacked PHP function – unserialize– to exploit Kerio Control. Unserialize can be called on user control input, however, the code is only available in the authenticated area (therefore the session verification step is required).

To exploit the function, we used CVE-2014-3515. The flaw exists because unserialize parses fields from the input, stores references to these fields, but doesn’t increase the reference count of these fields. As soon as the reference count from a field (or better a ZVAL, the internal data structure used to describe all kind of variables in PHP like integers, strings, objects or arrays) becomes zero, the ZVAL gets freed. Nearly all memory corruptions in unserialize work like this, only the method to decrement the reference count to zero (free the data) changes. In CVE-2014-3515 SplObjectStorage objects are used for this. When unserializing a SplObjectStorage object, a specific code path can be taken, which frees fields from the SplObjectStorage. However, the unserialize code added references to these fields (without incrementing the reference counter) and can therefore be used to access the freed data (the references became dangling pointers).

Let’s see this in action:

 

Code image 1 - SEC Consult

The code tells PHP to parse a custom object (C), the name of the class is 16-characters long (SplObjectStorage) and the data between the { and } is 49 characters long. “x:i:3” tells PHP that three elements will follow which must be attached to SplObjectStorage (This is custom unserialize-code related to SplObjectStorage. The code which parses these fields is important during exploitation, we call it in this blog post “attach code”). “m:a:1” tells PHP that the object has one member variable, a string of length one named y (s:1:”y”) with the value “i:111”. The first character always tells PHP the type (e.g. s for string, i for integer, C for custom object, and so on), so “i:111” is an integer with the value 111.

The output of the above code:

Code image 2 - SEC Consult

As suspected the number 111 gets displayed. Let’s modify the code to use references:

Code image 3 - SEC Consult

Now R:1 is used instead of i:111 which means that the value of y is now a reference pointing to the entry with index 1 (the integer with value 777):

Code image 4 - SEC Consult

Until now everything works as expected. If we want to get the value 888 we have to update the reference to R:2, for 999 the reference R:3 must be used. But what happens if we use two times the same value?

Consider the following code:

Code image 5 - SEC Consult

What will now be printed (the second value changed from 888 to 777)? R:1 should still reference the first 777, but the code leads to the following output:

Code image 6 - SEC Consult

The problem is that the three values (from the “attach code” which are described by x:i:3) are added to a hash table. That means that a hash is calculated for every value and based on this hash the value gets inserted into the table. The values 777, 888 and 999 result in three different hashes and therefore all three values are stored in the table. However, if we store 777 two times, the second insertion will result in the same hash, the code then removes the first entry (with the same hash) from the table, frees the data and inserts the second data. That means that the ZVAL for the first 777 will be freed, however, there is still a reference (dangling pointer) to the ZVAL which can be accessed by the referencing code (R:1). After that PHP parses i:999 which will allocate the ZVAL of 999 over the freed memory address, therefore R:1 points to 999 (as well as R:3 would point to 999).

The problem is that the “attach code” should only process objects and no other types (and that a reference is added and the reference counter isn’t increased, but this is a problem for all PHP unserialize vulnerabilities). So typically it should not be allowed to add integers like we are doing with 777, 888 and 999. The associated code interprets these integers as objects (without checking the type) which exactly allows a collision of the hash. Here we are exploiting a type-confusion vulnerability to trigger a use-after-free bug.

We still have a problem with the above code. It only frees the ZVAL (=16 bytes of data describing the variable). We want to fully control this ZVAL, but for this the data is too small (if we for example allocate a string later, the string content will not be allocated over the freed data, instead the ZVAL of the string will be allocated over it). That means we have to free a variable which consumes more size than an integer. For this we have two possibilities: An array with additional elements or an object with member variables.

In both cases we have to add a second element with the same hash value. In the case of the array this leads to a problem because the “array-value” (source of the hash calculation) is a pointer to the heap and this pointer is randomized by ASLR (address space layout randomization).

So the logical solution is to use objects instead. The hash of an object is calculated based on the object handle (a number incremented per object) and the object handler (a pointer to function pointers of the object).

A first (not working) approach would be the following:

Code image 7 - SEC Consult

Now two objects of type “Beta” are used (currently without member variables), but the output shows that the use-after-free vulnerability isn’t triggered:

Code image 8 - SEC Consult

The reason is that the first Beta object has a different handle (handle 2) than the second Beta object (handle 3). It’s also possible to see this in the above output, SplObjectStorage has handle 1 (check the #1 in the output) and the referenced Beta object has handle 2 (#2 in the output). This is exactly how it should work. The code should only parse objects and every object has a different handle, therefore a hash collision should not be possible.

However, as described above, the PHP code contains a type confusion vulnerability which means that we can also add other types (integers, double values, strings, …) which will be interpreted as object and inserted into the hash table.

Let’s consider the following code:

Code image 9 - SEC Consult

The above code first adds an object of type stdClass and then the double value 8.784…. The double value was exactly chosen to result in the same hash as the stdClass and can therefore be used to free the stdClass object (A double value can be stored in eight bytes, the first four bytes will be interpreted as the object handle, the second four bytes will be interpreted as the “object handlers” pointer in the type confusion vulnerability). The output demonstrates that the memory of the stdClass object was freed:

Code image 10 - SEC Consult

We use the type confusion vulnerability to interpret the double value as object to make a hash collision to free the object to trigger a use-after-free vulnerability. The exact double value which is required for the collision depends on the PHP binary (more precise on the location of the “object handlers” pointer). Typically, this location is randomized by ASLR because PHP is loaded as library (e.g. in Apache). However, in the case of Kerio Control the code was statically compiled into the winroutebinary (the main binary of Kerio Control containing the webserver, PHP, the firewall, and so on) and the binary was not compiled as PIE (position-independent-executable). Therefore, the hash is always the same because the “object handlers” are always stored at the same location.

Another problem with winrouteis that stacks are marked as executable. This can be abused during exploitation (we didn’t used this flaw because the locations of the stacks are randomized by ASLR, but in combination with the identified stack pointer leakage it would be another solution for the exploit, however, with the additional dependency of a XSS vulnerability to read the stack pointer).

 

The next step is to control the content of the ZVAL. For demonstration we changed the ZVAL type to a string (type 6) and let the string point to the image base of PHP where the ELF-magic value is stored:

Code image 11 - SEC Consult

Now the stdClass object contains some fields (to increase the size of the freed data), the double value frees the object (as well as all member variables) and later we allocate a string of length 15. Length 15 is mandatory because PHP will add one to the length (null termination) resulting in an allocation of 16 bytes – exactly the size of a ZVAL. The reference was changed from R:1 to R:3 to point to one of the (freed) member variables from the stdClass object (R:1 would just point to the ZVAL of the 15-bytes string).

The content of the string now describes the referenced ZVAL, the last bytes specify the type (0x06 = string), the first DWORD (0x08048001) is the base address of the string, the second DWORD is the length of the string (0x03), the third DWORD is the reference counter which is in this case 0x01.

The following figure shows that the attack works as expected (the ELF-magic value gets dumped):

Code image 12 - SEC Consult

This vulnerability can be used to read the complete memory space and therefore to bypass ASLR. However, in the case of Kerio Control the result of the unserialize function is not reflected back to the attacker (moreover, because of SOP we would not be able to read the output, an additional XSS vulnerability would be required for this).

The exploit must therefore be written fully blind (and a way to bypass ASLR is required). The code execution step is trivial; the data type must only be changed from string (0x06) to object (0x05) to control the object function pointers.

The last missing step is to bypass ASLR. A solution would be to turn the vulnerability into a write4 vulnerability (that means that we get the possibility to write 4 chosen bytes to a chosen memory location). Then we can for example overwrite the base address or length field of a string or overwrite a function pointer to point to another location (e.g. change the function address of a function called on the unserialize output to var_dump() to reflect the output back to the attacker).

This can typically be done by changing the data type to an array, however, in the case of PHP it’s not trivial because arrays are not implemented as simple arrays, instead arrays are implemented via hash tables (including memory pointers which we don’t know because of ASLR). Another possibility is to carefully choose the unserialize data and implement the write4 via the later PHP code from the attacked PHP script. But all these techniques would require that we read the response and this is not possible because of SOP (without a XSS).

Moreover, we also found a pointer leak which can be used to obtain the base address of the stack and heap, but reading this pointer also requires a SOP bypass.

We therefore decided to just use heap spraying as a method to bypass ASLR because then the exploit works blind (SOP must not be bypassed). Heap spraying is not the most elegant way, it’s also not 100% reliable, however, during tests it worked 99% of time and even if we hit the 1% cave the server just restarts three seconds after the crash and we can exploit it again. For a simple PoC it’s enough to demonstrate the issue with heap spraying.

Luckily, the programmer left (in addition to the CSRF check bypasses) some other useful code for us. One of the two attacked scripts contains the following code:

1: $p_variable = urldecode($_POST[‘k_variable’]);
2: $p_value = urldecode($_POST[‘k_value’]);
3: …
4: $p_session->setSessionVariable($p_variable, $p_value);

We can set any session variable to any value. We can therefore set variable “x1” to an 8 MB (max file upload) payload, “x2” to the payload, “x3” to the payload and so on. That means we can use the code for heap spraying.

The last steps are straight forward:

  • We can spray two different areas (each 250 MB in size because the target device has 4 GB RAM) to the heap. Area one contains ROP (return-oriented-programming) code pointing to a stack-pointer-shifting gadget. This gadget shifts the stack pointer to area two (on the heap). In area two we add a ROP NOP (no-operation) sled followed by a ROP chain which calls mprotect to disable DEP (data execution protection). After that we execute our shellcode.
  • Change the data type of the ZVAL to object (0x05) and set the reference counter to zero. This will immediately invoke the free-function on the object. Since we can control the “object handlers” pointer we can let it point into area one. This will then shift the stack pointer into our ROP NOP sled in area two, then execute the ROP chain which calls mprotect and then executes our shellcode. The shellcode just spawns a reverse root shell on Kerio Control.

A video demonstrating the attack can be found at the beginning of the blog post.

Attack Vector Two

In addition to the described attack vector above a second method exists to spawn a reverse root shell. This attack vector has the pre-condition that the attacked user is logged in as administrator. The likelihood for a successful attack is therefore low, but this attack works without exploitation of a memory corruption vulnerability and is therefore simpler.

The administrative interface contains functionality to upload an upgrade image. No checks are performed on the uploaded file. The included bash script will just be executed with root privileges. This vulnerability was already reported and published by Raschin Tavakoli on 2015-10-12 and is still unfixed (more information can be found in the original advisory at https://www.exploit-db.com/exploits/38450/). Only the used XSS vulnerability was fixed by Kerio.

The upgrade functionality is protected by a CSRF check which is correctly implemented. A XSS vulnerability is therefore required to bypass the check. To demonstrate that the vulnerability still exists SEC Consult searched for two different XSS vulnerabilities to demonstrate the attack.

One of these XSS vulnerabilities can be found in one of the two scripts which was used during the first attack. With this reflected XSS vulnerability an administrator can be attacked to upload an upgrade image and then perform the upgrade. This executes the uploaded bash script with root privileges allowing to spawn a reverse root shell. The attack can also be conducted via a malicious website from the Internet.

Vendor Response

The vendor was contacted on 2016-08-29. On 2016-09-01 the vendor responded with a PGP key. The advisory was sent PGP encrypted to Kerio on 2016-09-01. On 2016-09-09 the vendor responded that most vulnerabilities will be fixed by 2016-09-20.

The command execution vulnerability in the administration interface (second attack vector) will not be fixed as well as the reported issue “webserver running with root privileges”. Kerio’s statement to these vulnerabilities was: “I [they] do not consider this a vulnerability”.

The explanation of Kerio was that an attacker can also use the XSS vulnerability to change the administrator password, activate the SSH service and open the SSH port from the Internet. Therefore, the attacker doesn’t have to abuse the upgrade functionality to spawn a reverse root shell.

However, SEC Consult considers this itself a vulnerability. All this should not be possible with a simple XSS vulnerability. Actions like changing the administrator password or activation of SSH access from the Internet should require the input of the administrator password. Then it’s no longer possible to abuse a simple XSS vulnerability in such a heavy way. The same applies for the upgrade functionality which must be fixed by the vendor.

SEC Consult informed Kerio about this on 2016-09-09, however, Kerio kept their decision. Hence the command execution vulnerability remains unfixed. A simple XSS vulnerability can still be used to obtain a reverse root shell on Kerio Control.

To demonstrate the issue one more time we have just discovered another XSS 0-day vulnerability which makes all of this exploitable again (in the most current version which is 9.1.3 build 1408). Of course we have notified the vendor on this issue through our responsible disclosure but it seems a more thorough approach to securing their products and raising the quality is needed.

Conclusion

Here are some lessons which can be learned from the attack:

For administrators:

  • Just because a system is in the internal network it doesn’t mean that weak credentials can be configured. During the above attack the attacker was able to brute-force the credentials from an internal system via CSRF from the Internet. Internal systems must also be secured by strong credentials, patches have to be applied timely and a layered approach (zone concept) should be used for accessing critical systems.

For vendors:

  • Vulnerabilities must be fixed in-depth. It’s not enough to fix a bug superficially, instead developers have to understand why a vulnerability occurs and identify the real reason behind it. For example, in PHP the real reason was that references were added without incrementing the reference counter. Instead, only the free code was patched for all kinds of unserialize bugs. In the case of Kerio Control, the real issue is a 6-year-old PHP binary, instead Kerio just fixed the unserialize vulnerability. Same applies for the RCE vulnerability which was not fixed, only the used XSS vulnerability was fixed.

This research was conducted by René Freingruber and Raschin Tavakoli, Security Consultants at SEC Consult.