This notebooks lists all recent flows/collections across all orgs on the platform. It may be used for auditing or as a means of finding a collection previously scheduled.
By default it will only look for the last five flows per client, and return a maximum of 50 flows altogether. Adjust the LIMITs as needed. Hunts are ignored by default, but may be included by setting IgnoreHunts to True.
Links are created for clients and flows, but in order for these to work, you need to set the server metadata field “VelociraptorServerURL”. If not set, https://127.0.0.1:8889 is used.
name: Notebooks.Admin.Flows
author: Andreas Misje – @misje
description: |
This notebooks lists all recent flows/collections across all orgs on the
platform. It may be used for auditing or as a means of finding a collection
previously scheduled.
By default it will only look for the last five flows per client, and return a
maximum of 50 flows altogether. Adjust the LIMITs as needed. Hunts are ignored
by default, but may be included by setting IgnoreHunts to True.
Links are created for clients and flows, but in order for these to work, you
need to set the [server metadata](/app/index.html?org_id=root#/host/server)
field "VelociraptorServerURL". If not set, https://127.0.0.1:8889 is used.
type: NOTEBOOK
sources:
- notebook:
- type: markdown
template: |
# Recent flows (all orgs)
- type: vql
output: |
<< Latest flows: Click here to customise and calculate >>
template: |
LET ColumnTypes = dict(`Client`='url', `Flow`='url')
LET IgnoreHunts = True
-- In order to create links to clients and flows, the server URL is needed
-- (relative links do not work when specifying org ID). The server metadata
-- field "VelociraptorServerURL", used by some other artifacts, is used for
-- this:
LET ServerURL = get(
item=server_metadata(),
field='VelociraptorServerURL',
default='https://127.0.0.1:8889')
/*
Last refreshed at {{ Get ( Query "SELECT timestamp(epoch=now()) AS Refreshed FROM scope()" | Expand ) "0.Refreshed" }}
*/
SELECT *
FROM foreach(
row={
SELECT Name,
OrgId
FROM orgs()
},
query={
SELECT
Name AS Org,
*
FROM query(
org_id=OrgId,
-- Pass these variables to the scope:
env=dict(IgnoreHunts=IgnoreHunts, ServerURL=ServerURL),
query={
SELECT *
FROM foreach(
row={
SELECT client_id,
os_info.hostname AS Hostname,
timestamp(epoch=first_seen_at) AS FirstSeenAt
FROM clients()
},
query={
SELECT
format(
format='[%v](%v/app/index.html?org_id="%v"#/host/%v)',
args=[Hostname, ServerURL, org().id, client_id]) AS Client,
-- It may be useful to know whether the collection was run
-- because the client was new at the time:
timestamp(
epoch=start_time).Unix - FirstSeenAt.Unix < 60 AS _NewClient,
format(
format='[%v](%v/app/index.html?org_id="%v"#/collected/%v/%v)',
-- Use the first artifact name (capped to 30 chars) as link name:
args=[request.artifacts[0][:30] + '…', ServerURL, org().id, client_id, session_id]) AS Flow,
session_id =~ '.H$' AS _IsHunt,
timestamp(
epoch=create_time) AS Created,
timestamp(
epoch=active_time) AS LastActive,
request.creator AS Creator,
state AS State,
status AS Status,
-- Create a more readable dict with artifact parameters arguments,
-- using the artifact name as key, and as value, a dict with parameter
-- name and values):
to_dict(
item={
SELECT
artifact AS _key,
to_dict(
item={
SELECT
key AS _key,
value AS _value
FROM foreach(
row=parameters.env)
}) AS _value
FROM foreach(
row=request.specs)
}) AS _Requested,
artifacts_with_results AS WithResults,
format(
format='%.1f',
args=[execution_duration / 1000000000.0]) AS _Duration,
total_collected_rows AS _Rows,
total_uploaded_files AS _FilesUploaded
FROM flows(client_id=client_id)
WHERE NOT IgnoreHunts OR NOT session_id =~ '.H$'
ORDER BY Created DESC
LIMIT 5
},
-- This query is ideal for parallel execution (it is also necessary):
workers=50)
})
})
ORDER BY Created DESC
LIMIT 50