From eb31ddb7e1121af0d47598a5c71f86009e37d51f Mon Sep 17 00:00:00 2001 From: nesies <> Date: Wed, 22 May 2024 09:58:13 +0200 Subject: [PATCH 01/13] download_sensor.py_add_error_if_auth_fails --- samples/sensor_download/download_sensor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/sensor_download/download_sensor.py b/samples/sensor_download/download_sensor.py index 99b430326..3b46050a8 100644 --- a/samples/sensor_download/download_sensor.py +++ b/samples/sensor_download/download_sensor.py @@ -178,7 +178,12 @@ def create_constants(): filter=OS_FILTER, sort="version.desc" ) -if CMD in "list": + +if sensors["status_code"] == 401: + print("authentification failed, status_code={}".format( + sensors["status_code"])) + print("sensors:{}".format(sensors)) +elif CMD in "list": # List sensors data = [] headers = { From 18163c33b5429787188cdaa96d3fbf797af1dfe9 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Thu, 30 May 2024 11:58:15 -0400 Subject: [PATCH 02/13] Adjust error messaging. Add SystemExit. --- samples/sensor_download/download_sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/sensor_download/download_sensor.py b/samples/sensor_download/download_sensor.py index 3b46050a8..5260f9678 100644 --- a/samples/sensor_download/download_sensor.py +++ b/samples/sensor_download/download_sensor.py @@ -180,9 +180,9 @@ def create_constants(): ) if sensors["status_code"] == 401: - print("authentification failed, status_code={}".format( - sensors["status_code"])) - print("sensors:{}".format(sensors)) + raise SystemExit("Authentication failure (STATUS CODE {})".format(sensors["status_code"])) + + elif CMD in "list": # List sensors data = [] From 4d3eaabf556391b479791c97d09d7b873b44f531 Mon Sep 17 00:00:00 2001 From: nmills Date: Wed, 22 May 2024 14:57:04 +1200 Subject: [PATCH 03/13] added new arg for host regex, fix warnings, and errors --- samples/hosts/stale_sensors.py | 71 +++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/samples/hosts/stale_sensors.py b/samples/hosts/stale_sensors.py index be9dca774..a1113906f 100644 --- a/samples/hosts/stale_sensors.py +++ b/samples/hosts/stale_sensors.py @@ -21,8 +21,13 @@ - @morcef, jshcodes@CrowdStrike; 06.05.22 - More reasonable date calcs, Linting, Easier arg parsing Easier base_url handling, renamed grouping_tag to tag - jshcodes@Crowdstrike; 11.02.22 - Added CSV output options and cleaner date outputs. +- nmills@forbarr; 22.05.24 - Fixed deprecation warning on date function, + Added new arg to accept hostname pattern + Batch the call to hide_hosts to avoid API error """ import csv +import os +import re from argparse import ArgumentParser, RawTextHelpFormatter from datetime import datetime, timedelta, timezone from dateutil import parser as dparser @@ -34,7 +39,10 @@ "CrowdStrike FalconPy must be installed in order to use this application.\n" "Please execute `python3 -m pip install crowdstrike-falconpy` and try again." ) from no_falconpy - +# from dotenv import load_dotenv +# load_dotenv() +# clientId = os.getenv('clientid') +# clientSecret = os.getenv('secret') def parse_command_line() -> object: """Parse command-line arguments and return them back as an ArgumentParser object.""" @@ -46,13 +54,15 @@ def parse_command_line() -> object: '-k', '--client_id', help='CrowdStrike Falcon API key ID', - required=True + required=True, + # default=clientId ) parser.add_argument( '-s', '--client_secret', help='CrowdStrike Falcon API key secret', - required=True + required=True, + # default=clientSecret ) parser.add_argument( '-m', @@ -74,7 +84,8 @@ def parse_command_line() -> object: '-d', '--days', help='Number of days since a host was seen before it is considered stale', - required=False + required=False, + default=10 ) parser.add_argument( '-r', @@ -130,9 +141,15 @@ def parse_command_line() -> object: required=False, dest="osfilter" ) + parser.add_argument( + "-p", "--host-pattern", + help="filter hostnames by regex", + default=r".*", + required=False, + dest="hostfilter" + ) return parser.parse_args() - def connect_api(key: str, secret: str, base_url: str, child_cid: str = None) -> Hosts: """Connect to the API and return an instance of the Hosts Service Class.""" return Hosts(client_id=key, client_secret=secret, base_url=base_url, member_cid=child_cid) @@ -156,6 +173,10 @@ def get_hosts(date_filter: str, tag_filter: str, os_filter: str) -> list: if os_filter == "K8s": os_filter = "K8S" filter_string = f"{filter_string} + platform_name:'{os_filter}'" + x = falcon.query_devices_by_filter_scroll( + limit=5000, + filter=filter_string + )["body"]["resources"] return falcon.query_devices_by_filter_scroll( limit=5000, filter=filter_string @@ -164,7 +185,7 @@ def get_hosts(date_filter: str, tag_filter: str, os_filter: str) -> list: def calc_stale_date(num_days: int) -> str: """Calculate the 'stale' datetime based upon the number of days provided by the user.""" - today = datetime.utcnow() + today = datetime.now(timezone.utc) return str(today - timedelta(days=num_days)).replace(" ", "T") @@ -212,14 +233,17 @@ def hide_hosts(id_list: list) -> dict: # List to hold our identified hosts stale = [] # For each stale host identified -try: - for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)): - # Retrieve host detail - stale = parse_host_detail(host, stale) -except KeyError as api_error: - raise SystemExit( - "Unable to communicate with CrowdStrike API, check credentials and try again." - ) from api_error +if args.hostfilter == r".*": + pattern = args.hostfilter +else: + pattern = re.escape(args.hostfilter) + print(f"Pattern is: {pattern}") +for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)): + # Retrieve host detail + if 'hostname' in host: + if re.findall(pattern, host['hostname']): + print(f"{host['hostname']} matches expected pattern...") + stale = parse_host_detail(host, stale) # If we produced stale host results if stale: @@ -245,12 +269,15 @@ def hide_hosts(id_list: list) -> dict: else: # Remove the hosts host_list = [x[1] for x in stale] - remove_result = hide_hosts(host_list) - if remove_result["status_code"] == 202: - for deleted in remove_result["body"]["resources"]: - print(f"Removed host {deleted['id']}") - else: - for deleted in remove_result["body"]["errors"]: - print(f"[{deleted['code']}] {deleted['message']}") + batch_size = 50 + for i in range(0, len(host_list), batch_size): + batch = host_list[i:i + batch_size] + remove_result = hide_hosts(batch) + if remove_result["status_code"] == 202: + for deleted in remove_result["body"]["resources"]: + print(f"Removed host {deleted['id']}") + else: + for deleted in remove_result["body"]["errors"]: + print(f"[{deleted['code']}] {deleted['message']}") else: - print("No stale hosts identified for the range specified.") + print("No stale hosts identified for the range specified.") \ No newline at end of file From ad2386eda14b020df192e94fe3165716543c69f3 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 31 May 2024 01:00:07 -0400 Subject: [PATCH 04/13] Update documentation to detail new command line argument. --- samples/hosts/README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/samples/hosts/README.md b/samples/hosts/README.md index a1bea4c6e..eb1cebc7d 100644 --- a/samples/hosts/README.md +++ b/samples/hosts/README.md @@ -746,6 +746,12 @@ This variation will retrieve a list of hosts that haven't checked in to CrowdStr python3 stale_sensors.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -d 30 -t testtag ``` +This variation leverages a regular expression to match the host "SDKDEMO3". + +```shell +python3 stale_sensors.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -d 30 -p "^SDK.*3$" +``` + You can reverse the list sort with the `-r` or `--reverse` argument. ```shell @@ -762,7 +768,8 @@ Command-line help is available via the `-h` argument. ```shell % python3 stale_sensors.py -h -usage: stale_sensors.py [-h] -k CLIENT_ID -s CLIENT_SECRET [-m MSSP] [-g] [-d DAYS] [-r] [-x] [-t TAG] +usage: stale_sensors.py [-h] -k CLIENT_ID -s CLIENT_SECRET [-m MSSP] [-g] [-d DAYS] [-r] [-x] [-t TAG] [-c] [-o OUTPUT_FILE] [-q] + [-f {windows,mac,linux,k8s}] [-p HOSTFILTER] CrowdStrike Unattended Stale Sensor Environment Detector. @@ -786,6 +793,10 @@ results for the US-GOV-1 region, pass the '-g' argument. - ray.heffer@crowdstrike.com; 03.29.22 - Added new argument for Grouping Tags (--grouping, -g) - @morcef, jshcodes@CrowdStrike; 06.05.22 - More reasonable date calcs, Linting, Easier arg parsing Easier base_url handling, renamed grouping_tag to tag +- jshcodes@Crowdstrike; 11.02.22 - Added CSV output options and cleaner date outputs. +- nmills@forbarr; 22.05.24 - Fixed deprecation warning on date function, + Added new arg to accept hostname pattern + Batch the call to hide_hosts to avoid API error optional arguments: -h, --help show this help message and exit @@ -799,6 +810,14 @@ optional arguments: -r, --reverse Reverse sort (defaults to ASC) -x, --remove Remove hosts identified as stale -t TAG, --tag TAG Falcon Grouping Tag name for the hosts + -c, --csv Export results to CSV + -o OUTPUT_FILE, --output_file OUTPUT_FILE + File to output CSV results to. Ignored when "-c" is not specified. + -q, --quotes Quote non-numeric fields in CSV output. + -f {windows,mac,linux,k8s}, --filter-by-os {windows,mac,linux,k8s} + OS filter (windows, macos, linux) + -p HOSTFILTER, --host-pattern HOSTFILTER + filter hostnames by regex ``` ### Example source code From fe587d9290a9becf43682ae7d3af96987803ec0e Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 31 May 2024 01:00:35 -0400 Subject: [PATCH 05/13] Linting --- samples/hosts/stale_sensors.py | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/samples/hosts/stale_sensors.py b/samples/hosts/stale_sensors.py index a1113906f..1dabf7531 100644 --- a/samples/hosts/stale_sensors.py +++ b/samples/hosts/stale_sensors.py @@ -26,7 +26,6 @@ Batch the call to hide_hosts to avoid API error """ import csv -import os import re from argparse import ArgumentParser, RawTextHelpFormatter from datetime import datetime, timedelta, timezone @@ -39,10 +38,6 @@ "CrowdStrike FalconPy must be installed in order to use this application.\n" "Please execute `python3 -m pip install crowdstrike-falconpy` and try again." ) from no_falconpy -# from dotenv import load_dotenv -# load_dotenv() -# clientId = os.getenv('clientid') -# clientSecret = os.getenv('secret') def parse_command_line() -> object: """Parse command-line arguments and return them back as an ArgumentParser object.""" @@ -54,15 +49,13 @@ def parse_command_line() -> object: '-k', '--client_id', help='CrowdStrike Falcon API key ID', - required=True, - # default=clientId + required=True ) parser.add_argument( '-s', '--client_secret', help='CrowdStrike Falcon API key secret', - required=True, - # default=clientSecret + required=True ) parser.add_argument( '-m', @@ -233,16 +226,14 @@ def hide_hosts(id_list: list) -> dict: # List to hold our identified hosts stale = [] # For each stale host identified -if args.hostfilter == r".*": - pattern = args.hostfilter -else: - pattern = re.escape(args.hostfilter) - print(f"Pattern is: {pattern}") +pattern = args.hostfilter +if args.hostfilter != r".*": + print(f"Pattern is: {re.escape(pattern)}") + for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)): # Retrieve host detail if 'hostname' in host: if re.findall(pattern, host['hostname']): - print(f"{host['hostname']} matches expected pattern...") stale = parse_host_detail(host, stale) # If we produced stale host results @@ -280,4 +271,4 @@ def hide_hosts(id_list: list) -> dict: for deleted in remove_result["body"]["errors"]: print(f"[{deleted['code']}] {deleted['message']}") else: - print("No stale hosts identified for the range specified.") \ No newline at end of file + print("No stale hosts identified for the range specified.") From 32a3ada69fb05b3720321d17215980149d0752c8 Mon Sep 17 00:00:00 2001 From: David-M-Berry <168669748+David-M-Berry@users.noreply.github.com> Date: Tue, 28 May 2024 10:05:06 -0700 Subject: [PATCH 06/13] Updated get_hostnames function to ignore comments. --- samples/hosts/host_search.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/samples/hosts/host_search.py b/samples/hosts/host_search.py index e433a1d88..53db06654 100755 --- a/samples/hosts/host_search.py +++ b/samples/hosts/host_search.py @@ -66,12 +66,16 @@ def consume_arguments() -> Namespace: def get_hostnames(target_file: str): - """Open CSV and import serials.""" + """Open file and import hostnames, ignoring comments.""" try: - with open(target_file, newline='') as host_file: + with open(target_file, 'r') as host_file: print("Opening hostname file") - return host_file.read().splitlines() - + hostnames = [] + for line in host_file: + line = line.split('#')[0].strip() # Remove comments and strip whitespace + if line: # Ignore empty lines + hostnames.append(line) + return hostnames except FileNotFoundError: raise SystemExit( "You must provide a valid hostname file with the '-f' argument, " From ff6beb33987f3d8f83fbc06e478bc0a3e4313bcb Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 31 May 2024 01:30:52 -0400 Subject: [PATCH 07/13] Add comment and fix doc typo --- samples/hosts/host_search.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/hosts/host_search.py b/samples/hosts/host_search.py index 53db06654..149d47082 100755 --- a/samples/hosts/host_search.py +++ b/samples/hosts/host_search.py @@ -22,6 +22,7 @@ which hostnames are not currently reporting in to the console. Developed by @Don-Swanson-Adobe +Modification: 05.28.24 - David M. Berry - Updated get_hostnames function to ignore comments. """ import os import logging @@ -79,7 +80,7 @@ def get_hostnames(target_file: str): except FileNotFoundError: raise SystemExit( "You must provide a valid hostname file with the '-f' argument, " - "or a host with the '-h' argument in order to run this program." + "or a host with the '-n' argument in order to run this program." ) From 9792cd9debbbc637be06af9794fc95b4327837d6 Mon Sep 17 00:00:00 2001 From: David-M-Berry <168669748+David-M-Berry@users.noreply.github.com> Date: Mon, 3 Jun 2024 20:57:22 -0600 Subject: [PATCH 08/13] Create host_search_advanced.py adding an advanced host search which can take partial matches of a hostname in a wildcard manner from either the command line or hostname file, as well as ignore comments in a hostname file to keep a cleaner output.csv. --- samples/hosts/host_search_advanced.py | 137 ++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 samples/hosts/host_search_advanced.py diff --git a/samples/hosts/host_search_advanced.py b/samples/hosts/host_search_advanced.py new file mode 100644 index 000000000..0c5423f1e --- /dev/null +++ b/samples/hosts/host_search_advanced.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +r""" + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | |::.. . | FalconPy +`-------' `-------' + + _ _ _ ____ _ + | | | | ___ ___| |_ / ___| ___ __ _ _ __ ___| |__ + | |_| |/ _ \/ __| __| \___ \ / _ \/ _` | '__/ __| '_ \ + | _ | (_) \__ \ |_ ___) | __/ (_| | | | (__| | | | + |_| |_|\___/|___/\__| |____/ \___|\__,_|_| \___|_| |_| + _ _ _ + / \ __| |_ ____ _ _ __ ___ ___ __| | + / _ \ / _` \ \ / / _` | '_ \ / __/ _ \/ _` | + / ___ \ (_| |\ V / (_| | | | | (_| __/ (_| | + /_/ \_\__,_| \_/ \__,_|_| |_|\___\___|\__,_| + + +This script will take a file listing of hostnames (one host per line) or +a single hostname provided at runtime to produce a CSV containing the +details for hosts that are found. This solution can be used to compare a +list of hostnames to the list of hosts in the Falcon Console to determine +which hostnames are not currently reporting in to the console, or to discover hosts based on a partial match of the hostname. Comments in input files are also ommitted from lookup, thus keeping the output.csv clean, and allowing you to work with more useful host name files/inventory. + +Developed by @Don-Swanson-Adobe, additional functionality by @David-M-Berry +""" + + +import os +import logging +from argparse import ArgumentParser, RawTextHelpFormatter, Namespace +from falconpy import Hosts + + +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", + default=False + ) + parser.add_argument("-n", "--hostname", + help="Hostname to search for", + required=False # Make this argument optional + ) + parser.add_argument("-i", "--input_file", # Add a new argument for input file + help="Text file containing hostnames to search for" + ) + parser.add_argument("-o", "--output_path", + help="Location to store CSV output", + default="output.csv" + ) + req = parser.add_argument_group("Required arguments") + req.add_argument("-k", "--client_id", + help="CrowdStrike Falcon API key", + default=os.getenv("FALCON_CLIENT_ID") + ) + req.add_argument("-s", "--client_secret", + help="CrowdStrike Falcon API secret", + default=os.getenv("FALCON_CLIENT_SECRET") + ) + parsed = parser.parse_args() + if not parsed.client_id or not parsed.client_secret: + parser.error("You must provide CrowdStrike API credentials using the '-k' and '-s' arguments.") + + return parsed + + +def process_hostnames(hostnames): + with open(cmd_line.output_path, 'w') as file_object: # Open in 'write' mode to clear previous content + with Hosts(client_id=cmd_line.client_id, + client_secret=cmd_line.client_secret, + debug=cmd_line.debug, + pythonic=True + ) as falcon: + for hostname in hostnames: + host_ids = falcon.query_devices_by_filter(filter=f"hostname:*'*{hostname}*'") + if host_ids: + for device in falcon.get_device_details(ids=host_ids.data): + device_hostname = device["hostname"] + last_seen = device["last_seen"] + rfm = device["reduced_functionality_mode"] + cid = device["cid"] + tags = device["tags"] + tag = ",".join(tag_string.replace('SensorGroupingTags/', '') for tag_string in tags) + file_object.write(f"{device_hostname},{cid},{rfm},{last_seen},{tag}\n") + else: + file_object.write(f"{hostname},HOST NOT FOUND\n") + + # Deduplicate the output file + deduplicate_output(cmd_line.output_path) + print(f"Search complete, results have been written to {cmd_line.output_path}") + + +def deduplicate_output(output_path): + """Remove duplicate entries from the output file.""" + with open(output_path, 'r') as file: + lines = file.readlines() + unique_lines = set(lines) + with open(output_path, 'w') as file: + file.writelines(unique_lines) + + +def prepend_header(output_path): + """Prepend the header line to the output file.""" + with open(output_path, 'r+') as file: + content = file.read() + file.seek(0, 0) + file.write("Hostname,CID,RFM,Last Seen,Landscape,Tag1,Tag2,Tag3,Tag4\n" + content) + + +cmd_line = consume_arguments() + +if cmd_line.hostname: + hostnames = [cmd_line.hostname] +elif cmd_line.input_file: + with open(cmd_line.input_file, 'r') as file: + hostnames = [] + for line in file: + line = line.split('#')[0].strip() # Remove comments and strip whitespace + if line: # Ignore empty lines + hostnames.append(line) +else: + print("You must provide either a hostname or an input file.") + exit(1) + + +if cmd_line.debug: + logging.basicConfig(level=logging.DEBUG) + +process_hostnames(hostnames) +prepend_header(cmd_line.output_path) From c16995c8366c920ced22b92d4a59654b94c15549 Mon Sep 17 00:00:00 2001 From: David-M-Berry <168669748+David-M-Berry@users.noreply.github.com> Date: Mon, 3 Jun 2024 21:11:06 -0600 Subject: [PATCH 09/13] Update README.md added Host Search Advanced section. Fixed typo with Host Report header section. --- samples/hosts/README.md | 66 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/samples/hosts/README.md b/samples/hosts/README.md index eb1cebc7d..3364fc64b 100644 --- a/samples/hosts/README.md +++ b/samples/hosts/README.md @@ -9,6 +9,7 @@ The examples in this folder focus on leveraging CrowdStrike's Hosts API to perfo - [Get Host Groups](#get-host-groups) - [Host Report](#host-report) - [Host Search](#host-search) +- [Host Search Advanced](#host-search-advanced) - [List sensor versions by Hostname](#list-sensors-by-hostname) - [List (and optionally remove) duplicate sensors](#list-duplicate-sensors) - [List (and optionally remove) stale sensors](#list-stale-sensors) @@ -362,7 +363,7 @@ The source code for these examples can be found [here](get_host_groups.py). --- -## Hosts Report +## Host Report This script replaces the manual daily export of hosts from the Falcon Console that was required to audit host compliance. It was developed to be run as a recurring job and will output a CSV with all hosts in the CID along with other required info that can then be imported into a compliance dashboard or tool. ### Running the program @@ -545,6 +546,67 @@ Required arguments: ### Example source code The source code for these examples can be found [here](host_search.py). +--- +## Host Search Advanced + +This script retains the original functionality of host_search.py above, but adds in functionality for partial matches of hostnames. This will help with endpoint discovery where the domain is known, or a pattern of host naming is known, but not all endpoints have been discovered. + +This script will also ignore comments in a hostname file, thus keeping the output.csv cleaner. + +To read an input file of hostnames, the -f option (used in the original host_search.py) has been changed to -i. This made more sense considering the more "insensitive" nature of the search, and makes a visual idendification of the full command easier if you use both the original host_search.py, and the host_search_advanced.py. A potential use case could be to discover hosts using the 'advanced' search, in order to reconcile with hostname files for use with the original host search. + +#### Command-line help +Command-line help is available via the `-h` argument. + +```shell +usage: host_search_advanced.py [-h] [-d] [-n HOSTNAME] [-i INPUT_FILE] [-o OUTPUT_PATH] + [-k CLIENT_ID] [-s CLIENT_SECRET] + + _______ __ _______ __ __ __ +| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----. +|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__| +|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____| +|: 1 | |: 1 | +|::.. . | |::.. . | FalconPy +`-------' `-------' + + _ _ _ ____ _ + | | | | ___ ___| |_ / ___| ___ __ _ _ __ ___| |__ + | |_| |/ _ \/ __| __| \___ \ / _ \/ _` | '__/ __| '_ \ + | _ | (_) \__ \ |_ ___) | __/ (_| | | | (__| | | | + |_| |_|\___/|___/\__| |____/ \___|\__,_|_| \___|_| |_| + _ _ _ + / \ __| |_ ____ _ _ __ ___ ___ __| | + / _ \ / _` \ \ / / _` | '_ \ / __/ _ \/ _` | + / ___ \ (_| |\ V / (_| | | | | (_| __/ (_| | + /_/ \_\__,_| \_/ \__,_|_| |_|\___\___|\__,_| + + +This script will take a file listing of hostnames (one host per line) or +a single hostname provided at runtime to produce a CSV containing the +details for hosts that are found. This solution can be used to compare a +list of hostnames to the list of hosts in the Falcon Console to determine +which hostnames are not currently reporting in to the console, or to discover hosts based on a partial match of the hostname. Comments in input files are also ommitted from lookup, thus keeping the output.csv clean, and allowing you to work with more useful host name files/inventory. + +Developed by @Don-Swanson-Adobe, additional functionality by @David-M-Berry + +options: + -h, --help show this help message and exit + -d, --debug Enable API debugging + -n HOSTNAME, --hostname HOSTNAME + Hostname to search for + -i INPUT_FILE, --input_file INPUT_FILE + Text file containing hostnames to search for + -o OUTPUT_PATH, --output_path OUTPUT_PATH + Location to store CSV output + +Required arguments: + -k CLIENT_ID, --client_id CLIENT_ID + CrowdStrike Falcon API key + -s CLIENT_SECRET, --client_secret CLIENT_SECRET + CrowdStrike Falcon API secret +``` + --- @@ -1326,4 +1388,4 @@ Required arguments: ### Example source code The source code for these examples can be found [here](serial_search.py). ---- \ No newline at end of file +--- From aec3c23116b2d7c81703d1ee9ae057ef56cbcafe Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Tue, 4 Jun 2024 07:57:44 -0400 Subject: [PATCH 10/13] Fix typo --- samples/hosts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/hosts/README.md b/samples/hosts/README.md index 3364fc64b..8a3cdb2d4 100644 --- a/samples/hosts/README.md +++ b/samples/hosts/README.md @@ -553,7 +553,7 @@ This script retains the original functionality of host_search.py above, but adds This script will also ignore comments in a hostname file, thus keeping the output.csv cleaner. -To read an input file of hostnames, the -f option (used in the original host_search.py) has been changed to -i. This made more sense considering the more "insensitive" nature of the search, and makes a visual idendification of the full command easier if you use both the original host_search.py, and the host_search_advanced.py. A potential use case could be to discover hosts using the 'advanced' search, in order to reconcile with hostname files for use with the original host search. +To read an input file of hostnames, the -f option (used in the original host_search.py) has been changed to -i. This made more sense considering the more "insensitive" nature of the search, and makes a visual identification of the full command easier if you use both the original host_search.py, and the host_search_advanced.py. A potential use case could be to discover hosts using the 'advanced' search, in order to reconcile with hostname files for use with the original host search. #### Command-line help Command-line help is available via the `-h` argument. From a756ad3d8e649ae302afe622e459e7cfc33c3591 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Tue, 4 Jun 2024 07:58:18 -0400 Subject: [PATCH 11/13] Update wordlist.txt --- .github/wordlist.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index 9638e59db..e411f0c5e 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -1379,3 +1379,5 @@ WorkflowTriggersCombined Destom ValueError QueryCasesIdsByFilter +SDKDEMO + From c678e8a24886a03b5baf700f0d069932d8a5d169 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Wed, 5 Jun 2024 10:23:14 -0400 Subject: [PATCH 12/13] Update sample library home --- samples/README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 760a51546..6b2379689 100644 --- a/samples/README.md +++ b/samples/README.md @@ -45,7 +45,7 @@ The following samples are categorized by CrowdStrike product, and further catego | Topic | Samples | | :-- | :-- | -| [Hosts](#hosts-samples)
[Host Groups](#hosts-samples)
| List sensors by hostname
Manage duplicate sensors
CUSSED (Manage stale sensors)
Default Groups
Get Host Groups
Hosts Report
Host Search
Host Tagger
Policy Check
RFM Report
Serial Search
Match usernames to hosts
Offset vs. Token
Prune Hosts by Hostname or AID
Quarantine a host
Quarantine a host (updated version) | +| [Hosts](#hosts-samples)
[Host Groups](#hosts-samples)
| List sensors by hostname
Manage duplicate sensors
CUSSED (Manage stale sensors)
Default Groups
Get Host Groups
Hosts Report
Host Search
Host Search Advanced
Host Tagger
Policy Check
RFM Report
Serial Search
Match usernames to hosts
Offset vs. Token
Prune Hosts by Hostname or AID
Quarantine a host
Quarantine a host (updated version) | | [Report Executions](#report-executions-samples) | Retrieve all report results | | [Sensor Download](#sensor-download-samples) | Download the CrowdStrike sensor | | [Sensor Update Policies](#sensor-update-policies-samples) | Clone Update Policy
Create Host Group and attach Update Policy
Policy Wonk | @@ -196,6 +196,7 @@ The samples collected in this section demonstrate leveraging CrowdStrike's Hosts - [Get Host Groups](#get-host-group) - [Hosts Report](#hosts-report) - [Host Search](#host-search) +- [Host Search Advanced](#host-search-advanced) - [Host Tagger](#host-tagger) - [Match usernames to hosts](#match-usernames-to-hosts) - [Offset vs. Token](#offset-vs-token) @@ -332,6 +333,22 @@ This sample demonstrates the following CrowdStrike Hosts API operations: --- +#### Hosts Search Advanced +This [example](hosts#hosts-search-advanced) will demonstrate how to search for host details by hostname. + +[![Hosts](https://img.shields.io/badge/Service%20Class-Hosts_Search-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==)](hosts#hosts-search-advanced) +[![Community Contribution](https://img.shields.io/badge/-Contribution-2C6B07?style=for-the-badge&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==)](https://github.com/CrowdStrike/falconpy/blob/main/AUTHORS.md) + +##### Hosts API operations discussed +This sample demonstrates the following CrowdStrike Hosts API operations: + +| Operation | Description | +| :--- | :--- | +| [GetDeviceDetails](https://falconpy.io/Service-Collections/Hosts.html#getdevicedetails) | Get details on one or more hosts by providing agent IDs (AID). You can get a host's agent IDs (AIDs) from the [QueryDevicesByFilter](https://www.falconpy.io/Service-Collections/Hosts.html#querydevicesbyfilter) operation, the Falcon console or the Streaming API. | +| [QueryDevicesByFilter](https://falconpy.io/Service-Collections/Hosts.html#querydevicesbyfilter) | Search for hosts in your environment by platform, hostname, IP, and other criteria. | + +--- + #### Hosts Tagger This [example](hosts#hosts-tagger) will demonstrate how to tag or untag multiple hosts in batch. From 7da0ccf788f99f428e4893f761442f4da06d0ae7 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Wed, 5 Jun 2024 10:23:53 -0400 Subject: [PATCH 13/13] Add @nickforsythbarr, @nesies and @David-M-Berry --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index cab8e04e8..84a4e73f6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -94,6 +94,9 @@ This has been a critical element in the development of the FalconPy project. + `@PeroSoy` + Shubham, `@i-shubham01` + Don "Swanson" I., `@Don-Swanson-Adobe` ++ Nick, `nickforsythbarr` ++ `nesies` ++ `David-M-Berry` ## Sponsors