Skip to content

Commit

Permalink
Add Get file from multiple hosts sample
Browse files Browse the repository at this point in the history
  • Loading branch information
jshcodes committed Feb 13, 2024
1 parent 5d60621 commit 9dd5330
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 1 deletion.
29 changes: 28 additions & 1 deletion samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ The following samples are categorized by CrowdStrike product, and further catego
| [ML Exclusions](#ml-exclusions-samples) | ML Exclusion Audit |
| [Prevention Policies](#prevention-policies-samples) | Clone Prevention Policy<BR/>Create Host Group and attach Prevention Policies<BR/>Prevention Policy Hawk |
| [Incidents](#incidents-samples) | CrowdScore QuickChart<BR/>Incident Triage |
| [Real Time Response](#real-time-response-samples) | Bulk execute a command<BR/>Bulk execute a command (queued)<BR/>Get host uptime<BR/>Get RTR result<BR/>Dump memory for a running process<BR/>My Little RTR<BR/>ProxyTool<BR/>Remotely restart a sensor while taking a capture<BR/>RTR Script Manager |
| [Real Time Response](#real-time-response-samples) | Bulk execute a command<BR/>Bulk execute a command (queued)<BR/>Get file from multiple hosts<BR/>Get host uptime<BR/>Get RTR result<BR/>Dump memory for a running process<BR/>My Little RTR<BR/>ProxyTool<BR/>Remotely restart a sensor while taking a capture<BR/>RTR Script Manager |
| [Sensor Visibility Exclusions](#sensor-visibility-exclusions-samples) | Sensor Visibility Exclusion Audit |
| [Firewall Management](#firewall-management-samples) | Export Firewall events to a file |

Expand Down Expand Up @@ -1213,6 +1213,7 @@ These samples focus on CrowdStrike's Real Time Response and Real Time Response A

- [Bulk execute a command](#bulk-execute-a-command)
- [Bulk execute a command (queued)](#bulk-execute-a-command-queued)
- [Get file from multiple hosts](#get-file-from-multiple-hosts)
- [Get RTR result](#get-rtr-result)
- [Dump memory for a running process](#dump-memory-for-a-running-process)
- [My Little RTR](#my-little-rtr)
Expand Down Expand Up @@ -1274,6 +1275,32 @@ This sample demonstrates the following CrowdStrike Hosts, Real Time Response and

---

#### Get file from multiple hosts
This [sample](rtr#get-file-from-multiple-hosts) will retrieve a file of the same name from multiple hosts.

[![Real Time Response](https://img.shields.io/badge/Service%20Class-Get_file_from_multiple_hosts-silver?style=for-the-badge&labelColor=C30A16&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAOCAYAAAAi2ky3AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVBzuIOGSoDmJBVEQ3rUIRKoRaoVUHk5f+CE0akhQXR8G14ODPYtXBxVlXB1dBEPwBcXNzUnSREu9LCi1ifPB4H+e9c7jvXkColZhmtY0Cmm6bqURczGRXxNAruhAEMI1hmVnGrCQl4bu+7hHg512MZ/m/+3N1qzmLAQGReIYZpk28Tjy5aRuc94kjrCirxOfEIyYVSPzIdcXjN84FlwWeGTHTqTniCLFYaGGlhVnR1IgniKOqplO+kPFY5bzFWStVWKNO/sNwTl9e4jrtASSwgEVIEKGggg2UYCNGp06KhRTdx338/a5fIpdCrg0wcsyjDA2y6wefwe/eWvnxMS8pHAfaXxznYxAI7QL1quN8HztO/QQIPgNXetNfrgFTn6RXm1r0COjZBi6um5qyB1zuAH1PhmzKrsTnL+TzwPsZjSkL9N4Cnate3xr3OH0A0tSr5A1wcAgMFSh7zeffHa19+/dNo38/hq9yr+iELI0AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflDAsTByz7Va2cAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAYBJREFUKM+lkjFIlVEYht/zn3sFkYYUyUnIRcemhCtCU6JQOLiIU+QeJEQg6BBIm0s4RBCBLjq5OEvgJC1uOniJhivesLx17/97/vO9b4NK4g25157hfHCGB773/cA0HZIEAKiMj+LWiOxljG/i96pnCFP58XHnrWX2+9cj0dYl9Yu2FE9/9rXrcAAgs2eSyiBfOe/XRD503h/CuffOubQVUXL+Jh9BllzBbyJJBgDclVkO4Kukd8zzkXJbeUljIldFTstsmSHM6S81ma2KfPKlFdkGAMY4wzx/bbXapMy21My+YizdKNq5mDzLkrxafSxySFKjSWX2oTmjKzz4vN0r2lOFcL/Q3V0/mX95ILMXTTGYVfaut/aP2+oCMAvnZgCcsF5fcR0dg65YHAdwB+QApADvu0AuOe/ftlJAD7Nsgmm6yBjDtfWORJZlNtFyo/lR5Z7MyheKA5ktSur7sTAHazSG27pehjAiaVfkN8b4XFIJ/wOzbOx07VNRUuHy7w98CzCcGPyWywAAAABJRU5ErkJggg==)](rtr#get-file-from-multiple-hosts)

##### Real Time Response API operations discussed
This sample demonstrates the following CrowdStrike Real Time Response and Real Time Response Admin API operations:

| Operation | Description |
| :--- | :--- |
| [RTR_GetExtractedFileContents](https://falconpy.io/Service-Collections/Real-Time-Response.html#rtr_getextractedfilecontents) | Get RTR extracted file contents for specified session and sha256. |
| [RTR_DeleteSession](https://falconpy.io/Service-Collections/Real-Time-Response.html#rtr_deletesession) | Delete a session. |
| [RTR_ListFilesV2](https://falconpy.io/Service-Collections/Real-Time-Response.html#rtr_listfilesv2) | Get a list of files for the specified RTR session. |
| [RTR_DeleteFileV2](https://falconpy.io/Service-Collections/Real-Time-Response.html#rtr_deletefilev2) | Delete a RTR session file. |
| [BatchInitSessions](https://falconpy.io/Service-Collections/Real-Time-Response.html#batchinitsessions) | Batch initialize a RTR session on multiple hosts. Before any RTR commands can be used, an active session is needed on the host. |
| [BatchGetCmd](https://falconpy.io/Service-Collections/Real-Time-Response.html#batchgetcmd) | Batch execute a `GET` command across hosts to retrieve files. After this call is made, [BatchGetCmdStatus](https://falconpy.io/Service-Collections/Real-Time-Response.html#batchgetcmdstatus) can be used to query for the results. |

##### Hosts API operations discussed
This sample demonstrates the following CrowdStrike Hosts API operations:

| Operation | Description |
| :--- | :--- |
| [QueryDevicesByFilterScroll](https://www.falconpy.io/Service-Collections/Hosts.html#querydevicesbyfilterscroll) | Search for hosts in your environment by platform, hostname, IP, and other criteria with continuous pagination capability (based on offset pointer which expires after 2 minutes with no maximum limit). |

---

#### Get RTR result
Retrieve the results for previously executed RTR commands.

Expand Down
100 changes: 100 additions & 0 deletions samples/rtr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The examples within this folder focus on leveraging CrowdStrike's Real Time Resp

- [Bulk Execute](#bulk-execute-a-command-on-matched-hosts) - Bulk execute a command on multiple hosts that you select by using a search string.
- [Queued Execute](#bulk-execute-a-command-on-matched-hosts-with-queuing) - Bulk execute a command on multiple hosts that are selected by using a search string or a provided list of host AIDs. Execution is queued for offline hosts with request IDs stored to an external file for later result retrieval.
- [Get file from multiple hosts](#get-file-from-multiple-hosts) - Retrieve a file of the same name from multiple hosts.
- [Get host uptime](#get-host-uptime) - Retrieve the uptime for a host using a RTR session and a script command.
- [Get RTR result](#get-rtr-result) - Retrieve the results for previously executed RTR batch commands.
- [Restart Sensor](#restart-sensor) - Restarts the sensor while taking a TCP dump.
Expand Down Expand Up @@ -178,6 +179,105 @@ The source code for this example can be found [here](queued_execute.py).

---

## Get file from multiple hosts
This sample demonstrates retrieving a file of the same name from multiple hosts.

### Running the program
In order to run this demonstration, you you will need access to CrowdStrike API keys with the following scopes:

| Service Collection | Scope |
| :---- | :---- |
| Hosts | __READ__ |
| Real Time Response | __READ__, __WRITE__ |

### Execution syntax
This sample leverages simple command-line arguments to implement functionality.

#### Basic usage
Retrieve a file from multiple hosts.

```shell
python3 get_file_from_hosts.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -n HOSTNAME_SEARCH -f FILENAME
```

> [!TIP]
> Hostname is a stemmed search.
GovCloud users can change their CrowdStrike region using the `-b` argument.

```shell
python3 get_file_from_hosts.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -n HOSTNAME_SEARCH -f FILENAME -b usgov1
```

Environment authentication is supported, so this solution can be executed without providing credentials if the environment variables `FALCON_CLIENT_ID` and `FALCON_CLIENT_SECRET` are defined.

```shell
python3 get_file_from_hosts.py -n HOSTNAME_SEARCH -f FILENAME
```

Activate API debug logging with the `-d` argument.

```shell
python3 get_file_from_hosts.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -n HOSTNAME_SEARCH -f FILENAME -d
```

#### Command-line help
Command-line help is available via the `-h` argument.

```shell
usage: get_file_from_hosts.py [-h] [-d] [-n HOSTNAME] [-b BASE_URL] [-k CLIENT_ID] [-s CLIENT_SECRET] -f FILEPATH

Retrieve a file from multiple hosts.

_______ __ _______ __ __ __
| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
|: 1 | |: 1 |
|::.. . | |::.. . | FalconPy v1.3
`-------' `-------'
____ __ _______
/ __ \___ ____ _/ / /_ __(_)___ ___ ___
/ /_/ / _ \/ __ `/ / / / / / __ `__ \/ _ \
/ _, _/ __/ /_/ / / / / / / / / / / / __/
/_/ |_|\___/\__,_/_/ /_/ /_/_/ /_/ /_/\___/
____
/ __ \___ _________ ____ ____ ________
/ /_/ / _ \/ ___/ __ \/ __ \/ __ \/ ___/ _ \
/ _, _/ __(__ ) /_/ / /_/ / / / (__ ) __/
/_/ |_|\___/____/ .___/\____/_/ /_/____/\___/
/_/
This program will retrieve a single file of the same name
from multiple hosts. Files will be saved as 7zip archives
named after the host's AID.
Creation date: 11.30.23 - jshcodes@CrowdStrike
optional arguments:
-h, --help show this help message and exit
-d, --debug Enable API debugging
-n HOSTNAME, --hostname HOSTNAME
Hostname to target (stemmed search)
-b BASE_URL, --base_url BASE_URL
CrowdStrike API base URL
authentication arguments:
-k CLIENT_ID, --client-id CLIENT_ID
Falcon API client ID
-s CLIENT_SECRET, --client-secret CLIENT_SECRET
Falcon API client ID
required arguments:
-f FILEPATH, --filepath FILEPATH
Filename and path of the file to be downloaded
```
### Example source code
The source code for this example can be found [here](get_file_from_hosts.py).
---
## Get host uptime
Leverages the `runscript` RTR command to retrieve the uptime for host(s) within your environment.
Expand Down
152 changes: 152 additions & 0 deletions samples/rtr/get_file_from_hosts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
r"""Retrieve a file from multiple hosts.
_______ __ _______ __ __ __
| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
|: 1 | |: 1 |
|::.. . | |::.. . | FalconPy v1.3
`-------' `-------'
____ __ _______
/ __ \___ ____ _/ / /_ __(_)___ ___ ___
/ /_/ / _ \/ __ `/ / / / / / __ `__ \/ _ \
/ _, _/ __/ /_/ / / / / / / / / / / / __/
/_/ |_|\___/\__,_/_/ /_/ /_/_/ /_/ /_/\___/
____
/ __ \___ _________ ____ ____ ________
/ /_/ / _ \/ ___/ __ \/ __ \/ __ \/ ___/ _ \
/ _, _/ __(__ ) /_/ / /_/ / / / (__ ) __/
/_/ |_|\___/____/ .___/\____/_/ /_/____/\___/
/_/
This program will retrieve a single file of the same name
from multiple hosts. Files will be saved as 7zip archives
named after the host's AID.
Creation date: 11.30.23 - jshcodes@CrowdStrike
"""
import os
import multiprocessing
from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from logging import basicConfig, DEBUG
from time import sleep
try:
from falconpy import Hosts, RealTimeResponse
except ImportError as no_falconpy:
raise SystemExit(
"FalconPy v1.3 or greater must be installed to run this program."
) from no_falconpy


def consume_arguments() -> Namespace:
"""Consume any provided command line arguments."""
parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
parser.add_argument("-d", "--debug",
help="Enable API debugging",
action="store_true"
)
parser.add_argument("-n", "--hostname",
help="Hostname to target (stemmed search)",
default=""
)
parser.add_argument("-b", "--base_url", help="CrowdStrike API base URL", default="auto")
auth = parser.add_argument_group("authentication arguments")
auth.add_argument("-k", "--client-id",
dest="client_id",
help="Falcon API client ID",
default=os.getenv("FALCON_CLIENT_ID")
)
auth.add_argument("-s", "--client-secret",
dest="client_secret",
help="Falcon API client ID",
default=os.getenv("FALCON_CLIENT_SECRET")
)
req = parser.add_argument_group("required arguments")
req.add_argument("-f", "--filepath",
help="Filename and path of the file to be downloaded",
required=True
)
return parser.parse_args()


def wait_for_file(ses: dict, fname: str):
"""Asynchronously wait for a file to complete the get process.
After processing completes, delete the session.
"""
success = False
waiting = True
while waiting:
file_list = rtr.list_files_v2(session_id=ses["session_id"])["body"]["resources"]
for item in [f for f in file_list if f["cloud_request_id"] == ses["task_id"]]:
if item["complete"]:
waiting = False
else:
sleep(3)
file_download = rtr.get_extracted_file_contents(session_id=ses["session_id"],
sha256=item["sha256"],
filename=fname,
)
if isinstance(file_download, bytes):
with open(f"{ses['aid']}.7z", "wb") as save_file: # Save to a file named after the device ID
save_file.write(file_download)
print(f"Successfully downloaded {fname} from {ses['aid']} to {ses['aid']}.7z")
success = True

rtr.delete_session(ses["session_id"]) # Close this specific host session

return {"session_id": ses["session_id"],
"id": item["id"],
"success": success
}


start = datetime.now().timestamp()
successful = 0
cmdline = consume_arguments()
# Debug logging
if cmdline.debug:
basicConfig(level=DEBUG)
# Handle any provided command line hostname filters
target_filter = ""
if cmdline.hostname:
target_filter = f"hostname:*'*{cmdline.hostname}*'"
# Retrieve our target filename from the provided file path
_, target_file = os.path.split(cmdline.filepath)
# Construct instances of the Service Classes we are wanting to use.
hosts = Hosts(debug=cmdline.debug,
client_id=cmdline.client_id,
client_secret=cmdline.client_secret,
base_url=cmdline.base_url
)
rtr = RealTimeResponse(auth_object=hosts)
# Retrieve our target device AIDs.
target_devices = hosts.query_devices_by_filter_scroll(filter=target_filter)["body"]["resources"]
print(f"{len(target_devices)} matching hosts identified.")
# Initialize a session with the host batch.
session_init = rtr.batch_init_sessions(host_ids=target_devices)
batch_id = session_init["body"]["batch_id"] # Grab the batch ID
# Issue a batch get command
result = rtr.batch_get_command(batch_id=batch_id, file_path=cmdline.filepath)
# Quickly loop thru the result to create our batch of successful session IDs
sessions = [d for d in result["body"]["combined"]["resources"].values() if d["stdout"]]
print(f"File successfully identified on {len(sessions)} hosts.")
# Grab the batch ID
batch_req_id = result["body"]["batch_get_cmd_req_id"]
# Asynchronously wait for the upload to complete, and then the file to download
# Afterwards, delete the session and the file from the cloud
with ThreadPoolExecutor(max_workers=min(multiprocessing.cpu_count() * 2, 20)) as executor:
futures = {
executor.submit(wait_for_file, sess, target_file) for sess in sessions
}
for fut in as_completed(futures):
fresult = fut.result()
# Delete the file now that it's been downloaded
rtr.delete_file_v2(session_id=fresult["session_id"], ids=fresult["id"])
if fresult["success"]:
successful += 1
# Routine complete, display the total run time.
print(f"{successful} total files downloaded.")
print(f"Total run time: {datetime.now().timestamp() - start:.2f} seconds")

0 comments on commit 9dd5330

Please sign in to comment.