eyes

Intro - TL;DR

On April 29th, exploit-db published a Local Privilege Escalation (LPE) exploit for Druva InSync. Druva had by then implemented a patch on their latest InSync release, fixing the bug. However, the patch introduced an additional bug, paving the way for further exploitation and making it possible for a local low-privileged user to obtain SYSTEM level privileges. A team from Tenable Research and I concurrently discovered this new vulnerability, resulting in a new CVE (CVE-2020-5752) and exploit being published.

Initial bug history

Druva InSync is an endpoint application that is responsible for “Integrated backup, eDiscovery and compliance monitoring” as stated on Druva website. The InSync Windows client application runs a service as SYSTEM, hence any security vulnerability could lead to an escalation of privileges (EoP). The first bug discovered by Tenable research is in fact a Local Privilege Escalation (LPE) via Command Injection. The escalation is accomplished by interacting to the he local service running on port 6064 by means of valid network commands. The communication protocol is pretty straightforward and it requires the following packets to be sent one at a time:

  • A hello packet: in our case it’s the hardcoded string inSync PHC RPCW[v0002]
  • • A function number: the app maps various functionalities, like backup snapshot or probing, to a function number. In this case, it is function number 5 that’s of interest, as it is mapped to a generic command execution.
  • The command string length
  • The command itself

Setting in motion IDA and verifying that function number 5 is exposing remote command injection, gives this result:

And sure enough, at location 140001F60 we spot our beloved CreateProcess taking the command line string as an argument.

This is clearly an unrestricted command injection that allows us to execute any commands with the same privileges as SYSTEM user. This made me curious and I started to dig deeper into how the vulnerability had been mitigated.

Is the fix really fixing what is supposed to be fixed? 👾

Druva’s security bulletin stated that the latest 6.6.3 release included a fix, which can be downloaded here.

If we run the old exploit against the latest version, we fail miserably and we get warned by an admonitory message.

As an initial strategy we can diff the patched CreateProcess function to see if there is an additional check in place or any other changes to the previous version.

It seems that a path checker has been introduced: any binary invoked outside the scope of the inSync path will just be ignored. This poses the questions; which string is representing the allowed path? And how is the verification actually done? Let’s deal with one thing at a time. Firstly, let’s take a closer look at the two functions, strlen and strncmp that are responsible for the branching decision.

To check the path length, strlen is called first, whose argument is the Str variable, derived by the following function, which has obviously hardcoded parameters matching the expected application path C:\ProgramData\Druva\inSync4\.

This parameter is then passed to strlen, which gives 0x1d as a result. We can double-check via WinDbg that, in my specific case, the path-string is indeed 29 character long.

This value is then saved in r8 (as MaxCount) and passed together with our entire command to strncmp. We should pay extra attention here, because the devil always sits undisturbed in the details.

As opposed to strcmp which is dealing with null-terminated strings, strncmp is returning a 0 (identical strings) if one of the two checked ** non-null-terminated** substrings are identical for the given N characters, no matter how many extra characters are prepended afterwords.

Here is the definition from MSDN:

int strncmp(
   const char *string1,
   const char *string2,
   size_t count
);

This means that, count (or MaxCount in IDA) tells strncmp to stop checking as long as we have a match up until the end of the application path, regardless any additional characters, null-byte included.

As an example, we can test the following string C:\ProgramData\Druva\inSync4\bypassed and verify through WinDbg if we can get past the check. We place a breakpoint after the string comparison has been done and take a look at the returned value from EAX.

Which is zero, and according to MSDN means that the two substrings are identical:

Return value 	Description
------------   -----------
< 0            string1 substring less than string2 substring
0              string1 substring identical to string2 substring
> 0            string1 substring greater than string2 substring

Which also means that we are going to jump to the right branch, and successfully spawn a process.

An additional interesting bit from the above output is that the residual value pointed by RCX is the exact remainder of the string we have sent.

Exploitation manifests by itself

We can therefore jump to the conclusion that by prepending the submitted path with many \.. we could still obviously execute any system-wide-available binary, and that’s also the principal change I made to the original code. The complete new version of the exploit can be found here

We could also reuse the previous exploit by simply inserting the right amount of dot-dot-slashes.

Conclusions

As a key takeaway, it’s always a good practice to verify how a vendor implemented a patch: the learning experience can be great and might give back unexpected findings. Specifically to this application, due to the direct use of C-like functions instead of standard Windows API, a local low-privileged user could obtain SYSTEM level privileges by escaping the allowed PATH via directory traversal.

Disclosure Timeline

  • 04/30/2020 - Vulnerability discovered and disclosed. 90-day date is 07/29/2020.

  • 04/30/2020 - Druva notifies that the vulnerability has also been reported by another researcher and CVE filed.

  • 04/30/2020 - Requested more information about a possible coordinated disclosure

  • 05/05/2020 - Druva replies that Tenable has submitted the vulnerability in advance.

  • 05/06/2020 - Reached out Tenable and in agreement with Druva we decide for coordinate disclosure on Tenable original 90-days deadline, which happens to be 05/25/2020.

  • 05/19/2020 - Druva informs us that the patch should be available on 05/21/2020

  • 05/21/2020 - Druva releases the fix on version 6.6.4 and the findings are disclosed.