mirror of
https://github.com/valitydev/Cortex-Analyzers.git
synced 2024-11-06 09:05:19 +00:00
Merge branch 'weslambert-feature/Velociraptor-Responder' into develop
This commit is contained in:
commit
bfc502902e
9
responders/Velociraptor/README.md
Normal file
9
responders/Velociraptor/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
### Velociraptor
|
||||
This responder can be used to run a flow for a Velociraptor artifact. This could include gathering data, or performing initial response, as the artifact (or artifact "pack") could encompass any number of actions. The responder can be run on an observable type of `ip`, `fqdn`, or `other`, and will look for a matching client via the Velociraptor server. If a client match is found for the last seen IP, or the hostname, the responder will kick off the flow, the results will be returned, and the client ID will be added as a tag to the case and the observable.
|
||||
|
||||
#### Requirements
|
||||
The following options are required in the Velociraptor Responder configuration:
|
||||
|
||||
- `velociraptor_client_config`: The path to the Velociraptor API client config.
|
||||
(See the following for generating an API client config: https://www.velocidex.com/docs/user-interface/api/, and ensure the appropriate ACLs are granted to the API user).
|
||||
- `velociraptor_artifact`: The name artifact you which to collect (as you would see it in the Velociraptor GUI)
|
4
responders/Velociraptor/requirements.txt
Executable file
4
responders/Velociraptor/requirements.txt
Executable file
@ -0,0 +1,4 @@
|
||||
cortexutils
|
||||
cryptography
|
||||
grpcio-tools
|
||||
pyvelociraptor
|
30
responders/Velociraptor/velociraptor_flow.json
Executable file
30
responders/Velociraptor/velociraptor_flow.json
Executable file
@ -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
responders/Velociraptor/velociraptor_flow.py
Executable file
127
responders/Velociraptor/velociraptor_flow.py
Executable file
@ -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()
|
Loading…
Reference in New Issue
Block a user