logic to run remote commands via velociraptor (#42)
taylor_socfortress committed
Jul 17, 2023 at 13:16 UTC
5f767a76f15e6133ce43725f9094af5ca0fe279a
3 files changed
+148
-4
backend/app/routes/velociraptor.py
+36
@@ -118,3 +118,39 @@ def collect_artifact():
118
artifact=artifact_name,
119
)
120
return artifact_results
121
+
122
+
123
+@bp.route("/velociraptor/remotecommand", methods=["POST"])
124
+def run_remote_command():
125
+ """
126
+ Endpoint to run a remote command.
127
+ It collects the command and client name from the request body and returns the results.
128
+
129
+ Returns:
130
+ json: A JSON response containing the result of the PowerShell command execution.
131
+ """
132
+ req_data = request.get_json()
133
+ command = req_data["command"]
134
+ client_name = req_data["client_name"]
135
+ artifact_name = req_data["artifact_name"]
136
+ service = UniversalService()
137
+ client_id = service.get_client_id(client_name=client_name)["results"][0]["client_id"]
138
+ if client_id is None:
139
+ return (
140
+ jsonify(
141
+ {
142
+ "message": f"{client_name} has not been seen in the last 30 seconds and may not be online with the "
143
+ "Velociraptor server.",
144
+ "success": False,
145
+ },
146
+ ),
147
+ 500,
148
+ )
149
+
150
+ artifact_service = ArtifactsService()
151
+ artifact_results = artifact_service.run_remote_command(
152
+ client_id=client_id,
153
+ artifact=artifact_name,
154
+ command=command,
155
+ )
156
+ return artifact_results
backend/app/services/Velociraptor/artifacts.py
+47
-2
@@ -24,18 +24,22 @@ class ArtifactsService:
24
"""
25
return query
26
27
- def _get_artifact_key(self, client_id: str, artifact: str) -> str:
27
+ def _get_artifact_key(self, client_id: str, artifact: str, command: str = None) -> str:
28
"""
29
Construct the artifact key.
30
31
Args:
32
client_id (str): The ID of the client.
33
artifact (str): The name of the artifact.
34
+ command (str): The command that was run, if applicable.
35
36
Returns:
37
str: The constructed artifact key.
38
"""
38
- return f"collect_client(client_id='{client_id}', artifacts=['{artifact}'])"
39
+ if command:
40
+ return f"collect_client(client_id='{client_id}', urgent=true, artifacts=['{artifact}'], env=dict(Command='{command}'))"
41
+ else:
42
+ return f"collect_client(client_id='{client_id}', artifacts=['{artifact}'])"
43
44
def collect_artifacts(self) -> dict:
45
"""
@@ -163,3 +167,44 @@ class ArtifactsService:
167
"message": "Failed to run artifact collection",
168
"success": False,
169
}
170
+
171
+ def run_remote_command(self, client_id: str, artifact: str, command: str) -> dict:
172
+ """
173
+ Run a remote command on a specific client.
174
+ Accepted artifact names are `Windows.System.PowerShell`, `Windows.System.CmdShell`.
175
+
176
+ Args:
177
+ client_id (str): The ID of the client.
178
+ artifact (str): The name of the artifact.
179
+ command (str): The command to be executed.
180
+
181
+ Returns:
182
+ dict: A dictionary with the success status, a message, and potentially the results.
183
+ """
184
+ try:
185
+ query = self._create_query(
186
+ f"SELECT collect_client(client_id='{client_id}', urgent=true, artifacts=['{artifact}'], env=dict(Command='{command}')) "
187
+ "FROM scope()",
188
+ )
189
+ flow = self.universal_service.execute_query(query)
190
+ logger.info(f"Successfully ran artifact collection on {flow}")
191
+
192
+ artifact_key = self._get_artifact_key(client_id, artifact, command)
193
+ flow_id = flow["results"][0][artifact_key]["flow_id"]
194
+ logger.info(f"Successfully ran artifact collection on {flow_id}")
195
+
196
+ completed = self.universal_service.watch_flow_completion(flow_id)
197
+ logger.info(f"Successfully watched flow completion on {completed}")
198
+
199
+ results = self.universal_service.read_collection_results(
200
+ client_id,
201
+ flow_id,
202
+ artifact,
203
+ )
204
+ return results
205
+ except Exception as err:
206
+ logger.error(f"Failed to run artifact collection: {err}")
207
+ return {
208
+ "message": "Failed to run artifact collection",
209
+ "success": False,
210
+ }
backend/app/static/swagger.json
+65
-2
@@ -1734,9 +1734,9 @@
1734
"description": "Endpoint to get all artifacts for a hostname.",
1735
"parameters": [
1736
{
1737
- "name": "hostname",
1737
+ "name": "client_name",
1738
"in": "path",
1739
- "description": "The hostname",
1739
+ "description": "The client name",
1740
"required": true,
1741
"schema": {
1742
"type": "string"
@@ -1834,6 +1834,69 @@
1834
"tags": ["Velociraptor"]
1835
}
1836
},
1837
+ "/velociraptor/remotecommand": {
1838
+ "post": {
1839
+ "summary": "Run a remote command",
1840
+ "description": "Endpoint to run a remote command.",
1841
+ "requestBody": {
1842
+ "description": "Remote command details",
1843
+ "content": {
1844
+ "application/json": {
1845
+ "schema": {
1846
+ "type": "object",
1847
+ "properties": {
1848
+ "client_name": {
1849
+ "type": "string",
1850
+ "value": "WIN-39O01J5F7G5",
1851
+ "description": "The hostname of the client to run the command on."
1852
+ },
1853
+ "artifact_name": {
1854
+ "type": "string",
1855
+ "value": "Windows.System.PowerShell",
1856
+ "description": "The name of the artifact to run."
1857
+ },
1858
+ "command": {
1859
+ "type": "string",
1860
+ "value": "ping 8.8.8.8",
1861
+ "description": "The powershell command to run."
1862
+ }
1863
+ }
1864
+ }
1865
+ }
1866
+ }
1867
+ },
1868
+ "responses": {
1869
+ "200": {
1870
+ "description": "Successful operation",
1871
+ "content": {
1872
+ "application/json": {
1873
+ "schema": {
1874
+ "type": "object",
1875
+ "properties": {
1876
+ "output": {
1877
+ "type": "string",
1878
+ "description": "The output of the command."
1879
+ }
1880
+ }
1881
+ }
1882
+ }
1883
+ }
1884
+ },
1885
+ "default": {
1886
+ "description": "Unexpected error",
1887
+ "content": {
1888
+ "application/json": {
1889
+ "schema": {
1890
+ "$ref": "#/components/schemas/Error"
1891
+ }
1892
+ }
1893
+ }
1894
+ }
1895
+ },
1896
+ "operationId": "runPowershellCommand",
1897
+ "tags": ["Velociraptor"]
1898
+ }
1899
+ },
1900
"/dfir_iris/cases": {
1901
"get": {
1902
"summary": "Get all cases",