This page displays information about the Velociraptor built in artifacts. There are 38 artifacts in total. Use the navigation menu to the right to quickly skip to the right artifact definition. Definitions may be expanded to view the VQL source.

Generic.Client.Stats

An Event artifact which generates client’s CPU and memory statistics.

name: Generic.Client.Stats
description: An Event artifact which generates client's CPU and memory statistics.
parameters:
  - name: Frequency
    description: Return stats every this many seconds.
    default: "10"

sources:
  - queries:
      - |
        SELECT UnixNano FROM clock(period=atoi(string=Frequency))

Linux.Applications.Chrome.Extensions

Fetch Chrome extensions.

Chrome extensions are installed into the user’s home directory. We search for manifest.json files in a known path within each system user’s home directory. We then parse the manifest file as JSON.

Many extensions use locale packs to resolve strings like name and description. In this case we detect the default locale and load those locale files. We then resolve the extension’s name and description from there.

name: Linux.Applications.Chrome.Extensions
description: |
  Fetch Chrome extensions.

  Chrome extensions are installed into the user's home directory.  We
  search for manifest.json files in a known path within each system
  user's home directory. We then parse the manifest file as JSON.

  Many extensions use locale packs to resolve strings like name and
  description. In this case we detect the default locale and load
  those locale files. We then resolve the extension's name and
  description from there.

parameters:
  - name: extensionGlobs
    default: /.config/google-chrome/*/Extensions/*/*/manifest.json
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        /* For each user on the system, search for extension manifests
           in their home directory. */
        LET extension_manifests = SELECT * from foreach(
          row={
             SELECT Uid, User, Homedir from Artifact.Linux.Sys.Users()
          },
          query={
             SELECT FullPath, Mtime, Ctime, User, Uid from glob(
               globs=Homedir + '/' + extensionGlobs)
          })

      - |
        /* If the Manifest declares a default_locale then we
           load and parse the messages file. In this case the
           messages are actually stored in the locale file
           instead of the main manifest.json file.
        */
        LET maybe_read_locale_file =
           SELECT * from if(
              condition={
                 select * from scope() where Manifest.default_locale
              },
              then={
                 SELECT Manifest,
                        Uid, User,
                        Filename as LocaleFilename,
                        ManifestFilename,
                        parse_json(data=Data) AS LocaleManifest
                 FROM read_file(
                         -- Munge the filename to get the messages.json path.
                         filenames=regex_replace(
                           source=ManifestFilename,
                           replace="/_locales/" + Manifest.default_locale +
                                   "/messages.json",
                           re="/manifest.json$"))
              },
              else={
                  -- Just fill in empty Locale results.
                  SELECT Manifest,
                         Uid, User,
                         "" AS LocaleFilename,
                         "" AS ManifestFilename,
                         "" AS LocaleManifest
                  FROM scope()
              })

      - |
        LET parse_json_files = SELECT * from foreach(
           row={
             SELECT Filename as ManifestFilename,
                    Uid, User,
                    parse_json(data=Data) as Manifest
             FROM read_file(filenames=FullPath)
           },
           query=maybe_read_locale_file)

      - |
        LET parsed_manifest_files = SELECT * from foreach(
          row=extension_manifests,
          query=parse_json_files)

      - |
        SELECT Uid, User,

               /* If the manifest name contains __MSG_ then the real
                  name is stored in the locale manifest. This condition
                  resolves the Name column either to the main manifest or
                  the locale manifest.
               */
               if(condition="__MSG_" in Manifest.name,
                  then=get(item=LocaleManifest,
                     member=regex_replace(
                        source=Manifest.name,
                        replace="$1",
                        re="(?:__MSG_(.+)__)")).message,
                  else=Manifest.name) as Name,

               if(condition="__MSG_" in Manifest.description,
                  then=get(item=LocaleManifest,
                     member=regex_replace(
                        source=Manifest.description,
                        replace="$1",
                        re="(?:__MSG_(.+)__)")).message,
                  else=Manifest.description) as Description,

               /* Get the Identifier and Version from the manifest filename */
               regex_replace(
                 source=ManifestFilename,
                 replace="$1",
                 re="(?:.+Extensions/([^/]+)/([^/]+)/manifest.json)$") AS Identifier,
               regex_replace(
                 source=ManifestFilename,
                 replace="$2",
                 re="(?:.+Extensions/([^/]+)/([^/]+)/manifest.json)$") AS Version,

               Manifest.author as Author,
               Manifest.background.persistent AS Persistent,
               regex_replace(
                 source=ManifestFilename,
                 replace="$1",
                 re="(.+Extensions/.+/)manifest.json$") AS Path,

               Manifest.oauth2.scopes as Scopes,
               Manifest.permissions as Permissions,
               Manifest.key as Key

        FROM parsed_manifest_files

Linux.Applications.Chrome.Extensions.Upload

Upload all users chrome extension.

We dont bother actually parsing anything here, we just grab all the extension files in user’s home directory.

name: Linux.Applications.Chrome.Extensions.Upload
description: |
  Upload all users chrome extension.

  We dont bother actually parsing anything here, we just grab all the
  extension files in user's home directory.

parameters:
  - name: extensionGlobs
    default: /.config/google-chrome/*/Extensions/**
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        /* For each user on the system, search for extension files
           in their home directory and upload them. */
        SELECT * from foreach(
          row={
             SELECT Uid, User, Homedir from Artifact.Linux.Sys.Users()
          },
          query={
             SELECT FullPath, Mtime, Ctime, User, Uid,
                    upload(file=FullPath) as Upload
             FROM glob(globs=Homedir + '/' + extensionGlobs)
          })

Linux.Applications.Docker.Info

Get Dockers info by connecting to its socket.

