I briefly touched on USB device monitoring on Mac OSX via Osquery during my Osquery Con 2019 talk last year. However, I though I’d explore in more detail how to setup device monitoring on Osquery from start to finish. Including some more example queries.
Prerequisites
This article is written using Osquery 4.2.0. If you’re looking for help on installing Osquery please see our guide here.
Why monitor USB devices?
There are three main reasons I can think of — off the top of my head as to why you’d want to monitor your devices USB devices.
Data loss prevention
Removable media can be used as a medium to exfiltrate data from corporate networks. Most companies will block access to online file sharing services in favor of corporate file sharing services. Which will provide audit-able events for compliance. With restrictions on email attachment types and sizes, USB devices become a fast medium of file exchange between systems. Amusing of course USB ports aren’t blocked either. Nevertheless, files left on USB devices can become a potential source of a data breach. Given their size, these devices are typically misplaced or re-purposed. Even if deleted files can still be recovered from USB devices. Having an audit-able log of files copied to USB devices can help mitigate risk.
Compliance & Auditing
As with the use case above. Many security framework and compliance standards require some form of USB device monitoring and auditing. In the event of a data breach, having a strong reference point can massively help aid investigations. Thereby, potentially minimising any damage to the business operations.
Security
USB devices can be a potential source of malicious files being introduced to a system or network. Being able to identify those files — understanding whether they’re known or unknown by the organisation can help minimise damage to the business.
Setting up File Integrity Monitoring (FIM)
FIM uses FSEvents
(Mac OS X) to monitor files and directories for changes. As files and directories are written, read and deleted events are created. Osquery can update its FIM configuration on the fly via the remote API. Wildcards for directories can also be used.
When a USB device if plugged into a Mac, its automatically mounted into the /Volumes/
directory. The following Osquery FIM configuration will create an event as files are created, modified and deleted within the /Volumes/<device>
directory.
An important note is that this configuration is done for simplicity. In some cases, secondary system volumes may be mounted within /Volumes/
leading to a large number of events being generated, just something to bear in mind.
The following is a simple JSON file called osq.conf
which will be loaded by Osquery.
{
"options": {
},
"file_paths": {
"homes": [
"/Volumes/%%"
]
}
}
Fire up the osqueryi
like so providing our new config file:
sudo osqueryi --disable_audit=false --verbose --disable_events=false --config_path ./osq.conf
USB devices
The first query you can run is as follows:
SELECT vendor, model FROM usb_devices WHERE removable = 1;
This will provide you with a list of all the removable USB devices currently attached to your system.
+---------+--------------+
| vendor | model |
+---------+--------------+
| SanDisk | Cruzer Glide |
+---------+--------------+
There are two tables we need for this next part disk_events
and mounts
this will help us find the mount path for our newly inserted USB device.
SELECT action, DATETIME(time, 'unixepoch') AS datetime, vendor, mounts.path FROM disk_events LEFT JOIN mounts ON mounts.device = disk_events.device;
Which will yield the following:
+--------+---------------------+---------+------------------+
| action | datetime | vendor | path |
+--------+---------------------+---------+------------------+
| add | 2020-08-19 16:16:10 | SanDisk | |
| add | 2020-08-19 16:16:10 | SanDisk | /Volumes/SanDisk |
| add | 2020-08-19 16:16:10 | SanDisk | |
+--------+---------------------+---------+------------------+
When the USB device is removed, the action will change to a remove
event. The path
column refers to where the USB device has been mounted on your system.
To check to see what files have been created, modified or removed on the device we can use the file_events
table in conjunction with the target_path
column.
SELECT action, uid, SUBSTR(target_path, 18) AS path, SUBSTR(md5, 0, 8) AS hash, time FROM file_events WHERE sha1 <> '' AND target_path NOT LIKE '%DS_Store';
When I copy the Zercurity.png
over to the USB device, the following file events are generated:
+---------+-----+---------------+---------+------------+
| action | uid | path | hash | time |
+---------+-----+---------------+---------+------------+
| CREATED | 99 | | 43cf614 | 1597853771 |
| CREATED | 99 | Zercurity.png | 8894958 | 1597853800 |
| CREATED | 99 | Zercurity.png | 8894958 | 1597853800 |
| UPDATED | 99 | Zercurity.png | 8894958 | 1597853800 |
| UPDATED | 99 | Zercurity.png | 8894958 | 1597853800 |
+---------+-----+---------------+---------+------------+
The query above will grab all file events from all configured file_paths
. We’ll link those file events to a device after the fact. However, to do this in one single query for a given device. We can join both the mounts
table and the file_events
table together like so:
SELECT mounts.path FROM disk_events
INNER JOIN mounts ON mounts.device = disk_events.device
WHERE vendor IN (
SELECT vendor FROM usb_devices
WHERE removable = 1
)
This query will provide us with a list of mount paths:
+--------------------+
| path |
+--------------------+
| /Volumes/Zercurity |
+--------------------+
Then, whilst using the above query in a WITH
statement we can use this to query your file_events
table.
WITH target_paths AS (
SELECT mounts.path FROM disk_events
INNER JOIN mounts ON mounts.device = disk_events.device
WHERE vendor IN (
SELECT vendor FROM usb_devices
WHERE removable = 1
)
)SELECT * FROM file_events WHERE target_path LIKE (SELECT path || '%' FROM target_paths);
This will then give you a complete dump of all the file changes made on any attached USB devices.
You’ll also automatically get the user making the change and hash of the file too. Which you can use to wash against known files within your organisation.
Hopefully that helps. Please feel free to get in touch if you need any help. Or join us on the #zercurity Osquery Slack channel.