C2 powered by dinosaurs

research

Attackers love using tools that are open source and typically trusted as being legitimate / benign (e. g. ADExplorer or rclone). Here at SEC Consult, we never lost our childhood love for dinosaurs and use every chance we get to go for a walk with our pet Velociraptor. During one of these walks, our SEC Defence incident response team and application security experts wondered: "Could people with bad intentions misappropriate this cute Velociraptor for malicious purposes?" So, in a quiet April week, they set out to see: How well does Velociraptor work as a Command and Control (C2) framework?

Reminder on Velociraptor

A short intro to those that have yet to learn about Velociraptor outside of their childhood:
Velociraptor is a live forensics / incident response tool for large scale investigations. It is a well-known, state-of-the-art defense / blue team tool. It supports a client-server-architecture and allows data collection and parsing, limited only by your creativity (and willingness to implement it in the Velociraptor Query Language).

C2 worthiness of Velociraptor

Note: we will focus on Velociraptor as it is shipped by the developers - we will not go into possibilities achievable by modifying the source code of Velociraptor.

The Velociraptor executables for Windows and Mac are signed with certificates from trusted certificate authorities (DigiCert CA on Windows, Apple CA on Mac). This gives Velociraptor a higher default trust on these operating systems. The Velociraptor binaries for Linux and FreeBSD are unsigned (likely due to the ELF format missing an agreed upon way of signing binaries). 

Velociraptor has a client-server-architecture by design, with the server defining tasks and the client executing them. These tasks are defined in its custom language - Velociraptor Query Language (VQL) - and most often saved as "artifacts". The following typical C2 functions are covered:

C2 functionalityVelociraptor functionalityComments
Program Execution


plugin execve

artifact Generic.Client.VQL

artifact Windows.System.PowerShell

artifact Windows.System.CmdShell

artifact Linux.Sys.BashShell

Most artifacts end up using the plugin execve under the hood

Linux.Sys.BashShell is not just limited to Linux operating system - it works on any system that has the executable /bin/bash available

Retrieve Files from target

artifact System.VFS.DownloadFile

artifact Generic.Collectors.File

-
Searching files

artifact Windows.Search.FileFinder

artifact MacOS.Search.FileFinder

artifact Linux.Search.FileFinder

Search by filename, pattern and or YARA rule

Also includes the option to directly retrieve found files

Push files to target

plugin http_client

artifact Generic.Utils.FetchBinary

Generic.Utils.FetchBinary caches the file on disk
Communication channels

HTTPS

Websocket Secure 

-
Updating Configurationartifact Admin.Client.UpdateClientConfig-
"Ransom" / Denial of Service

artifact Windows.Remediation.Quarantine

artifact Linux.Remediation.Quarantine

Isolates the client from the network except for connections to the Velociraptor server
Beacon Hiding / Sleeping-No such functionality exists

VQL allows for a lot of flexibility. But interaction with the low level API of the kernel is limited to specific functionality exposed by Velociraptor - no unrestricted access is implemented. 

Running Velociraptor on a target system requires the binary, a config file and local admin privileges. This makes Velociraptor unusable with low-privileged user accounts. Velociraptor can either be run on demand or persistent. Persistence is achieved by creating a service - the config file defines the service name and the location, where the files related for the service are stored. While Velociraptor includes a way to embed its config file into the binary, this is currently only usable for one time executions (called "offline collectors") and not for running the program for long time periods.

The client/server communication can be secured using mTLS, if configured. The implementation for mTLS uses the same certificate for each client, meaning specific clients cannot be differentiated based on the client certificate.

The Velociraptor multi-frontend functionality allows having multiple server instances that a client can connect to but all such server instances need to be defined in the config file upfront. Each client then chooses a server instance to connect to at random. As such, this is a way of defining backup domains that can easily be defeated by peeking into the config file.