name: Linux.Applications.Docker.Info
description: Get Dockers info by connecting to its socket.
parameters:
  - name: dockerSocket
    description: |
      Docker server socket. You will normally need to be root to connect.
    default: /var/run/docker.sock
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        LET data = SELECT parse_json(data=Content) as JSON
        FROM http_client(url=dockerSocket + ":unix/info")
      - |
        SELECT JSON.ID as ID,
               JSON.Containers as Containers,
               JSON.ContainersRunning as ContainersRunning,
               JSON.ContainersPaused as ContainersPaused,
               JSON.ContainersStopped as ContainersStopped,
               JSON.Images as Images,
               JSON.Driver as Driver,
               JSON.MemoryLimit as MemoryLimit,
               JSON.SwapLimit as SwapLimit,
               JSON.KernelMemory as KernelMemory,
               JSON.CpuCfsPeriod as CpuCfsPeriod,
               JSON.CpuCfsQuota as CpuCfsQuota,
               JSON.CPUShares as CPUShares,
               JSON.CPUSet as CPUSet,
               JSON.IPv4Forwarding as IPv4Forwarding,
               JSON.BridgeNfIptables as BridgeNfIptables,
               JSON.BridgeNfIp6tables as BridgeNfIp6tables,
               JSON.OomKillDisable as OomKillDisable,
               JSON.LoggingDriver as LoggingDriver,
               JSON.CgroupDriver as CgroupDriver,
               JSON.KernelVersion as KernelVersion,
               JSON.OperatingSystem as OperatingSystem,
               JSON.OSType as OSType,
               JSON.Architecture as Architecture,
               JSON.NCPU as NCPU,
               JSON.MemTotal as MemTotal,
               JSON.HttpProxy as HttpProxy,
               JSON.HttpsProxy as HttpsProxy,
               JSON.NoProxy as NoProxy,
               JSON.Name as Name,
               JSON.ServerVersion as ServerVersion,
               JSON.DockerRootDir as DockerRootDir
        FROM data

Linux.Applications.Docker.Version

Get Dockers version by connecting to its socket.

name: Linux.Applications.Docker.Version
description: Get Dockers version by connecting to its socket.
parameters:
  - name: dockerSocket
    description: |
      Docker server socket. You will normally need to be root to connect.
    default: /var/run/docker.sock
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        LET data = SELECT parse_json(data=Content) as JSON
        FROM http_client(url=dockerSocket + ":unix/version")
      - |
        SELECT JSON.Version as Version,
               JSON.ApiVersion as ApiVersion,
               JSON.MinAPIVersion as MinAPIVersion,
               JSON.GitCommit as GitCommit,
               JSON.GoVersion as GoVersion,
               JSON.Os as Os,
               JSON.Arch as Arch,
               JSON.KernelVersion as KernelVersion,
               JSON.BuildTime as BuildTime
        FROM data

Linux.Debian.AptSources

Parse Debian apt sources.

We first search for *.list files which contain lines of the form

deb http://us.archive.ubuntu.com/ubuntu/ bionic main restricted

For each line we construct the cache file by spliting off the section (last component) and replacing / and ” ” with _.

We then try to open the file. If the file exists we parse some metadata from it. If not we leave those columns empty.

name: Linux.Debian.AptSources
description: |
  Parse Debian apt sources.

  We first search for \*.list files which contain lines of the form

  .. code:: console

     deb http://us.archive.ubuntu.com/ubuntu/ bionic main restricted

  For each line we construct the cache file by spliting off the
  section (last component) and replacing / and " " with _.

  We then try to open the file. If the file exists we parse some
  metadata from it. If not we leave those columns empty.

reference: "https://osquery.io/schema/3.2.6#apt_sources"
parameters:
  - name: linuxAptSourcesGlobs
    description: Globs to find apt source *.list files.
    default: /etc/apt/sources.list,/etc/apt/sources.list.d/*.list
  - name:  aptCacheDirectory
    description: Location of the apt cache directory.
    default: /var/lib/apt/lists/
sources:
  - precondition:
      SELECT OS From info() where OS = 'linux'
    queries:
       - |
         /* Search for files which may contain apt sources. The user can
            pass new globs here. */
         LET files = SELECT FullPath from glob(
           globs=split(string=linuxAptSourcesGlobs, sep=","))

       - |
         /* Read each line in the sources which is not commented.
            Deb lines look like:
            deb [arch=amd64] http://dl.google.com/linux/chrome-remote-desktop/deb/ stable main
            Contains URL, base_uri and components.
         */
         LET deb_sources = SELECT *
           FROM parse_records_with_regex(
             file=files.FullPath,
             regex="(?m)^ *(?P<Type>deb(-src)?) (?:\\[arch=(?P<Arch>[^\\]]+)\\] )?" +
                  "(?P<URL>https?://(?P<base_uri>[^ ]+))" +
                  " +(?P<components>.+)")

       - |
         /* We try to get at the Release file in /var/lib/apt/ by munging
           the components and URL.
           Strip the last component off, convert / and space to _ and
           add _Release to get the filename.
         */
         LET parsed_apt_lines = SELECT Arch, URL,
            base_uri + " " + components as Name, Type,
            FullPath as Source, aptCacheDirectory + regex_replace(
              replace="_",
              re="_+",
              source=regex_replace(
                replace="_", re="[ /]",
                source=base_uri + "_dists_" + regex_replace(
                   source=components,
                   replace="", re=" +[^ ]+$")) + "_Release"
              )  as cache_file
         FROM deb_sources

       - |
         /* This runs if the file was found. Read the entire file into
            memory and parse the same record using multiple RegExps.
         */
         LET parsed_cache_files = SELECT Name, Arch, URL, Type,
           Source, parse_string_with_regex(
                string=Record,
                regex=["Codename: (?P<Release>[^\\s]+)",
                       "Version: (?P<Version>[^\\s]+)",
                       "Origin: (?P<Maintainer>[^\\s]+)",
                       "Architectures: (?P<Architectures>[^\\s]+)",
                       "Components: (?P<Components>[^\\s]+)"]) as Record
           FROM parse_records_with_regex(file=cache_file, regex="(?sm)(?P<Record>.+)")

       - |
         // Foreach row in the parsed cache file, collect the FileInfo too.
         LET add_stat_to_parsed_cache_file = SELECT * from foreach(
           query={
             SELECT FullPath, Mtime, Ctime, Atime, Record, Type,
               Name, Arch, URL, Source from stat(filename=cache_file)
           }, row=parsed_cache_files)

       - |
         /* For each row in the parsed file, run the appropriate query
            depending on if the cache file exists.
            If the cache file is not found, we just copy the lines we
            parsed from the source file and fill in empty values for
            stat.
         */
         LET parse_cache_or_pass = SELECT * from if(
           condition={
              SELECT * from stat(filename=cache_file)
           },
           then=add_stat_to_parsed_cache_file,
           else={
             SELECT Source, dict() as Mtime, dict() as Ctime,
               dict() as Atime, Type,
               dict() as Record, Arch, URL, Name from scope()
           })

       - |
         -- For each parsed apt .list file line produce some output.
         SELECT * from foreach(
             row=parsed_apt_lines,
             query=parse_cache_or_pass)

