Skip to content

Commit

Permalink
Merge branch 'feature/Velociraptor-Responder' of https://github.com/w…
Browse files Browse the repository at this point in the history
…eslambert/Cortex-Analyzers into weslambert-feature/Velociraptor-Responder
  • Loading branch information
jeromeleonard committed Aug 12, 2020
2 parents 481df18 + b638621 commit 6c9d3e2
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 0 deletions.
4 changes: 4 additions & 0 deletions responders/Velociraptor/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cortexutils
cryptography
grpcio-tools
pyvelociraptor
30 changes: 30 additions & 0 deletions responders/Velociraptor/velociraptor_flow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "Velociraptor_Flow",
"version": "0.1",
"author": "Wes Lambert",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "AGPL-V3",
"description": "Run Velociraptor flow",
"dataTypeList": ["thehive:case_artifact"],
"command": "Velociraptor/velociraptor_flow.py",
"baseConfig": "Velociraptor",
"configurationItems": [
{
"name": "velociraptor_client_config",
"description": "Path to API client config file",
"type": "string",
"multi": false,
"required": true,
"defaultValue": ""
},
{
"name": "velociraptor_artifact",
"description": "Artifact to collect",
"type": "string",
"multi": false,
"required": true,
"defaultValue": ""
}
]
}

127 changes: 127 additions & 0 deletions responders/Velociraptor/velociraptor_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
from cortexutils.responder import Responder
import json
import grpc
import re
import time
import yaml
import pyvelociraptor
from pyvelociraptor import api_pb2
from pyvelociraptor import api_pb2_grpc

class Velociraptor(Responder):
def __init__(self):
Responder.__init__(self)
self.configpath = self.get_param('config.velociraptor_client_config', None, "File path missing!")
self.config = yaml.load(open(self.configpath).read(), Loader=yaml.FullLoader)
self.artifact = self.get_param('config.velociraptor_artifact', None, 'Artifact missing!')
self.observable_type = self.get_param('data.dataType', None, "Data type is empty")
self.observable = self.get_param('data.data', None, 'Data missing!')

def run(self):
Responder.run(self)
creds = grpc.ssl_channel_credentials(
root_certificates=self.config["ca_certificate"].encode("utf8"),
private_key=self.config["client_private_key"].encode("utf8"),
certificate_chain=self.config["client_cert"].encode("utf8")
)

options = (('grpc.ssl_target_name_override', "VelociraptorServer",),)

with grpc.secure_channel(self.config["api_connection_string"],
creds, options) as channel:
stub = api_pb2_grpc.APIStub(channel)

if self.observable_type == "ip":
client_query = "select client_id from clients() where last_ip =~ '"+ self.observable + "'"
elif re.search(r'fqdn|other', self.observable_type):
client_query = "select client_id from clients(search='host:" + self.observable + "')"
else:
self.report({'message': "Not a valid data type!" })
return

# Send initial request
client_request = api_pb2.VQLCollectorArgs(
max_wait=1,
Query=[api_pb2.VQLRequest(
Name="TheHive-ClientQuery",
VQL=client_query,
)])

for client_response in stub.Query(client_request):
try:
client_results = json.loads(client_response.Response)
global client_id
client_id = client_results[0]['client_id']
except:
self.report({'message': 'Could not find a suitable client.'})
pass

# Define initial query
init_query = "SELECT collect_client(client_id='"+ client_id +"',artifacts=['" + self.artifact + "']) FROM scope()"

# Send initial request
request = api_pb2.VQLCollectorArgs(
max_wait=1,
Query=[api_pb2.VQLRequest(
Name="TheHive-Query",
VQL=init_query,
)])

for response in stub.Query(request):
try:
init_results = json.loads(response.Response)
flow=list(init_results[0].values())[0]
self.report({'message': init_results })

# Define second query
flow_query = "SELECT * from flows(client_id='" + str(flow['request']['client_id']) + "', flow_id='" + str(flow['flow_id']) + "')"

state=0

# Check to see if the flow has completed
while (state == 0):

followup_request = api_pb2.VQLCollectorArgs(
max_wait=1,
Query=[api_pb2.VQLRequest(
Name="TheHive-QueryForFlow",
VQL=flow_query,
)])

for followup_response in stub.Query(followup_request):
try:
flow_results = json.loads(followup_response.Response)
except:
pass
state = flow_results[0]['state']
if state == 1:
time.sleep(5)
break

# Grab the source from the artifact
source_query="SELECT * from source(client_id='"+ str(flow['request']['client_id']) + "', flow_id='" + str(flow['flow_id']) + "', artifact='" + self.artifact + "')"
source_request = api_pb2.VQLCollectorArgs(
max_wait=1,
Query=[api_pb2.VQLRequest(
Name="TheHive-SourceQuery",
VQL=source_query,
)])
source_results=[]
for source_response in stub.Query(source_request):
try:
source_result = json.loads(source_response.Response)
source_results += source_result
self.report({'message': source_results })
except:
pass
except:
pass

def operations(self, raw):
global client_id
return [self.build_operation('AddTagToArtifact', tag=client_id)]
return [self.build_operation('AddTagToCase', tag=client_id)]

if __name__ == '__main__':
Velociraptor().run()

0 comments on commit 6c9d3e2

Please sign in to comment.