Velociraptor allows packaging VQL queries inside mini-programs called
Artifacts. An artifact is simply a structured YAML file containing a
query, with a name attached to it. This allows Velociraptor users to
search for the query by name or description and simply run the query
on the endpoint without necessarily needing to understand or type the
query into the UI.
Therefore Artifacts can be thought of as VQL modules.
Usually an artifact is geared towards collecting a single type of information from the endpoint. For example consider the following artifact:
name: Custom.Artifact.Name description: | This is the human readable description of the artifact. type: CLIENT parameters: - name: FirstParameter default: Default Value of first parameter precondition: "" sources: - name: MySource precondition: SELECT OS From info() where OS = 'windows' OR OS = 'linux' OR OS = 'darwin' query: | SELECT * FROM info() LIMIT 10
The Artifact contains a number of important parameters:
SELECTclause and it must be at the end of the query potentially following any
Artifact parameters allow the user to customize the collection in a controlled way - without needing to edit the VQL. The GUI will present a form that allows the user to update the parameters prior to each collection.
Parameters may define a type. This type will be used to hint to the GUI how to render the form element. The type also determines how the parameter is sent to the client and ensures the parameter appears as that type in the query.
Prior to launching the query on the endpoint, Velociraptor will populate the scope with the parameters. This allows the VQL query to directly access the parameters.
Artifact parameters are sent to the client as strings The client automatically parses them into a VQL type depending on the parameter’s type specification. The GUI uses type specification to render an appropriate UI
Currently the following parameter types are supported
Let’s take a look at a typical artifact
This artifact uncovers the mutants (named mutexes) on a system, using two methods. First we enumerate all handles, and check which process is holding a handle to a mutant object. Alternatively we enumerate the kernel object manager to receive the same information.
Therefore this artifact contains two sources - each gets similar information in a different way. A user who is just interested in listing the Mutants on an endpoint would probably need to see both results.
We also see some parameters declared to allow a user to filter by process name or mutant name.
A precondition is a query that is run before collecting the artifact to determine if the artifact should be collected at all. The precondition makes it safe to collect artifacts without needing to worry about if the artifact is designed for this particular architecture or operating system. For example, performing a hunt for a Windows only artifact is safe to target all clients because Linux clients will just ignore it and return no rows. Most preconditions target specific operating systems or architectures but the precondition can be an arbitrary query.
You can specify a precondition at the top level of the artifact or at each source:
Typically we have a new idea for a new detection. The first step is to
develop the VQL that will detect the anomaly by writing the VQL in a
notebook cell on the target operating system itself (usually we use
velociraptor gui to start a new local server).
While developing the VQL, Use the
log() VQL function librally to
provide print debugging.
Use format(format="%T %v", args=[X, X]) to learn about a value’s type and value.
You can call other artifacts from your own VQL using the
Artifact.<artifact name> plugin notation. Args to the
plugin are passed as artifact parameters.
When calling artifacts types are not converted. Make sure you pass the expected types
When collecting an artifact from the client, the server compiles the artifact and it’s dependencies into raw VQL statements and sends these to the client for evaluation. We never rely on the artifact definitions embedded in the client itself - instead we always send the compiled VQL to the client. This allows us to upgrade artifact definitions on the server without needing to update the client itself.