Linux.Debian.Packages

Parse dpkg status file.

name: Linux.Debian.Packages
description: Parse dpkg status file.
parameters:
  - name: linuxDpkgStatus
    default: /var/lib/dpkg/status
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        /* First pass - split file into records start with
           Package and end with \n\n.

           Then parse each record using multiple RegExs.
        */
        LET packages = SELECT parse_string_with_regex(
            string=Record,
            regex=['Package:\\s(?P<Package>.+)',
                   'Installed-Size:\\s(?P<InstalledSize>.+)',
                   'Version:\\s(?P<Version>.+)',
                   'Source:\\s(?P<Source>.+)',
                   'Architecture:\\s(?P<Architecture>.+)']) as Record
            FROM parse_records_with_regex(
                   file=linuxDpkgStatus,
                   regex='(?sm)^(?P<Record>Package:.+?)\\n\\n')
      - |
        SELECT Record.Package as Package,
               Record.InstalledSize as InstalledSize,
               Record.Version as Version,
               Record.Source as Source,
               Record.Architecture as Architecture from packages

Linux.Mounts

List mounted filesystems by reading /proc/mounts

name: Linux.Mounts
description: List mounted filesystems by reading /proc/mounts
parameters:
  - name: ProcMounts
    default: /proc/mounts
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        SELECT Device, Mount, FSType, split(string=Opts, sep=",") As Options
               FROM parse_records_with_regex(
                   file=ProcMounts,
                   regex='(?m)^(?P<Device>[^ ]+) (?P<Mount>[^ ]+) (?P<FSType>[^ ]+) '+
                         '(?P<Opts>[^ ]+)')

Linux.Proc.Arp

ARP table via /proc/net/arp.

name: Linux.Proc.Arp
description: ARP table via /proc/net/arp.
parameters:
  - name: ProcNetArp
    default: /proc/net/arp
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'

    queries:
      - |
        SELECT * from split_records(
           filenames=ProcNetArp,
           regex='\\s{3,20}',
           first_row_is_headers=true)

Linux.Proc.Modules

Module listing via /proc/modules.

name: Linux.Proc.Modules
description: Module listing via /proc/modules.
parameters:
  - name: ProcModules
    default: /proc/modules
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'

    queries:
      - |
        SELECT * from split_records(
           filenames=ProcModules,
           regex='\\s+',
           columns=['Name', 'Size', 'UseCount', 'UsedBy', 'Status', 'Address'])

Linux.Ssh.AuthorizedKeys

Find and parse ssh authorized keys files.

name: Linux.Ssh.AuthorizedKeys
description: Find and parse ssh authorized keys files.
parameters:
  - name: sshKeyFiles
    default: '.ssh/authorized_keys*'
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        // For each user on the system, search for authorized_keys files.
        LET authorized_keys = SELECT * from foreach(
          row={
             SELECT Uid, User, Homedir from Artifact.Linux.Sys.Users()
          },
          query={
             SELECT FullPath, Mtime, Ctime, User, Uid from glob(
               globs=Homedir + '/' + sshKeyFiles)
          })
      - |
        // For each authorized keys file, extract each line on a different row.
        // Note: This duplicates the path, user and uid on each key line.
        SELECT * from foreach(
          row=authorized_keys,
          query={
            SELECT Uid, User, FullPath, Key from split_records(
               filenames=FullPath, regex="\n", columns=["Key"])
          })

Linux.Ssh.KnownHosts

Find and parse ssh known hosts files.

name: Linux.Ssh.KnownHosts
description: Find and parse ssh known hosts files.
parameters:
  - name: sshKnownHostsFiles
    default: '.ssh/known_hosts*'
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        // For each user on the system, search for known_hosts files.
        LET authorized_keys = SELECT * from foreach(
          row={
             SELECT Uid, User, Homedir from Artifact.Linux.Sys.Users()
          },
          query={
             SELECT FullPath, Mtime, Ctime, User, Uid from glob(
               globs=Homedir + '/' + sshKnownHostsFiles)
          })
      - |
        // For each known_hosts file, extract each line on a different row.
        SELECT * from foreach(
          row=authorized_keys,
          query={
            SELECT Uid, User, FullPath, Line from split_records(
               filenames=FullPath, regex="\n", columns=["Line"])
            /* Ignore comment lines. */
            WHERE not Line =~ "^[^#]+#"
          })

Linux.Sys.ACPITables

Firmware ACPI functional table common metadata and content.

name: Linux.Sys.ACPITables
description: Firmware ACPI functional table common metadata and content.
reference: https://osquery.io/schema/3.2.6#acpi_tables
parameters:
  - name: kLinuxACPIPath
    default: /sys/firmware/acpi/tables
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        LET hashes = SELECT Name, Size, hash(path=FullPath) as Hash
                     FROM glob(globs=kLinuxACPIPath + '/*')
      - |
        SELECT Name, Size, Hash.MD5, Hash.SHA1, Hash.SHA256 from hashes

Linux.Sys.CPUTime

Displays information from /proc/stat file about the time the cpu cores spent in different parts of the system.

name: Linux.Sys.CPUTime
description: |
  Displays information from /proc/stat file about the time the cpu
  cores spent in different parts of the system.
parameters:
  - name: procStat
    default: /proc/stat
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        LET raw = SELECT * FROM split_records(
           filenames=procStat,
           regex=' +',
           columns=['core', 'user', 'nice', 'system',
                    'idle', 'iowait', 'irq', 'softirq',
                    'steal', 'guest', 'guest_nice'])
        WHERE core =~ 'cpu.+'
      - |
        SELECT core AS Core,
               atoi(string=user) as User,
               atoi(string=nice) as Nice,
               atoi(string=system) as System,
               atoi(string=idle) as Idle,
               atoi(string=iowait) as IOWait,
               atoi(string=irq) as IRQ,
               atoi(string=softirq) as SoftIRQ,
               atoi(string=steal) as Steal,
               atoi(string=guest) as Guest,
               atoi(string=guest_nice) as GuestNice FROM raw

Linux.Sys.Crontab

