Using osquery to monitor third-party system extensions for IT compliance
Monitoring system-level kernel extensions, modules, and drivers across all three major desktop platforms is a great way to ensure IT compliance, with a sprinkling of security investigation if you uncover something interesting.
In this tutorial we’re going to look at a few example queries and pivots for system-level extensions across Windows, Mac, and Linux. All of the examples below assume you’re using
osqueryi in interactive mode, on a machine of the appropriate type.
There are no kernel extensions on Windows, but there are drivers that perform similar functions, and a
drivers table in osquery. Let’s begin with a simple
'SELECT *' query:
SELECT * FROM drivers;
Not too bad, depending on what you have installed, but a lot of system devices are still listed. Let’s narrow it down:
SELECT * FROM drivers WHERE provider != 'Microsoft';
Again, much better. There are still some strange entries such as the Document XPS Writer, but, let’s narrow the query further:
SELECT device_name, description, service, inf, class, provider FROM drivers WHERE provider != 'Microsoft' AND device_name != '' ORDER BY device_name ASC;
Now, normally there would be one last step to get digital signature information. The
drivers table has the
image column, which is the path to the actual exe or bin. Joining against the
authenticode table should get digital signature information for the drivers. Unfortunately, due to a bug in osquery that has been fixed but not pushed to a release, the image column is empty, and joining against an empty column won’t get us very far. Once the next release is pushed (4.5.2), this final step should be eminently possible.
Next up is Linux, and the
kernel_modules table. This is a very simple table, essentially outputting the contents of
/proc/modules. Let’s take a look at a simple query first:
SELECT * FROM kernel_modules;
And then filtering by ‘Live’, i.e. loaded:
SELECT name, used_by FROM kernel_modules WHERE status = 'Live' ORDER BY name ASC;
There is no way that I have found to show proprietary or “tainted” modules as opposed to upstream/OSS modules—at least with this table. One possible interesting additional use case might be to join this table against the
augeas table, pointed at
/etc/modules, to determine blacklisted modules and the like.
On macOS, the osquery table that you will find most useful is
kernel_extensions. This includes (almost) all kernel extensions on the system, both those that are actively loaded and those within the load search path or part of the pre-linked kernel. Extensions can also live nearly anywhere on the system; VMware Fusion keeps theirs in
/Library/Application Support, while Google Drive File Stream’s fuse kext is inside the application bundle.
Be aware! Kernel extensions have changed a great deal over the past several releases of macOS, starting with user approval in High Sierra. They are also no longer ending with the deprecation of them in Big Sur. System extensions are the way forward, but the work to add them to osquery is not yet in progress.
Let’s start with a simple query:
SELECT * FROM kernel_extensions;
This is apt to return a lot of results. Adding
count(*) to the query shows more than 200 results on my machine. Most of these are Apple kexts, and not terribly useful as a result, since they’re on every machine. Let’s narrow the results to only third-party kexts. Excluding both the kernel itself, and anything loaded from the SIP-protected
/System/Library directory, and sorting by name for readability:
SELECT * FROM kernel_extensions WHERE path NOT LIKE '/System/Library/%' AND name != '__kernel__' ORDER BY name ASC;
This should offer a much smaller set of results, and it also reveals an interesting potential pivot. On Catalina, loaded third-party extensions are copied to
/Library/StagedExtensions. Let’s try filtering by that:
SELECT * FROM kernel_extensions WHERE path LIKE '/Library/StagedExtensions/%' ORDER BY name ASC;
Same results. Note that because we are filtering on
StagedExtensions, this could theoretically miss unloaded but still present kernel extensions. Try running this query before and after you open a recent version of VMware Fusion, for example.
kernel_extensions table is very useful, but it doesn’t provide an important piece of information: Who signed the kernel extension? For that we turn to the
signature table. We’ll join on the path, and substring just the important bits:
SELECT DISTINCT kx.name, substr(kx.path, 26) AS orig_path, substr(s.authority, 27) AS authority FROM kernel_extensions kx JOIN signature s USING (path) WHERE kx.path LIKE '/Library/StagedExtensions/%' ORDER BY kx.name ASC;
- When joining multiple tables, you can shortname the tables to allow for easier reference:
kxin this example.
/Library/StagedExtensionsin front of every kext; the
substrselection removes that (“select all characters starting from 26, going to the end”).
There are of course even further tables that could be joined against; the
hash table for example would provide MD5 or SHA-256 sums of the kext binaries for checking against VirusTotal. Here’s an updated version of a query I wrote for QueryCon in 2019, that does just that:
WITH kext_bins AS (SELECT path FROM file WHERE directory IN (SELECT path || '/Contents/MacOS/' FROM kernel_extensions WHERE path LIKE '/Library/StagedExtensions/%') ) SELECT substr(s.path, 26) AS orig_path, substr(s.authority, 27) AS authority, h.sha256 FROM signature s JOIN hash h USING (path) WHERE path IN kext_bins AND s.arch = 'x86_64';
Again, some explanation:
kext_binsis a subquery that appends the actual binary path inside the kext bundle
- Joining to
hashwith that path results in the SHA-256 hash of the actual binary
signaturereports both 32-bit and 64-bit kexts, so we exclude that by ignoring any non-x64 binaries
As I mentioned in the beginning, all of these examples were done using the
osqueryi tool. For IT compliance and other investigative work, setting these as scheduled queries to run using
osqueryd would give you historical reference, allowing you to track changes over time, and even run data analysis on outliers in your fleet. How many users have installed any kexts at all? How many users are running unique kexts? How has that changed over time?
I hope you find these queries useful, and a springboard for your own investigations. Good hunting!
Subscribe for new posts
- Building Your Cyber Security Strategy: A Step-By-Step Guide
- 8 Docker Security Best Practices To Optimize Your Container System
- Intro to Osquery: Frequently Asked Questions for Beginners
- SOC 2 Compliance Requirements: Essential Knowledge For Security Audits
- Warzone RAT comes with UAC bypass technique