Notebooks.Admin.Flows

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