Displays parsed information from crontab.

name: Linux.Sys.Crontab
description: |
  Displays parsed information from crontab.
parameters:
  - name: cronTabGlob
    default: /etc/crontab,/etc/cron.d/**,/var/at/tabs/**,/var/spool/cron/**,/var/spool/cron/crontabs/**
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        LET raw = SELECT * FROM foreach(
          row={
            SELECT FullPath from glob(globs=split(string=cronTabGlob, sep=","))
          },
          query={
            SELECT FullPath, data, parse_string_with_regex(
              string=data,
              regex=[
                 /* Regex for event (Starts with @) */
                 "^(?P<Event>@[a-zA-Z]+)\\s+(?P<Command>.+)",

                 /* Regex for regular command. */
                 "^(?P<Minute>[^\\s]+)\\s+"+
                 "(?P<Hour>[^\\s]+)\\s+"+
                 "(?P<DayOfMonth>[^\\s]+)\\s+"+
                 "(?P<Month>[^\\s]+)\\s+"+
                 "(?P<DayOfWeek>[^\\s]+)\\s+"+
                 "(?P<Command>.+)$"]) as Record

            /* Read lines from the file and filter ones that start with "#" */
            FROM split_records(
               filenames=FullPath,
               regex="\n", columns=["data"]) WHERE not data =~ "^\\s*#"
            }) WHERE Record.Command

      - |
        SELECT Record.Event AS Event,
               Record.Minute AS Minute,
               Record.Hour AS Hour,
               Record.DayOfMonth AS DayOfMonth,
               Record.Month AS Month,
               Record.DayOfWeek AS DayOfWeek,
               Record.Command AS Command,
               FullPath AS Path
        FROM raw

Linux.Sys.LastUserLogin

Find and parse system wtmp files. This indicate when the user last logged in.

name: Linux.Sys.LastUserLogin
description: Find and parse system wtmp files. This indicate when the
             user last logged in.
parameters:
  - name: wtmpGlobs
    default: /var/log/wtmp*

    # This is automatically generated from dwarf symbols by Rekall:
    # gcc -c -g -o /tmp/test.o /tmp/1.c
    # rekall dwarfparser /tmp/test.o

    # And 1.c is:
    # #include "utmp.h"
    # struct utmp x;

  - name: wtmpProfile
    default: |
       {
         "timeval": [8, {
          "tv_sec": [0, ["int"]],
          "tv_usec": [4, ["int"]]
         }],
         "exit_status": [4, {
          "e_exit": [2, ["short int"]],
          "e_termination": [0, ["short int"]]
         }],
         "timezone": [8, {
          "tz_dsttime": [4, ["int"]],
          "tz_minuteswest": [0, ["int"]]
         }],
         "utmp": [384, {
          "__glibc_reserved": [364, ["Array", {
           "count": 20,
           "target": "char",
           "target_args": null
          }]],
          "ut_addr_v6": [348, ["Array", {
           "count": 4,
           "target": "int",
           "target_args": null
          }]],
          "ut_exit": [332, ["exit_status"]],
          "ut_host": [76, ["String", {
           "length": 256
          }]],
          "ut_id": [40, ["String", {
           "length": 4
          }]],
          "ut_line": [8, ["String", {
           "length": 32
          }]],
          "ut_pid": [4, ["int"]],
          "ut_session": [336, ["int"]],
          "ut_tv": [340, ["timeval"]],
          "ut_type": [0, ["Enumeration", {
            "target": "short int",
            "choices": {
               "0": "EMPTY",
               "1": "RUN_LVL",
               "2": "BOOT_TIME",
               "5": "INIT_PROCESS",
               "6": "LOGIN_PROCESS",
               "7": "USER_PROCESS",
               "8": "DEAD_PROCESS"
             }
          }]],
          "ut_user": [44, ["String", {
           "length": 32
          }]]
         }]
       }

sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        SELECT * from foreach(
          row={
            SELECT FullPath from glob(globs=split(string=wtmpGlobs, sep=","))
          },
          query={
            SELECT ut_type, ut_id, ut_host.AsString as Host,
                   ut_user.AsString as User,
                   timestamp(epoch=ut_tv.tv_sec.AsInteger) as login_time
            FROM binary_parse(
                   file=FullPath,
                   profile=wtmpProfile,
                   target="Array",
                   args=dict(Target="utmp")
                 )
          })

Linux.Sys.Users

Get User specific information like homedir, group etc from /etc/passwd.

name: Linux.Sys.Users
description: Get User specific information like homedir, group etc from /etc/passwd.
parameters:
  - name: PasswordFile
    default: /etc/passwd
    description: The location of the password file.
sources:
  - precondition: |
      SELECT OS From info() where OS = 'linux'
    queries:
      - |
        SELECT User, Description, Uid, Gid, Homedir, Shell
          FROM parse_records_with_regex(
            file=PasswordFile,
            regex='(?m)^(?P<User>[^:]+):([^:]+):' +
                  '(?P<Uid>[^:]+):(?P<Gid>[^:]+):(?P<Description>[^:]*):' +
                  '(?P<Homedir>[^:]+):(?P<Shell>[^:\\s]+)')

Network.ExternalIpAddress

Detect the external ip address of the end point.

name: Network.ExternalIpAddress
description: Detect the external ip address of the end point.
parameters:
  - name: externalUrl
    default: http://www.myexternalip.com/raw
    description: The URL of the external IP detection site.
sources:
  - precondition: SELECT * from info()
    queries:
      - SELECT Content as IP from http_client(url=externalUrl)

Windows.Applications.ChocolateyPackages

Chocolatey packages installed in a system.

name: Windows.Applications.ChocolateyPackages
description: Chocolatey packages installed in a system.
parameters:
  - name: ChocolateyInstall
    default: ""

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET files =
          SELECT FullPath, parse_xml(file=FullPath) AS Metadata
          -- Use the ChocolateyInstall parameter if it is set.
          FROM glob(globs=if(
             condition=ChocolateyInstall,
             then=ChocolateyInstall,
             -- Otherwise just use the environment.
             else=environ(var='ChocolateyInstall')) + '/lib/*/*.nuspec')
      - |
        SELECT * FROM if(
        condition={
            SELECT * FROM if(
               condition=ChocolateyInstall,
               then=ChocolateyInstall,
               else=environ(var="ChocolateyInstall"))
          },
        then={
            SELECT FullPath,
                   Metadata.package.metadata.id as Name,
                   Metadata.package.metadata.version as Version,
                   Metadata.package.metadata.summary as Summary,
                   Metadata.package.metadata.authors as Authors,
                   Metadata.package.metadata.licenseUrl as License
            FROM files
        })