Beyond the extensibility of VQL, Velociraptor itself can also be easily modified to support new functionality. As an example, we implemented local authentication as well as Pass-the-Hash functionality for the smb plugin of Velociraptor and successfully used it in an environment. To use the modified Velociraptor, we only needed to change the binary running on the client - the unmodified server communicated with the modified client without problems.

Spying on the blue team

Velociraptor allows monitoring of activities on the clients and automated reactions (be it triggered by the client directly or orchestrated by the server). Such monitoring is dependent on your needs and things of interest. We investigated the following points, focusing heavily on Windows systems (as these tend to be focused by attackers in our experience):

  • Installation of new services on Windows systems: Some tools used in incident response need to be installed as a service on Windows (e. g. Velociraptor when using a persistent mode or some EDR tools). In general, a service installation signals new components added to a system, potentially creating a new attack surface. Thankfully, Velociraptor already ships with an artifact enabling this kind of monitoring: Windows.Events.ServiceCreation
  • Process Creations: Beyond new services, the processes run on a system are of interest. These can either be collected in general or filtered for incident response tooling known to be used in the environment (e. g. kape.exe, Collector_velociraptor.* or thor*). 

Velociraptor already ships with an artifact enabling this kind of monitoring: Windows.Events.ProcessCreation - the output of this artifact can be improved by enabling data enrichment, either via Sysmon (if it is already present on the system or we are willing to install it - Windows.Events.TrackProcesses) or via regular polling of running processes (Windows.Events.TrackProcessesBasic). The artifact Windows.Events.ProcessCreation creates regular WMI queries, which might alert the blue team that things are fishy. So we were interested in alternative options.

As Velociraptor has recently (Version v0.74) added support for ETW kernel providers, we implemented a client monitoring based on it:

name: Windows.Events.ETWProcesses
author: Herbert Bärschneider, SEC Consult
description: |
  This artifact is meant for monitoring processes on Windows clients. 
  It uses Kernel ETW providers to notice process creations and terminations.
  
  Requirements:
  - Velociraptor Client and Velociraptor Server need version v0.74 or newer (or else the feature is just not supported and won't work at all)

type: CLIENT_EVENT

sources:
   - query: |
       SELECT *, atoi(string=EventData.ProcessId) AS Pid, atoi(string=EventData.ParentId) AS Ppid 
       FROM watch_etw(guid='kernel', kernel_tracer_type=['process'])

 

We also tinkered with monitoring processes using the generic pslist() plugin of Velociraptor, creating a client monitoring for process creations that runs on every operating system supported by Velociraptor:

name: Generic.Events.Processes
author: Herbert Bärschneider, SEC Consult
description: |
  This artifact is meant for monitoring processes on clients. It is usable on every operating system supported by Velociraptor.
  It periodically queries the existing processes and emits lines for differences (new processes and missing/removed ones).
  Processes are tracked and compared based on the following elements: process ID, parent process ID, SID of the process owner, username, process name, executable associated with the process, commandline of the process

type: CLIENT_EVENT

parameters:
  - name: Period
    default: 2
    type: int
    description: how many seconds the artifact waits between checking processes for changes

sources:
   - query: |
       LET RunningProcesses = SELECT *, format(format="%v %v %v %v %v %v %v", args=[Pid, Ppid, OwnerSid, Username, Name, Exe, CommandLine]) AS DiffKey FROM pslist()
     
       LET EventQuery = SELECT * FROM diff(query=RunningProcesses, period=Period, key="DiffKey")
     
       SELECT * FROM EventQuery

 

  • Account Logons on Windows systems: In every environment, there are accounts that interest you. As such, it is valuable to learn when such an account logs into any system you control. 

Velociraptor already ships with an artifact enabling monitoring of account logins, including a filter for the account you care about: Windows.Events.Trackaccount (companion server monitoring artifact: Server.Alerts.Trackaccount). 

Beyond just simple logons, we were also interested in logons with high privileges. These are interesting targets for account takeover. We created the following Velociraptor artifact to enable this kind of monitoring:

name: Windows.Events.HighPrivilegedLogon
author: Herbert Bärschneider, SEC Consult
description: |
 Artifact to detect logons that get special privileges assigned. The artifact monitors for security event id 4672.
 This is useful to see where accounts with high privileges are being actively used.
 
 Be aware, there is lots of noise from NT_AUTHORITY\SYSTEM and the machine account of a system. These are filtered out for you.

type: CLIENT_EVENT

parameters:
 - name: eventLog
   default: C:\Windows\system32\winevt\logs\Security.evtx
 - name: PrivilegesRegex
   type: regex
   description: Regex for the privileges you care about

sources:
 - precondition:
     SELECT OS From info() where OS = 'windows'
   query: |
     LET files = SELECT * FROM glob(globs=eventLog)
     
     SELECT timestamp(epoch=System.TimeCreated.SystemTime) As EventTime,
             System.EventRecordID as EventRecordID,
             System.EventID.Value as EventID,
             System.Computer as SourceComputer,
             EventData.SubjectUserName as SubjectUserName,
             EventData.PrivilegeList as PrivilegeList,
             System,
             EventData,
             Message
       FROM foreach(
         row=files,
         async=TRUE,
         query={
           SELECT *
           FROM watch_evtx(filename=OSPath)
           WHERE System.EventID.Value = 4672
             AND EventData.PrivilegeList =~ PrivilegesRegex
             AND NOT ((EventData.SubjectUserName =~ "SYSTEM" AND EventData.SubjectDomainName =~ "NT AUTHORITY") OR EventData.SubjectUserName =~ "\$$")
       })

Note: In the above example, we only showed the client side of the monitoring - creation of server monitoring artifacts that consume the results from the client monitoring and trigger alerts and or reactions is left as an exercise for the motivated reader.

Detecting Velociraptor (mis-)usage

Velociraptor on endpoints:

As both default ways of running Velociraptor need a config file and binary on disk, searching for those is a simple way to find usage of Velociraptor. While searching for file names is a good start, we also created some Yara rules to help us:

rule velociraptor_client_config {
   meta:
       author = "Herbert Bärschneider, SEC Consult"
       date = "2025-05-21"
       description = "Detects a Velociraptor client config file"
       reference = "https://docs.velociraptor.app/docs/deployment/references/"
   
   strings:
       $s1 = "server_urls:" ascii
       $s2 = "ca_certificate:" ascii
       $w1 = "writeback_darwin:" ascii
       $w2 = "writeback_linux:" ascii
       $w3 = "writeback_windows:" ascii
       $w4 = "level2_writeback_suffix:" ascii
       
   condition:
       (all of ($s*)) and (any of ($w*))
}

rule velociraptor_binary {
   meta:
       author = "Herbert Bärschneider, SEC Consult"
       date = "2025-05-21"
       description = "Detects a Velociraptor binary"
       reference = "https://docs.velociraptor.app/downloads/"
   
   strings:
       $s1 = "velociraptor" ascii
       $s2 = "velocidex" ascii
       $vql1 = "precondition:" ascii
       $vql2 = "query:" ascii
       $vql3 = "hunt_flows(" ascii
       $x1 = "protobuf" ascii
       
   condition:
       4 of them
}

Running external programs or commands through Velociraptor results in either binaries being dropped to disk or shell executions or unmasked process executions. Files dropped on disk are scanned by AV / EDR and will trigger alerts. Shell executions and unmasked process executions can be matched with signatures for malicious activity, but are also scanned by AV / EDR and trigger alerts. When running artifacts that have the Velociraptor program itself do malicious actions, the AV did not trigger alerts but the EDR from our tests did alert on it.

Note: testing of AV and EDR detections was done with Windows Defender (non cloud enabled) and a widely used EDR vendor product. 

Beyond endpoints, Velociraptor can also be identified on the network:

Velociraptor keeps a consistent network connection between each client and its server. This connection is TLS encrypted, using either HTTPS or Websocket Secure. Figure 1 shows the start of such TLS session:

Figure 1: TLS session captured in Wireshark

Velociraptor uses a hard-coded Servername in the certificate identifying the server against the clients (Note: this can be changed in the source code in constants.go, modifying PinnedServerName). A weaker indicator is client to server communication via TLS on port 8000, requesting h2 and http/1.1

alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"TLS CLIENT HELLO requesting connection to a Velociraptor Server"; flow:established,to_server; tls.sni; content:"VelociraptorServer"; nocase; sid:1030403; rev:1;)


