* add deploy category, change product to guides * update links to deployment guides * Update deploy-fleet-on-hetzner-cloud.md * Update enrolling-a-digital-ocean-droplet-on-a-fleet-instance.md * Update how-to-install-osquery-and-enroll-linux-devices-into-fleet.md * Update delivering-data-to-snowflake-from-fleet-and-osquery.md * Update how-to-install-osquery-and-enroll-windows-devices-into-fleet.md * Update how-to-install-osquery-and-enroll-macos-devices-into-fleet.md * Update deploying-fleet-on-aws-with-terraform.md * Update deploying-fleet-on-render.md * Update how-to-uninstall-osquery.md * Update osquery-a-tool-to-easily-ask-questions-about-operating-systems.md * Update get-and-stay-compliant-across-your-devices-with-fleet.md * Update work-may-be-watching-but-it-might-not-be-as-bad-as-you-think.md * Update converting-unix-timestamps-with-osquery.md * Update correlate-network-connections-with-community-id-in-osquery.md * Update using-elasticsearch-and-kibana-to-visualize-osquery-performance.md * Update fleet-quick-tips-querying-procdump-eula-has-been-accepted.md * Update locate-assets-with-osquery.md * Update osquery-consider-joining-against-the-users-table.md * Update import-and-export-queries-and-packs-in-fleet.md * Update ebpf-the-future-of-osquery-on-linux.md * Update generate-process-trees-with-osquery.md * Update get-and-stay-compliant-across-your-devices-with-fleet.md * Update work-may-be-watching-but-it-might-not-be-as-bad-as-you-think.md * Update ebpf-the-future-of-osquery-on-linux.md * Change category meta value back to guides Co-authored-by: Desmi-Dizney <99777687+Desmi-Dizney@users.noreply.github.com> * change article category * update latest article category * add redirects for articles not handled by cloudflare rules * Update to main nav I did a little more housekeeping on the main nav for when we do the switch on these categories. - Added link to /deploy under Guides on the Documentation dropdown. - Added link to /guides under Guides on the Documentation dropdown - Removed the now redundant "See all" from under Guides on the Documentation dropdown - Removed the now redundant "See all" from under Articles on the Community dropdown (There's no need to point users to All categories any more, since all the category land are now linked to independently from the main nav. * Update article category name Co-authored-by: Desmi-Dizney <99777687+Desmi-Dizney@users.noreply.github.com> Co-authored-by: Mike Thomas <mthomas@fleetdm.com>
5.9 KiB
Generate process trees with osquery
Rich process trees on macOS, Linux, and Windows
Using advanced SQL syntax, it is possible to generate process trees in osquery similar to those generated by the pstree
utility. With osquery, the generated trees can be extended to include additional information that can aid analysis.
Below is the basic structure of the query:
WITH target_procs AS (
SELECT * FROM processes WHERE name = 'osqueryd'
)
SELECT *
FROM (
WITH recursive parent_proc AS (
SELECT * FROM target_procs
UNION ALL
SELECT p.* FROM processes p JOIN parent_proc pp ON p.pid = pp.parent
WHERE pp.pid != pp.parent
ORDER BY pid
)
SELECT pid, parent, uid, name, path
FROM parent_proc
);
Running this query in osqueryi
will generate results like:
+-------+--------+-----+-------------+---------------------------------------------------------------+
| pid | parent | uid | name | path |
+-------+--------+-----+-------------+---------------------------------------------------------------+
| 69230 | 1 | 0 | orbit | /private/var/lib/orbit/bin/orbit/macos/stable/orbit |
| 1 | 0 | 0 | launchd | /sbin/launchd |
| 0 | 0 | 0 | kernel_task | |
| 38179 | 4016 | 501 | osqueryd | /usr/local/bin/osqueryd |
| 4016 | 4015 | 501 | zsh | /usr/local/Cellar/zsh/5.7_1/bin/zsh |
| 4015 | 4014 | 501 | login | /usr/bin/login |
| 4014 | 4008 | 501 | iTerm2 | /Applications/iTerm.app/Contents/MacOS/iTerm2 |
| 4008 | 1 | 501 | iTerm2 | /Applications/iTerm.app/Contents/MacOS/iTerm2 |
| 1 | 0 | 0 | launchd | /sbin/launchd |
| 0 | 0 | 0 | kernel_task | |
+-------+--------+-----+-------------+---------------------------------------------------------------+
How it Works
This query makes use of SQLite Common Table Expressions (CTEs) to recursively generate the requested data. Below we will examine the components of the query:
WITH target_procs AS (
SELECT * FROM processes WHERE name = 'osqueryd'
)
SELECT *
FROM (
WITH recursive parent_proc AS (
SELECT * FROM target_procs
UNION ALL
SELECT p.* FROM processes p JOIN parent_proc pp ON p.pid = pp.parent
WHERE pp.pid != pp.parent
ORDER BY pid
)
SELECT pid, parent, uid, name, path
FROM parent_proc
);
- Line 2 — Set the target process(es) that the query will generate trees for.
- Line 10 — Stop the recursion when the process parent is the process itself. This prevents infinite recursion on macOS and Linux for the process
0
. - Line 11 — Order the evaluation of recursive rows by the
pid
. This generally results in the process trees being output in the correct order (though we cannot guarantee that the ordering will be correct, we will get results for all processes in the trees). On Windows it can be better toORDER BY start_time
aspids
are not increasing. - Line 13 — Choose the columns to retrieve from the results. In this example we select a limited set of columns to ease interpretation of the results.
Extend the Concept
There are a number of ways that this query can be extended to address different needs.
Retrieve More Details
Change the SELECT
statement in line 13 to retrieve a different set of results. A simple case could be changing to SELECT *
to get all the columns from the processes
table.
A more complex scenario could be to generate the hashes of the running binaries:
WITH target_procs AS (
SELECT * FROM processes WHERE name = 'osqueryd'
)
SELECT *
FROM (
WITH recursive parent_proc AS (
SELECT * FROM target_procs
UNION ALL
SELECT p.* FROM processes p JOIN parent_proc pp ON p.pid = pp.parent
WHERE pp.pid != pp.parent
ORDER BY pid
)
SELECT pid, parent, uid, name, path, md5
FROM parent_proc LEFT JOIN hash USING (path)
);
Target Different Processes
Change the query on line 2 to generate trees for a different set of processes. One option would be SELECT * FROM processes WHERE pid = 1234
if we know the pid of the process we are interested in. Targeting processes by attributes that remain consistent on different machines (like name
, unlike pid
) is a useful technique to ensure that live queries across hosts are successful.
This can also be extended using the full power of osquery. For example, we might want the process tree of every process bound to a port:
WITH target_procs AS (
SELECT DISTINCT processes.*
FROM processes JOIN listening_ports USING (pid)
WHERE port != 0
)
SELECT *
FROM (
WITH recursive parent_proc AS (
SELECT * FROM target_procs
UNION ALL
SELECT p.* FROM processes p JOIN parent_proc pp ON p.pid = pp.parent
WHERE pp.pid != pp.parent
ORDER BY pid
)
SELECT pid, parent, uid, name, path
FROM parent_proc
);
Wrapping Up
With this query as a building block, osquery provides the capability to generate rich process trees. Consider using this with an osquery TLS server such as Fleet to examine this information on multiple machines at once.