Windows.Applications.Chrome.Extensions

Fetch Chrome extensions.

Chrome extensions are installed into the user’s home directory. We search for manifest.json files in a known path within each system user’s home directory. We then parse the manifest file as JSON.

Many extensions use locale packs to resolve strings like name and description. In this case we detect the default locale and load those locale files. We then resolve the extension’s name and description from there.

name: Windows.Applications.Chrome.Extensions
description: |
  Fetch Chrome extensions.

  Chrome extensions are installed into the user's home directory.  We
  search for manifest.json files in a known path within each system
  user's home directory. We then parse the manifest file as JSON.

  Many extensions use locale packs to resolve strings like name and
  description. In this case we detect the default locale and load
  those locale files. We then resolve the extension's name and
  description from there.

parameters:
  - name: extensionGlobs
    default: \AppData\Local\Google\Chrome\User Data\*\Extensions\*\*\manifest.json
sources:
  - precondition: |
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        /* For each user on the system, search for extension manifests
           in their home directory. */
        LET extension_manifests = SELECT * from foreach(
          row={
             SELECT Uid, Name AS User, Directory from Artifact.Windows.Sys.Users()
          },
          query={
             SELECT FullPath, Mtime, Ctime, User, Uid from glob(
               globs=Directory + extensionGlobs)
          })

      - |
        /* If the Manifest declares a default_locale then we
           load and parse the messages file. In this case the
           messages are actually stored in the locale file
           instead of the main manifest.json file.
        */
        LET maybe_read_locale_file =
           SELECT * from if(
              condition={
                 select * from scope() where Manifest.default_locale
              },
              then={
                 SELECT Manifest,
                        Uid, User,
                        Filename as LocaleFilename,
                        ManifestFilename,
                        parse_json(data=Data) AS LocaleManifest
                 FROM read_file(
                         -- Munge the filename to get the messages.json path.
                         filenames=regex_replace(
                           source=ManifestFilename,
                           replace="\\_locales\\" + Manifest.default_locale +
                                   "\\messages.json",
                           re="\\\\manifest.json$"))
              },
              else={
                  -- Just fill in empty Locale results.
                  SELECT Manifest,
                         Uid, User,
                         "" AS LocaleFilename,
                         "" AS ManifestFilename,
                         "" AS LocaleManifest
                  FROM scope()
              })

      - |
        LET parse_json_files = SELECT * from foreach(
           row={
             SELECT Filename as ManifestFilename,
                    Uid, User,
                    parse_json(data=Data) as Manifest
             FROM read_file(filenames=FullPath)
           },
           query=maybe_read_locale_file)

      - |
        LET parsed_manifest_files = SELECT * from foreach(
          row=extension_manifests,
          query=parse_json_files)

      - |
        SELECT Uid, User,

               /* If the manifest name contains __MSG_ then the real
                  name is stored in the locale manifest. This condition
                  resolves the Name column either to the main manifest or
                  the locale manifest.
               */
               if(condition="__MSG_" in Manifest.name,
                  then=get(item=LocaleManifest,
                     member=regex_replace(
                        source=Manifest.name,
                        replace="$1",
                        re="(?:__MSG_(.+)__)")).message,
                  else=Manifest.name) as Name,

               if(condition="__MSG_" in Manifest.description,
                  then=get(item=LocaleManifest,
                     member=regex_replace(
                        source=Manifest.description,
                        replace="$1",
                        re="(?:__MSG_(.+)__)")).message,
                  else=Manifest.description) as Description,

               /* Get the Identifier and Version from the manifest filename */
               regex_replace(
                 source=ManifestFilename,
                 replace="$1",
                 re="(?:.+Extensions\\\\([^\\\\]+)\\\\([^\\\\]+)\\\\manifest.json)$") AS Identifier,
               regex_replace(
                 source=ManifestFilename,
                 replace="$2",
                 re="(?:.+Extensions\\\\([^\\\\]+)\\\\([^\\\\]+)\\\\manifest.json)$") AS Version,

               Manifest.author as Author,
               Manifest.background.persistent AS Persistent,
               regex_replace(
                 source=ManifestFilename,
                 replace="$1",
                 re="(.+Extensions\\\\.+\\\\)manifest.json$") AS Path,

               Manifest.oauth2.scopes as Scopes,
               Manifest.permissions as Permissions,
               Manifest.key as Key

        FROM parsed_manifest_files

Windows.Events.ProcessCreation

Collect all process creation events.

name: Windows.Events.ProcessCreation
description: |
  Collect all process creation events.
parameters:
  - name: wmiQuery
    default: SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE
      TargetInstance ISA 'Win32_Process'
  - name: eventQuery
    default: SELECT * FROM Win32_ProcessStartTrace

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        // Convert the timestamp from WinFileTime to Epoch.
        SELECT timestamp(epoch=atoi(string=Parse.TIME_CREATED) / 10000000 - 11644473600 ) as Timestamp,
               Parse.ParentProcessID as PPID,
               Parse.ProcessID as PID,
               Parse.ProcessName as Name, {
                 SELECT CommandLine
                 FROM wmi(
                   query="SELECT * FROM Win32_Process WHERE ProcessID = " +
                    format(format="%v", args=Parse.ProcessID),
                   namespace="ROOT/CIMV2")
               } AS CommandLine
        FROM wmi_events(
           query=eventQuery,
           wait=5000000,   // Do not time out.
           namespace="ROOT/CIMV2")

Windows.Events.ServiceCreation

Monitor for creation of new services.

New services are typically created by installing new software or kernel drivers. Attackers will sometimes install a new service to either insert a malicious kernel driver or as a persistence mechanism.

This event monitor extracts the service creation events from the event log and records them on the server.

name: Windows.Events.ServiceCreation
description: |
  Monitor for creation of new services.

  New services are typically created by installing new software or
  kernel drivers. Attackers will sometimes install a new service to
  either insert a malicious kernel driver or as a persistence
  mechanism.

  This event monitor extracts the service creation events from the
  event log and records them on the server.