alert tls $HOME_NET any -> $EXTERNAL_NET 8000 (msg:"TLS CLIENT HELLO possible Velociraptor Server"; flow:established,to_server; tls.alpn; content:"h2 http/1.1"; requires: version >= 8; sid:1030401; rev:1;) 

(Note: these rules assume an externally hosted Velociraptor server; this could also be matched "any to any" for a wider net, potentially catching an internally running Velociraptor server)

We also looked at fingerprinting the network communication of the Velociraptor client (Windows and Linux, amd64 clients from the tagged versions of the last twelve months), specifically the JA3 and JA4 hashes (fingerprint of the TLS Client Hello messages):


JA3 and JA4 hashes change between versions of Velociraptor (but not every version) and are partially the same between Windows and Linux. These hashes seem to be unique (though the database we checked against is not too big) and are semi-stable, meaning the creation of an overview might be worth to enable detection of Velociraptor clients.


Velociraptor clients, Linux amd64 and Windows amd64, for the tagged versions v0.73.1 till v0.74.1 shared the following hashes:

  • JA3: f616cc2741a43765279c0838d9333742
  • JA4: t13d1313h2_f57a46bbacb6_9249cab70c77

Potential for DLL-Hijacking, especially for Offline Collectors

Because Velociraptor needs to be started as Administrator/SYSTEM, it is a prime target for DLL-Hijacking. DLL-Hijacking occurs when an application tries to load a DLL which does not exist and can be created by an attacker; or when an attacker replaces a legitimate DLL with a malicious copy. This can be used to escalate privileges and execute malicious code from within an otherwise benign application.

When Velociraptor is started without arguments, it tries to load the following DLLs from the folder where the Velociraptor executable is located:

  • dbghelp.dll
  • tdh.dll
  • dbgcore.dll
  • amsi.dll


Figure 2: Process Monitor output

Depending on which artifacts are executed, even more DLLs might be loaded from this folder. We performed a one-shot analysis, where we executed almost all artifacts, including ones which use third-party tools, and found more potential DLL Hijacks:

Figure 3: Potential DLL hijacking

Loading DLLs from the folder where the executable is located is a fixed part of the Windows DLL Search Order and can not be altered. In contrast, loading from the current working directory could be prevented by using SetDllDirectory. Just for fun, we ripped out the DLL imports for dbghelp.dll , tdh.dll and dbgcore.dll from the Velociraptor executable and it still started without problems. Only the loading of amsi.dll can not be removed/prevented.

Our experience shows that some fast and lightly prepared deployments of Velociraptor use default temporary folders for staging the executable and config file. This creates the opportunity for a malicious actor to place manipulated DLLs in those default folders. Due to the Windows DLL Search Order, these manipulated DLLs may then be run in parallel with a Velociraptor deployment.

If the default DLL search order can not be altered, what can you do? Simple!

Only install and start Velociraptor from a trusted folder with controlled content!

Conclusion

There are better alternatives for a C2 than Velociraptor. While the added bonus of being a blue team tool is nice, Velociraptor is not designed to be stealthy. It is also not excluded by default from AV / EDR scrutiny.

Also, make sure to only run it from a trusted folder.

About the author

Herbert Bärschneider

Security Consultant

Herbert works as a forensic analyst in the Managed Incident Response team of SEC Consult. In parallel to his operational work, he is finishing a masters program and explores niche operating systems for forensic artifacts. He values giving back to the community by contributing to triage and threat hunting capabilities.