A swiss-army tool for testing, creating, and manipulating osquery query packs.

With Go v1.20+ installed, run:
go install github.com/chainguard-dev/osqtool/cmd/osqtool@latest
osqtool supports 4 commands:
apply - programatically manipulate an osquery query pack, for instance, adjusting intervalspack - create a JSON pack file from a directory of raw SQL filesunpack - extract raw SQL files from a JSON query pack filerun - run an osquery pack file or directory of SQL queries with human and diff-friendly outputverify - verify that the queries in a query pack, directory, or raw SQL file are valid and test wellWant to take an osquery pack from the internet, but make changes to it programatically? osqtool has you covered:
curl https://raw.githubusercontent.com/osquery/osquery/master/packs/it-compliance.conf \
| osqtool --min-interval=8h --platforms linux,posix --exclude os_version apply -
This will set all queries to an 8-hour interval, remove Windows-specific queries, and exclude a query named os_version.
Create an osquery pack configuration from a recursive directory of SQL files:
osqtool pack /tmp/osx-attacks
Here's the example output:
{
"queries": {
"Aobo_Keylogger": {
"query": "select * from launchd where name like 'com.ab.kl%.plist';",
"interval": "3600",
"version": "1.4.5",
"description": "(http://aobo.cc/aobo-mac-os-x-keylogger.html)",
"value": "Artifact used by this malware"
},
"Backdoor_MAC_Eleanor": {
"query": "SELECT * FROM launchd WHERE name IN ('com.getdropbox.dropbox.integritycheck.plist','com.getdropbox.dropbox.timegrabber.plist','com.getdropbox.dropbox.usercontent.plist');",
"interval": "3600",
"version": "1.4.5",
"description": "(https://blog.malwarebytes.com/cybercrime/2016/07/new-mac-backdoor-malware-eleanor/)",
"value": "Artifact used by this malware"
},
...
The pack command supports the same flags as the apply command. In particular, you may find --exclude, --exclude-tags, and --verify useful.
Run a set of queries!
osqtool run incident-response.conf
Example output:
block_devices (7 rows)
----------------------
block_size:512 label: model: name:/dev/nvme0n1 parent: size:488397168 type: uuid: vendor:
block_size: label: model: name:/dev/nvme0n1p1 parent:/dev/nvme0n1 size:614400 type: uuid: vendor:
block_size: label: model: name:/dev/nvme0n1p2 parent:/dev/nvme0n1 size:415929892 type: uuid: vendor:
block_size: label: model: name:/dev/nvme0n1p3 parent:/dev/nvme0n1 size:71843677 type: uuid: vendor:
block_size:512 label: model:'SD Card Reader' name:/dev/sda parent: size:0 type: uuid: vendor:CalDigit
block_size:512 label: model: name:/dev/dm-0 parent: size:415925796 type: uuid: vendor:
block_size:512 label: model: name:/dev/dm-1 parent: size:71839581 type: uuid: vendor:
crontab (1 rows)
----------------
command:'root run-parts /etc/cron.hourly' day_of_month:* day_of_week:* event: hour:* minute:01 month:* path:/etc/cron.d/0hourly
disk_encryption (0 rows)
Extract an osquery pack into a directory of SQL files:
osqtool --output=/tmp/osx-attacks unpack osx-attacks.conf
Here is example output:
Writing 745 bytes to /tmp/out/OceanLotus_dropped_file_1.sql ...
Writing 268 bytes to /tmp/out/OSX_MaMi_DNS_Servers.sql ...
Writing 328 bytes to /tmp/out/OSX_ColdRoot_RAT_Files.sql ...
Writing 209 bytes to /tmp/out/iWorm.sql ...
74 queries saved to /tmp/out
The unpack command supports the same flags as the apply command.
Verify that the queries are valid in a pack, SQL file, or directory of SQL files
osqtool verify /tmp/detect
Example output:
Verifying "high-disk-bytes-written" ...
high-disk-bytes-written" returned 0 rows within 264.361831ms
Verifying "unexpected-shell-parents" ...
"unexpected-shell-parents" failed validation: /sbin/osqueryi --json [exit status 1]: Error: near line 1: near "sh": syntax error
78 queries found: 55 verified, 10 errored, 13 skipped
"verify" failed: 10 errors occurred:
* xprotect-reports: /sbin/osqueryi --json [exit status 1]: Error: near line 1: no such table: xprotect_reports
You can set limits on the number of rows returned, amount of runtime per query, per day, or across the pack, see --help for more information.
Here are the options that are available to apply, unpack, pack, and verify
-default-interval duration
Interval to use for queries which do not specify one (default 1h0m0s)
-exclude string
Comma-separated list of queries to exclude
-exclude-tags string
Comma-separated list of tags to exclude (default "disabled")
-max-interval duration
Queries can't be scheduled more often than this (default 15s)
-max-query-daily-duration duration
Maximum duration for a single query multiplied by how many times it runs daily (checked during --verify) (default 1h0m0s)
-max-query-duration duration
Maximum query duration (checked during --verify) (default 4s)
-max-results int
Maximum number of results a query may return during verify (default 1000)
-max-total-daily-duration duration
Maximum total query-duration per day across all queries (default 6h0m0s)
-min-interval duration
Queries cant be scheduled less often than this (default 24h0m0s)
-multi-line
output queries is multi-line form. This is accepted by osquery, but technically is invalid JSON.
-output string
Location of output
-platforms string
Comma-separated list of platforms to include
-single-quotes
Render double quotes as single quotes (may corrupt queries)
-skip_headers
If true, avoid header prefixes in the log messages
-tag-intervals string
modifiers to the default-interval based on query tags (default "transient=5m,postmortem=6h,rapid=15s,often=x/4,seldom=2x")
-verify
Verify the output
-workers int
Number of workers to use when verifying results (0 for automatic)
At the moment, flags must be declared before the subcommand. ¯\_(ツ)_/¯
$ claude mcp add osqtool \
-- python -m otcore.mcp_server <graph>