parameters:
  - name: systemLogFile
    default: >-
      C:/Windows/System32/Winevt/Logs/System.evtx

sources:
  - queries:
      - |
        SELECT System.TimeCreated.SystemTime as Timestamp,
               System.EventID.Value as EventID,
               EventData.ImagePath as ImagePath,
               EventData.ServiceName as ServiceName,
               EventData.ServiceType as Type,
               EventData as _EventData
        FROM watch_evtx(filename=systemLogFile) WHERE EventID = '7045'

Windows.Network.ArpCache

Address resolution cache, both static and dynamic (from ARP, NDP).

name: Windows.Network.ArpCache
description: Address resolution cache, both static and dynamic (from ARP, NDP).
parameters:
  - name: wmiQuery
    default: |
      SELECT AddressFamily, Store, State, InterfaceIndex, IPAddress,
             InterfaceAlias, LinkLayerAddress
      from MSFT_NetNeighbor
  - name: wmiNamespace
    default: ROOT\StandardCimv2

  - name: kMapOfState
    default: |
     {
      "0": "Unreachable",
      "1": "Incomplete",
      "2": "Probe",
      "3": "Delay",
      "4": "Stale",
      "5": "Reachable",
      "6": "Permanent",
      "7": "TBD"
     }

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET interfaces <=
          SELECT Index, HardwareAddr, IP
          FROM Artifact.Windows.Network.InterfaceAddresses()

      - |
        LET arp_cache = SELECT if(condition=AddressFamily=23,
                    then="IPv6",
                  else=if(condition=AddressFamily=2,
                    then="IPv4",
                  else=AddressFamily)) as AddressFamily,

               if(condition=Store=0,
                    then="Persistent",
                  else=if(condition=(Store=1),
                    then="Active",
                  else="?")) as Store,

               get(item=parse_json(data=kMapOfState),
                   member=encode(string=State, type='string')) AS State,
               InterfaceIndex, IPAddress,
               InterfaceAlias, LinkLayerAddress
            FROM wmi(query=wmiQuery, namespace=wmiNamespace)
      - |
        SELECT * FROM foreach(
          row=arp_cache,
          query={
             SELECT AddressFamily, Store, State, InterfaceIndex,
                    IP AS LocalAddress, HardwareAddr, IPAddress as RemoteAddress,
                    InterfaceAlias, LinkLayerAddress AS RemoteMACAddress
             FROM interfaces
             WHERE InterfaceIndex = Index
          })

Windows.Network.InterfaceAddresses

Network interfaces and relevant metadata.

name: Windows.Network.InterfaceAddresses
description: Network interfaces and relevant metadata.
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET interface_address =
           SELECT Index, MTU, Name, HardwareAddr, Flags, Addrs
           from interfaces()

      - |
        SELECT Index, MTU, Name, HardwareAddr,
           Flags, Addrs.IP as IP, Addrs.Mask as Mask
        FROM flatten(query=interface_address)

Windows.Network.ListeningPorts

Processes with listening (bound) network sockets/ports.

name: Windows.Network.ListeningPorts
description: Processes with listening (bound) network sockets/ports.
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET process <= SELECT Name, Pid from pslist()

      - |
        SELECT * from foreach(
          row={
            SELECT Pid AS PortPid, Laddr.Port AS Port,
                   TypeString as Protocol, FamilyString as Family,
                   Laddr.IP as Address
            FROM netstat() where Status = 'LISTEN'
          },
          query={
            SELECT Pid, Name, Port, Protocol, Family, Address
            FROM process where Pid = PortPid
          })

Windows.Network.Netstat

Show information about open sockets. On windows the time when the socket was first bound is also shown.

name: Windows.Network.Netstat
description: |
  Show information about open sockets. On windows the time when the
  socket was first bound is also shown.

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT Pid, FamilyString as Family,
               TypeString as Type,
               Status,
               Laddr.IP, Laddr.Port,
               Raddr.IP, Raddr.Port,
               Timestamp
               FROM netstat()

Windows.Packs.Autoexec

Aggregate of executables that will automatically execute on the target machine. This is an amalgamation of other tables like services, scheduled_tasks, startup_items and more.

name: Windows.Packs.Autoexec
description: |
  Aggregate of executables that will automatically execute on the
  target machine. This is an amalgamation of other tables like
  services, scheduled_tasks, startup_items and more.

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT * from chain(
          q1={
            SELECT Name, Command AS Path, "StartupItems" as Source
            FROM Artifact.Windows.Sys.StartupItems()
          })

Windows.Persistence.PermanentWMIEvents

Malware often registers a permanent event listener within WMI. When the event fires, the WMI system itself will invoke the consumer to handle the event. The malware does not need to be running at the time the event fires. Malware can use this mechanism to re-infect the machine for example.

name: Windows.Persistence.PermanentWMIEvents
description: |
   Malware often registers a permanent event listener within WMI. When
   the event fires, the WMI system itself will invoke the consumer to
   handle the event. The malware does not need to be running at the
   time the event fires. Malware can use this mechanism to re-infect
   the machine for example.

parameters:
  - name: namespace
    default: root/subscription

sources:
 - precondition:
     SELECT OS from info() where OS = "windows"
   queries:
   - |
     LET FilterToConsumerBinding = SELECT parse_string_with_regex(
        string=Consumer,
        regex=['((?P<namespace>^[^:]+):)?(?P<Type>.+?)\\.Name="(?P<Name>.+)"']) as Consumer,
          parse_string_with_regex(
        string=Filter,
        regex=['((?P<namespace>^[^:]+):)?(?P<Type>.+?)\\.Name="(?P<Name>.+)"']) as Filter
     FROM wmi(
         query="SELECT * FROM __FilterToConsumerBinding",
         namespace=namespace)
   - |
     SELECT {
       SELECT * FROM wmi(
          query="SELECT * FROM " + Consumer.Type,
          namespace=if(condition=Consumer.namespace,
              then=Consumer.namespace,
              else=namespace)) WHERE Name = Consumer.Name
     } AS ConsumerDetails,
     {
       SELECT * FROM wmi(
          query="SELECT * FROM " + Filter.Type,
          namespace=if(condition=Filter.namespace,
              then=Filter.namespace,
              else=namespace)) WHERE Name = Filter.Name
     } AS FilterDetails
     FROM FilterToConsumerBinding

