Tue 04 June 2019
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.
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
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).
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:
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
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.
- Drop a malicious
- Create a hard link, e.g.
- Remove the user's write access to
- Create file
- 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
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.
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