Creating queries

The SQL language implemented in Osquery is a superset of SQLite.

  • All queries will start with a SELECT statement. There is no updating or deleting any information/data on the endpoint.

  • The exception to the rule: Using other SQL statements, such as UPDATE and DELETE, is possible, but only when creating run-time tables (views) or using an extension if the extension supports them.

  • Queries will also include a FROM clause and end with a semicolon.

Exploring installed programs

To retrieve all the information about the installed programs on the endpoint (LIMIT limits the results to display):

osquery>select * from programs limit 1;
              name = 7-Zip 21.07 (x64)
           version = 21.07
  install_location = C:\Program Files\7-Zip\
    install_source =
          language =
         publisher = Igor Pavlov
  uninstall_string = "C:\Program Files\7-Zip\Uninstall.exe"
      install_date =
identifying_number =

To select specific columns rather than retrieve every column in the table:

osquery>select name, version, install_location, install_date from programs limit 1;
            name = 7-Zip 21.07 (x64)
         version = 21.07
install_location = C:\Program Files\7-Zip\
    install_date =

Count

To see how many programs or entries in any table are returned:

osquery>select count(*) from programs;
count(*) = 160

WHERE clause

To get the user table and only display the result for the user James:

osquery>SELECT * FROM users WHERE username='James';
        uid = 1002
        gid = 544
 uid_signed = 1002
 gid_signed = 544
   username = James
description =
  directory = C:\Users\James
      shell = C:\Windows\system32\cmd.exe
       uuid = S-1-5-21-605937711-2036809076-574958819-1002
       type = local

The equal sign is not the only filtering option in a WHERE clause:

The equal sign is not the only filtering option in a WHERE clause. Below are filtering operators that can be used in a WHERE clause:

Sign Meaning
= equal
<> not equal
>, >= greater than, greater than, or equal to
<, <= less than or less than or equal to
BETWEEN between a range
LIKE pattern wildcard searches
% wildcard, multiple characters
_ wildcard, one character

Matching wildcard rules

String Meaning
% Match all files and folders for one level
%% Match all files and folders recursively
%abc Match all within-level ending in "abc"
abc% Match all within-level starting with "abc"

Examples:

String Meaning
/Users/%/Library Monitor for changes to every user's Library folder, but not the contents within
/Users/%/Library/ Monitor for changes to files within each Library folder, but not the contents of their subdirectories
/Users/%/Library/% Same, changes to files within each Library folder
/Users/%/Library/%% Monitor changes recursively within each Library
/bin/%sh Monitor the bin directory for changes ending in sh

Some tables require a WHERE clause, such as the file table, to return a value. If the required WHERE clause is not included in the query, osquery will give an error.

osquery>select * from file;
W1017 12:38:29.730041 45744 virtual_table.cpp:965] Table file was queried without a required column in the WHERE clause
W1017 12:38:29.730041 45744 virtual_table.cpp:976] Please see the table documentation: https://osquery.io/schema/#file
Error: constraint failed

Joining tables

Osquery can also be used to join two tables based on a column that is shared by both tables.

osquery>.schema users
CREATE TABLE users(`uid` BIGINT, `gid` BIGINT, `uid_signed` BIGINT, `gid_signed` BIGINT, `username` TEXT, `description` TEXT, `directory` TEXT, `shell` TEXT, `uuid` TEXT, `type` TEXT, `is_hidden` INTEGER HIDDEN, `pid_with_namespace` INTEGER HIDDEN, PRIMARY KEY (`uid`, `username`, `uuid`, `pid_with_namespace`)) WITHOUT ROWID;

osquery>.schema processes
CREATE TABLE processes(`pid` BIGINT, `name` TEXT, `path` TEXT, `cmdline` TEXT, `state` TEXT, `cwd` TEXT, `root` TEXT, `uid` BIGINT, `gid` BIGINT, `euid` BIGINT, `egid` BIGINT, `suid` BIGINT, `sgid` BIGINT, `on_disk` INTEGER, `wired_size` BIGINT, `resident_size` BIGINT, `total_size` BIGINT, `user_time` BIGINT, `system_time` BIGINT, `disk_bytes_read` BIGINT, `disk_bytes_written` BIGINT, `start_time` BIGINT, `parent` BIGINT, `pgroup` BIGINT, `threads` INTEGER, `nice` INTEGER, `elevated_token` INTEGER, `secure_process` INTEGER, `protection_type` TEXT, `virtual_process` INTEGER, `elapsed_time` BIGINT, `handle_count` BIGINT, `percent_processor_time` BIGINT, `upid` BIGINT HIDDEN, `uppid` BIGINT HIDDEN, `cpu_type` INTEGER HIDDEN, `cpu_subtype` INTEGER HIDDEN, `translated` INTEGER HIDDEN, `cgroup_path` TEXT HIDDEN, `phys_footprint` BIGINT HIDDEN, PRIMARY KEY (`pid`)) WITHOUT ROWID;

Looking at both schemas, uid in users table is meant to identify the user record, and in the processes table, the column uid represents the user responsible for executing the particular process.

The tables can be joined using this uid field:

$ osqueryi
Using a virtual database. Need help, type '.help'
osquery>select p.pid, p.name, p.path, u.username from processes p JOIN users u on u.uid=p.uid LIMIT 10;
+-------+-------------------+---------------------------------------+----------+
| pid   | name              | path                                  | username |
+-------+-------------------+---------------------------------------+----------+
| 7560  | sihost.exe        | C:\Windows\System32\sihost.exe        | James    |
| 6984  | svchost.exe       | C:\Windows\System32\svchost.exe       | James    |
| 7100  | svchost.exe       | C:\Windows\System32\svchost.exe       | James    |
| 7144  | svchost.exe       | C:\Windows\System32\svchost.exe       | James    |
| 8636  | ctfmon.exe        | C:\Windows\System32\ctfmon.exe        | James    |
| 8712  | taskhostw.exe     | C:\Windows\System32\taskhostw.exe     | James    |
| 9260  | svchost.exe       | C:\Windows\System32\svchost.exe       | James    |
| 10168 | RuntimeBroker.exe | C:\Windows\System32\RuntimeBroker.exe | James    |
| 10232 | RuntimeBroker.exe | C:\Windows\System32\RuntimeBroker.exe | James    |
| 8924  | svchost.exe       | C:\Windows\System32\svchost.exe       | James    |
+-------+-------------------+---------------------------------------+----------+

Example use

A table autoexec contains the list of executables that are automatically executed on the target machine. There seems to be a batch file that runs automatically. What is the name of that batch file (with the extension .bat)?

What is the full path of the batch file found in the above question?

osquery> .schema autoexec
CREATE TABLE autoexec(`path` TEXT, `name` TEXT, `source` TEXT, PRIMARY KEY (`path`)) WITHOUT ROWID;
osquery> select * from autoexec WHERE name LIKE '%.bat';
+---------------------------------------------------------------------------------------------+----------------+---------------+
| path                                                                                        | name           | source        |
+---------------------------------------------------------------------------------------------+----------------+---------------+
| C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\batstartup.bat                 | batstartup.bat | startup_items |
| C:\Users\James\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\batstartup.bat | batstartup.bat | startup_items |
+---------------------------------------------------------------------------------------------+----------------+---------------+
osquery>