Windows.Persistence.PowershellRegistry

A common way of persistence is to install a hook into a user profile registry hive, using powershell. When the user logs in, the powershell script downloads a payload and executes it.

This artifact searches the user’s profile registry hive for signatures related to general Powershell execution. We use a yara signature specifically targeting the user’s profile which we extract using raw NTFS parsing (in case the user is currently logged on and the registry hive is locked).

name: Windows.Persistence.PowershellRegistry
description: |
  A common way of persistence is to install a hook into a user profile
  registry hive, using powershell. When the user logs in, the
  powershell script downloads a payload and executes it.

  This artifact searches the user's profile registry hive for
  signatures related to general Powershell execution. We use a yara
  signature specifically targeting the user's profile which we extract
  using raw NTFS parsing (in case the user is currently logged on and
  the registry hive is locked).

parameters:
  - name: yaraRule
    default: |
      rule PowerShell {
        strings:
          $a = /ActiveXObject.{,500}eval/ wide nocase

        condition:
          any of them
      }

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT * from foreach(
        row={
          SELECT Name, Directory as HomeDir from Artifact.Windows.Sys.Users()
          WHERE Directory and Gid
        },
        query={
          SELECT File.FullPath As FullPath,
                 Strings.Offset AS Off,
                 Strings.HexData As Hex,
                 upload(file=File.FullPath, accessor="ntfs") AS Upload
              FROM yara(
              files="\\\\.\\" + HomeDir + "\\ntuser.dat",
              accessor="ntfs",
              rules=yaraRule, context=50)
        })

Windows.Sys.AppcompatShims

Application Compatibility shims are a way to persist malware. This table presents the AppCompat Shim information from the registry in a nice format.

name: Windows.Sys.AppcompatShims
description: |
  Application Compatibility shims are a way to persist malware. This
  table presents the AppCompat Shim information from the registry in a
  nice format.

reference: |
  http://files.brucon.org/2015/Tomczak_and_Ballenthin_Shims_for_the_Win.pdf

parameters:
  - name: shimKeys
    default: >-
      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\InstalledSDB\*
  - name: customKeys
    default: >-
      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom\*\*

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET installed_sdb <=
           SELECT Key, Key.Name as SdbGUID, DatabasePath,
                  DatabaseType, DatabaseDescription,
                  -- Convert windows file time to unix epoch.
                  (DatabaseInstallTimeStamp / 10000000) - 11644473600 AS DatabaseInstallTimeStamp
           FROM read_reg_key(
             globs=split(string=shimKeys, sep=",[\\s]*"),
             accessor="reg")
      - |
        LET result = SELECT * from foreach(
          row={
            SELECT regex_replace(
               source=FullPath,
               replace="$1",
               re="^.+\\\\([^\\\\]+)\\\\[^\\\\]+$") as Executable,
              regex_replace(
               source=Name,
               replace="$1",
               re="(\\{[^}]+\\}).*$") as SdbGUIDRef,
               Name as ExeName from glob(
              globs=split(string=customKeys, sep=",[\\s]*"),
              accessor="reg")
          },
          query={
            SELECT Executable, DatabasePath, DatabaseType,
                   DatabaseDescription, DatabaseInstallTimeStamp, SdbGUID
            FROM installed_sdb
            WHERE SdbGUID = SdbGUIDRef
          })
      - |
        SELECT * from result

Windows.Sys.CertificateAuthorities

Certificate Authorities installed in Keychains/ca-bundles.

name: Windows.Sys.CertificateAuthorities
description: Certificate Authorities installed in Keychains/ca-bundles.
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        select Store, IsCA, Subject,
               encode(string=SubjectKeyId, type='hex') AS SubjectKeyId,
               encode(string=AuthorityKeyId, type='hex') AS AuthorityKeyId,
               Issuer, KeyUsageString,
               IsSelfSigned, SHA1, SignatureAlgorithm, PublicKeyAlgorithm, KeyStrength,
               NotBefore, NotAfter, HexSerialNumber
               from certificates()

Windows.Sys.DiskInfo

Retrieve basic information about the physical disks of a system.

name: Windows.Sys.DiskInfo
description: Retrieve basic information about the physical disks of a system.
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT Partitions,
               Index as DiskIndex,
               InterfaceType as Type,
               PNPDeviceID,
               DeviceID,
               Size,
               Manufacturer,
               Model,
               Name,
               SerialNumber,
               Description
        FROM wmi(
           query="SELECT * from Win32_DiskDrive",
           namespace="ROOT\\CIMV2")

Windows.Sys.Drivers

Details for in-use Windows device drivers. This does not display installed but unused drivers.

name: Windows.Sys.Drivers
description: Details for in-use Windows device drivers. This does not display installed but unused drivers.
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT * from wmi(
            query="select * from Win32_PnPSignedDriver",
            namespace="ROOT\\CIMV2")

Windows.Sys.FirewallRules

List windows firewall rules.

name: Windows.Sys.FirewallRules
description: List windows firewall rules.
reference:
  https://social.technet.microsoft.com/Forums/azure/en-US/aaed9c6a-fb8b-4d43-8b69-9f4e0f619a8c/how-to-check-the-windows-firewall-settings-from-netsh-command?forum=winserverGP

parameters:
  - name: regKey
    default: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\**\FirewallRules\*

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET rules = SELECT Name as Value,
               parse_string_with_regex(string=Data,
                 regex=["Action=(?P<Action>[^|]+)",
                        "Active=(?P<Active>[^|]+)",
                        "Dir=(?P<Dir>[^|]+)",
                        "Protocol=(?P<Protocol>[^|]+)",
                        "LPort=(?P<LPort>[^|]+)",
                        "Name=(?P<Name>[^|]+)",
                        "Desc=(?P<Desc>[^|]+)",
                        "App=(?P<App>[^|]+)"]) as Record,
               Data,
               FullPath
        FROM glob(globs=regKey, accessor="reg")

      - |
        SELECT Value,
               Record.Action as Action,
               Record.Name as Name,
               Record.Desc as Desc,
               Record.App as App,
               Record.Action as Action,
               Record.Dir as Dir,
               if(condition=Record.Protocol = "6",
                  then="TCP",
                  else=if(condition=Record.Protocol = "17",
                          then="UDP",
                          else=Record.Protocol)) as Protocol,
               if(condition=Record.LPort = NULL,
                  then="Any",
                  else=Record.LPort) as LPort,
               Record.Name as Name
        FROM rules

