Published on Tue 04 June 2019 by @clavoillotte

Edited on Thu 13 June 2019

Product: osquery for Windows

Type: Local Privilege Escalation

Summary: An access right misconfiguration in Osquery for Windows can be abused to run arbitrary programs or load arbitrary DLLs. This can be used by an unprivileged user to obtain SYSTEM privileges on the local machine.

This vulnerability is patched in version 3.4.0.

Updated with some additional details.

Description

The main executable of Osquery for Windows, osqueryd.exe , runs with SYSTEM privileges and starts automatically at boot. It is installed in C:\ProgramData\osquery\osqueryd with hardened access rights to prevent binary modification and DLL planting.

However, the parent directory C:\ProgramData\osquery does not have specific access rights, thus inherits the standard ACL from C:\ProgramData which allows unprivileged users to create files and directories (but not modify existing ones). So unprivileged users can create files & directories in C:\ProgramData\osquery .

This is an issue because there are at least 2 items that are not created by osquery's installer but are used by osqueryd.exe if present: the extensions.load file (used to load osquery extensions) and the osquery.conf.d directory (used to load additional configuration files).

Creating an extensions.load file with arbitrary content allows unprivileged users to make the osqueryd.exe service run arbitrary executables with SYSTEM privileges as osquery extentions. A check performed on the permissions & owner of the extension's executable file, and the permissions of its parent directory to (attempt to) prevent running a user-modifiable executable. There are several ways to bypass this check, one of which is creating a hard link to osqueryd.exe itself (as it is owned by SYSTEM ) in a user-owned directory in which we can drop a DLL to perform a standard DLL hijacking, as illustrated below:

osqueryd.exe attempts to load the following DLLs from its own directory: wevtapi.dll , NETAPI32.dll , WTSAPI32.dll , Secur32.dll , dbgeng.dll , IPHLPAPI.DLL , bcrypt.dll , VERSION.dll , dbgmodel.dll , dbghelp.dll , XmlLite.dll , USERENV.dll , NETUTILS.DLL , SAMCLI.DLL , SSPICLI.DLL . The one with the least imported functions is USERENV.dll , from which only GetUserProfileDirectoryW is imported, so we can make a DLL that only exports this function and runs the desired payload when loaded.

As an unprivileged user we can't write into the C:\ProgramData\osquery\osqueryd directory, but we can create our own directory, put the malicious USERENV.dll in it, and create a hard link to osqueryd.exe in order to obtain the desired owner/ACL. (The hardlink is created using James Forshaw's technique & tool, for more on this you can read a general explanation on privileged file operation bugs here.)

A check is also performed on the directory's ACL, so we'll also need to remove the write access entry of our own user from the directory's ACL to pass this check.

Finally, we can create C:\ProgramData\osquery\extensions.load (because of the default ACL) and write the path of the hardlink in it. At the next run, osqueryd will run itself (through the hardlink) from our directory, load our DLL and execute its code with SYSTEM privileges.

Note: in our DLL we also need to exit the process (just after starting the payload) to avoid infinite recursive spawning of osqueryd.exe processes that would DoS the machine, since every instance will read the extensions.load file and attempt to execute the extension.

To sum up this exploitation procedure, from an unprivileged user session:

Create a directory (somewhere the user has write access), e.g. C:\temp_dir Drop a malicious USERENV.dll in C:\temp_dir Create a hard link, e.g. C:\temp_dir\osq.exe , to C:\ProgramData\osquery\osqueryd\osqueryd.exe Remove the user's write access to C:\temp_dir Create file C:\ProgramData\osquery\extensions.load with content: C:\temp_dir\osq.exe Wait for the next run of the osquery service (e.g. next reboot)

The final state is illustrated below:

Proof of Concept

The following video shows a PoC script performing the above steps to load an arbitrary DLL that that adds the unprivileged user to the administrators group:

Fix

The vendor has released a patch and an advisory. The fix moves the installation directory to C:\Program Files\osquery , which restricts unprivileged write access.

Users should update to Osquery version 3.4.0 or above.

Update: osql users can track this issue.

References

https://www.facebook.com/security/advisories/cve-2019-3567

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3567

Timeline

2019-04-21: Initial report sent to vendor

2019-04-23: Vendor acknowledges reception of report

2019-05-23: Follow-up message sent to vendor

2019-06-04: Vendor response confirming the release of a fixed version

2019-06-04: Mail from vendor informing the bug has been granted a reward through their VRP

2019-06-04: Publication of this advisory