Written by Matteo Malvica, Security Researcher, mnemonic
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 myself concurrently discovered this new vulnerability, resulting in a new CVE (CVE-2020-5752).
Initial bug history
Druva InSync is an endpoint application that according to their website is responsible for “Integrated backup, eDiscovery and compliance monitoring”. 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 initially discovered by Tenable Research is a Local Privilege Escalation via Command Injection. The escalation is accomplished by interacting to the 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 this 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
5that’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?
When running the old exploit against the latest version, we fail and 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,
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
This parameter is then passed to
strlen, which gives
0x1d as a result. Via WinDbg we can double-check 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. 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 afterwards.
Here is the definition from MSDN:
const char *string1,
const char *string2,
This means that,
MaxCount in IDA) tells
strncmp to stop checking as long as we have a match up until the end of the application path, regardless of 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.
The value is zero, and according to MSDN this 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
This also means that we are jumping 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 sent.
This means that by prepending the submitted path with many
\.. we can still 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 is available here, and is also available on exploit-db.
We could also reuse the original exploit by simply inserting the right amount of dot-dot-slashes.
As a key takeaway, it’s always 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, this process made it obvious that 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.
- 04/30/2020 - Vulnerability discovered and disclosed. 90-day date is 07/29/2020.
- 04/30/2020 - Druva reports back that the vulnerability has 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 to Tenable and in agreement with Druva we decide to coordinate the disclosure on Tenable original 90-days deadline, 05/25/2020.
- 05/19/2020 - Druva informs us that the patch should be available earlier, on 05/21/2020.
- 05/21/2020 - Druva releases the fix on version 6.6.4 and the findings are published