Windows.Sys.PhysicalMemoryRanges

List Windows physical memory ranges.

name: Windows.Sys.PhysicalMemoryRanges
description: List Windows physical memory ranges.
reference: |
  https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_cm_resource_list
parameters:
  - name: physicalMemoryKey
    default: HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\System Resources\Physical Memory\.Translated

  - name: Profile
    default: |
      {
        "CM_RESOURCE_LIST": [0, {
          "Count": [0, ["uint32"]],
          "List": [4, ["CM_FULL_RESOURCE_DESCRIPTOR"]]
         }],
         "CM_FULL_RESOURCE_DESCRIPTOR": [0, {
           "PartialResourceList": [8, ["CM_PARTIAL_RESOURCE_LIST"]]
         }],

         "CM_PARTIAL_RESOURCE_LIST": [0, {
           "Version": [0, ["uint16"]],
           "Revision": [2, ["uint16"]],
           "Count": [4, ["uint32"]],
           "PartialDescriptors": [8, ["Array", {
              "Target": "CM_PARTIAL_RESOURCE_DESCRIPTOR"
           }]]
         }],

         "CM_PARTIAL_RESOURCE_DESCRIPTOR": [20, {
           "Type": [0, ["char"]],
           "ShareDisposition": [1, ["char"]],
           "Flags": [2, ["uint16"]],
           "Start": [4, ["int64"]],
           "Length": [12, ["uint32"]]
         }]
      }

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT Type.AsInteger as Type,
               format(format="%#0x", args=Start.AsInteger) as Start,
               format(format="%#0x", args=Length.AsInteger) as Length
        FROM foreach(
          row={
            SELECT Data
              FROM stat(filename=physicalMemoryKey, accessor='reg')
          },
          query={
            SELECT Type, Start, Length, Data FROM binary_parse(
              string=Data.value,
              profile=Profile,
              target="CM_RESOURCE_LIST",
              start="List.PartialResourceList.PartialDescriptors")
          })

Windows.Sys.Programs

Represents products as they are installed by Windows Installer. A product generally correlates to one installation package on Windows. Some fields may be blank as Windows installation details are left to the discretion of the product author.

name: Windows.Sys.Programs
description: |
  Represents products as they are installed by Windows Installer. A product generally
  correlates to one installation package on Windows. Some fields may be blank as Windows
  installation details are left to the discretion of the product author.
reference: https://github.com/facebook/osquery/blob/master/specs/windows/programs.table

parameters:
  - name: programKeys
    default: >-
      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*,
      HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*,
      HKEY_USERS\*\Software\Microsoft\Windows\CurrentVersion\Uninstall\*

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        SELECT Key.Name as Name,
               DisplayName,
               DisplayVersion,
               InstallLocation,
               InstallSource,
               Language,
               Publisher,
               UninstallString,
               InstallDate
        FROM read_reg_key(globs=split(string=programKeys, sep=',[\\s]*'))

Windows.Sys.StartupItems

Applications that will be started up from the various run key locations.

name: Windows.Sys.StartupItems
description: Applications that will be started up from the various run key locations.
reference: |
  https://docs.microsoft.com/en-us/windows/desktop/setupapi/run-and-runonce-registry-keys
parameters:
  - name: runKeyGlobs
    default: >
      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*\*,
      HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run*\*,
      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run*\*
      HKEY_USERS\*\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*\*,
      HKEY_USERS\*\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run*\*,
      HKEY_USERS\*\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run*\*
  - name: startupApprovedGlobs
    default: >
      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\**,
      HKEY_USERS\*\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\**
  - name: startupFolderDirectories
    default: >
      C:/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup/**,
      C:/Users/*/AppData/Roaming/Microsoft/Windows/StartMenu/Programs/Startup/**

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        /* We need to search this multiple times so we materialize it
           into a variable (using the <= operator)
         */
        LET approved <=
           SELECT Name as ApprovedName,
                  encode(string=Data, type="hex") as Enabled
           FROM glob(globs=split(
                     string=startupApprovedGlobs, sep="[, ]+"),
                     accessor="reg")
           WHERE Enabled =~ "^0[0-9]0+$"

      - |
        LET registry_runners = SELECT Name,
          FullPath, Data.value as Command,
          if(
           condition={
                SELECT Enabled from approved
                WHERE Name = ApprovedName
           },
           then="enabled", else="disabled") as Enabled
          FROM glob(
           globs=split(string=runKeyGlobs, sep="[, ]+"),
           accessor="reg")

      - |
        LET file_runners = SELECT * FROM foreach(
           row={
              SELECT Name, FullPath
              FROM glob(
                 globs=split(string=startupFolderDirectories,
                 sep=",\\s*"))
           }, query={
              SELECT Name, FullPath, "enable" as Enabled,
                  encode(string=Data, type='utf16') as Command
              FROM read_file(filenames=FullPath)
           })

      - SELECT * from chain(
           first=registry_runners,
           second=file_runners)

Windows.Sys.Users

List User accounts. We combine two data sources - the output from the NetUserEnum() call and the list of SIDs in the registry.

name: Windows.Sys.Users
description: |
  List User accounts. We combine two data sources - the output from
  the NetUserEnum() call and the list of SIDs in the registry.

parameters:
  - name: remoteRegKey
    default: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*

sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'
    queries:
      - |
        LET roaming_users <=
           SELECT "" as Uid, "" as Gid,
               lookupSID(
                 sid=basename(path=Key.FullPath)
               ) as Name,
               Key.FullPath as Description,
               ProfileImagePath as Directory,
               basename(path=Key.FullPath) as UUID, "roaming" as Type
           FROM read_reg_key(globs=remoteRegKey, accessor="reg")
      - |
        LET local_users <= select User_id as Uid, Primary_group_id as Gid, Name,
               Comment as Description, {
                 SELECT Directory from roaming_users WHERE User_sid = UUID
               } as Directory, User_sid as UUID, "local" AS Type
        FROM users()

      - |
        SELECT * from chain(
         q1=local_users,
         q2={
           -- Only show users not already shown in the local_users above.
           SELECT * from roaming_users
           where not UUID in local_users.UUID
         })