From c4881715e5231bec4d1c5ba414c89b17b35b76b6 Mon Sep 17 00:00:00 2001 From: jshcodes <74007258+jshcodes@users.noreply.github.com> Date: Fri, 2 Apr 2021 11:46:18 -0400 Subject: [PATCH 01/25] Update label_request.yml Labeller fails on forks, removing workflow from dev branches. --- .github/workflows/label_request.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/label_request.yml b/.github/workflows/label_request.yml index 70cf621c..0d6f448a 100644 --- a/.github/workflows/label_request.yml +++ b/.github/workflows/label_request.yml @@ -3,7 +3,6 @@ on: pull_request: branches: - main - - 'ver_*' jobs: triage: From 1d13967e7e1798ec0c264fa601d685a0c6bbbf26 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 13:31:38 -0400 Subject: [PATCH 02/25] Sample code: Sample Uploads API --- samples/sample_uploads/README.md | 1 + samples/sample_uploads/sample_uploads.py | 97 ++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 samples/sample_uploads/README.md create mode 100644 samples/sample_uploads/sample_uploads.py diff --git a/samples/sample_uploads/README.md b/samples/sample_uploads/README.md new file mode 100644 index 00000000..fa9138ad --- /dev/null +++ b/samples/sample_uploads/README.md @@ -0,0 +1 @@ +# FalconPy usage examples \ No newline at end of file diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py new file mode 100644 index 00000000..fd6bfa88 --- /dev/null +++ b/samples/sample_uploads/sample_uploads.py @@ -0,0 +1,97 @@ +# ____ _ _ _ _ _ +# / ___| __ _ _ __ ___ _ __ | | ___ | | | |_ __ | | ___ __ _ __| |___ +# \___ \ / _` | '_ ` _ \| '_ \| |/ _ \ | | | | '_ \| |/ _ \ / _` |/ _` / __| +# ___) | (_| | | | | | | |_) | | __/ | |_| | |_) | | (_) | (_| | (_| \__ \ +# |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ +# |_| |_| + +# _ ____ ___ _____ _ +# / \ | _ \_ _| | ____|_ ____ _ _ __ ___ _ __ | | ___ ___ +# / _ \ | |_) | | | _| \ \/ / _` | '_ ` _ \| '_ \| |/ _ \/ __| +# / ___ \| __/| | | |___ > < (_| | | | | | | |_) | | __/\__ \ +# /_/ \_\_| |___| |_____/_/\_\__,_|_| |_| |_| .__/|_|\___||___/ +# |_| +# +# These examples show how to interact with the Sample Uploads API using +# both the Uber class, and the regular Service class. +# +import json + +# _ _ _ ____ _ +# | | | | |__ ___ _ __ / ___| | __ _ ___ ___ +# | | | | '_ \ / _ \ '__| | | | |/ _` / __/ __| +# | |_| | |_) | __/ | | |___| | (_| \__ \__ \ +# \___/|_.__/ \___|_| \____|_|\__,_|___/___/ +# +# This example shows how to interact with the +# Sample Uploads API using the Uber class. + +from falconpy import api_complete as FalconSDK + +# Grab our config parameters +with open('config.json', 'r') as file_config: + config = json.loads(file_config.read()) + +# Create an instance of the Uber class +falcon = FalconSDK.APIHarness(creds={ + "client_id": config["falcon_client_id"], + "client_secret": config["falcon_client_secret"] + } +) + +# Define our file +FILENAME = "testfile.jpg" +# Open the file for binary read, this will be our payload +PAYLOAD = open(FILENAME, 'rb').read() +# Upload the file using the Sample Uploads API, name this file "newfile.jpg" in the API +response = falcon.command('UploadSampleV3', file_name="newfile.jpg", data=PAYLOAD, content_type="application/octet-stream") +# Grab the SHA256 unique identifier for the file we just uploaded +sha = response["body"]["resources"][0]["sha256"] +# Download a copy of this file, use the SHA256 ID to retrieve it +response = falcon.command("GetSampleV3", ids=sha) +# Save the result to a new file +open('uberclass.jpg', 'wb').write(response) +# Delete the file from the API +response = falcon.command("DeleteSampleV3", ids=sha) +# Print the results of our delete command +print(json.dumps(response, indent=4)) + + +# ____ _ ____ _ +# / ___| ___ _ ____ _(_) ___ ___ / ___| | __ _ ___ ___ +# \___ \ / _ \ '__\ \ / / |/ __/ _ \ | | | |/ _` / __/ __| +# ___) | __/ | \ V /| | (_| __/ | |___| | (_| \__ \__ \ +# |____/ \___|_| \_/ |_|\___\___| \____|_|\__,_|___/___/ +# +# This example shows how to interact with the +# Sample Uploads API using the Uber class. + +from falconpy import sample_uploads as FalconUploads + +# #Grab our config parameters +with open('config.json', 'r') as file_config: + config = json.loads(file_config.read()) + +falcon = FalconUploads.Sample_Uploads(creds={ + "client_id": config["falcon_client_id"], + "client_secret": config["falcon_client_secret"] + } +) + +# Define our file +FILENAME = "testfile.jpg" +# Open the file for binary read, this will be our payload +PAYLOAD = open(FILENAME, 'rb').read() +# Upload the file using the Sample Uploads API, name this file "newfile.jpg" in the API +# Since we are using the Service Class, we do not need to specify the content type +response = falcon.UploadSampleV3(file_name="newfile.jpg", file_data=PAYLOAD) +# Grab the SHA256 unique identifier for the file we just uploaded +sha = response["body"]["resources"][0]["sha256"] +# Download a copy of this file, use the SHA256 ID to retrieve it +response = falcon.GetSampleV3(ids=sha) +# Save the result to a new file +open('serviceclass.jpg', 'wb').write(response) +# Delete the file from the API +response = falcon.DeleteSampleV3(ids=sha) +# Print the results of our delete command +print(json.dumps(response, indent=4)) \ No newline at end of file From bcb75a4ee3514f2b66fc5d0f5aa4659272be49cf Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 13:35:25 -0400 Subject: [PATCH 03/25] Code sample: Sample Uploads API --- samples/sample_uploads/sample_uploads.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py index fd6bfa88..726c6fc1 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads.py @@ -1,16 +1,16 @@ -# ____ _ _ _ _ _ -# / ___| __ _ _ __ ___ _ __ | | ___ | | | |_ __ | | ___ __ _ __| |___ -# \___ \ / _` | '_ ` _ \| '_ \| |/ _ \ | | | | '_ \| |/ _ \ / _` |/ _` / __| -# ___) | (_| | | | | | | |_) | | __/ | |_| | |_) | | (_) | (_| | (_| \__ \ -# |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ +# ____ _ _ _ _ _ _ ____ ___ +# / ___| __ _ _ __ ___ _ __ | | ___ | | | |_ __ | | ___ __ _ __| |___ / \ | _ \_ _| +# \___ \ / _` | '_ ` _ \| '_ \| |/ _ \ | | | | '_ \| |/ _ \ / _` |/ _` / __| / _ \ | |_) | | +# ___) | (_| | | | | | | |_) | | __/ | |_| | |_) | | (_) | (_| | (_| \__ \ / ___ \| __/| | +# |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ /_/ \_\_| |___| # |_| |_| - -# _ ____ ___ _____ _ -# / \ | _ \_ _| | ____|_ ____ _ _ __ ___ _ __ | | ___ ___ -# / _ \ | |_) | | | _| \ \/ / _` | '_ ` _ \| '_ \| |/ _ \/ __| -# / ___ \| __/| | | |___ > < (_| | | | | | | |_) | | __/\__ \ -# /_/ \_\_| |___| |_____/_/\_\__,_|_| |_| |_| .__/|_|\___||___/ -# |_| +# +# _____ _ +# | ____|_ ____ _ _ __ ___ _ __ | | ___ ___ +# | _| \ \/ / _` | '_ ` _ \| '_ \| |/ _ \/ __| +# | |___ > < (_| | | | | | | |_) | | __/\__ \ +# |_____/_/\_\__,_|_| |_| |_| .__/|_|\___||___/ +# |_| # # These examples show how to interact with the Sample Uploads API using # both the Uber class, and the regular Service class. From 78efe406ca12fcd79a848775cdb66d3cfd79ad40 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 13:37:33 -0400 Subject: [PATCH 04/25] Sample config.json file --- samples/config_sample.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 samples/config_sample.json diff --git a/samples/config_sample.json b/samples/config_sample.json new file mode 100644 index 00000000..51d83b29 --- /dev/null +++ b/samples/config_sample.json @@ -0,0 +1,4 @@ +{ + "falcon_client_id": "API ID GOES HERE", + "falcon_client_secret": "API SECRET GOES HERE" +} From c74cc9c331cd078ed35aebc0c53160886352169b Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:00:44 -0400 Subject: [PATCH 05/25] Documentation updates --- samples/sample_uploads/README.md | 17 ++++++++++++++++- samples/sample_uploads/sample_uploads.py | 6 ------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/samples/sample_uploads/README.md b/samples/sample_uploads/README.md index fa9138ad..c4279933 100644 --- a/samples/sample_uploads/README.md +++ b/samples/sample_uploads/README.md @@ -1 +1,16 @@ -# FalconPy usage examples \ No newline at end of file +![CrowdStrike Falcon](https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png) [![Twitter URL](https://img.shields.io/twitter/url?label=Follow%20%40CrowdStrike&style=social&url=https%3A%2F%2Ftwitter.com%2FCrowdStrike)](https://twitter.com/CrowdStrike)
+ +# FalconPy usage examples +These examples are provided as a quick start for your project. + ++ [Samples by API](#samples-by-api) ++ [Suggestions](#suggestions) + +## Samples by API +These samples are categorized by API service collection. + +### Sample Uploads API ++ [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads.py) + +## Suggestions +Have a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). \ No newline at end of file diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py index 726c6fc1..3d435eb6 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads.py @@ -5,12 +5,6 @@ # |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ /_/ \_\_| |___| # |_| |_| # -# _____ _ -# | ____|_ ____ _ _ __ ___ _ __ | | ___ ___ -# | _| \ \/ / _` | '_ ` _ \| '_ \| |/ _ \/ __| -# | |___ > < (_| | | | | | | |_) | | __/\__ \ -# |_____/_/\_\__,_|_| |_| |_| .__/|_|\___||___/ -# |_| # # These examples show how to interact with the Sample Uploads API using # both the Uber class, and the regular Service class. From 6a35d1f312bb7227adc44d2ee39da540e770e429 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:04:51 -0400 Subject: [PATCH 06/25] Documentation updates --- samples/{sample_uploads => }/README.md | 9 +++++++++ samples/sample_uploads/sample_uploads.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) rename samples/{sample_uploads => }/README.md (59%) diff --git a/samples/sample_uploads/README.md b/samples/README.md similarity index 59% rename from samples/sample_uploads/README.md rename to samples/README.md index c4279933..558ada0b 100644 --- a/samples/sample_uploads/README.md +++ b/samples/README.md @@ -3,9 +3,18 @@ # FalconPy usage examples These examples are provided as a quick start for your project. ++ [Authentication for Examples](#authentication-for-these-examples) + [Samples by API](#samples-by-api) + [Suggestions](#suggestions) +## Authentication for these Examples +In order to expedite sample delivery, we will be following a standard pattern for defining and providing credentials to the API. +This is not the only method of providing these values, and not recommended for production deployments as the config.json file is +**not encrypted**. + +In order to test these samples locally in your development environment, update the file config.json to reflect your current +development API credentials. + ## Samples by API These samples are categorized by API service collection. diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py index 3d435eb6..2589b880 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads.py @@ -23,7 +23,7 @@ from falconpy import api_complete as FalconSDK # Grab our config parameters -with open('config.json', 'r') as file_config: +with open('../config.json', 'r') as file_config: config = json.loads(file_config.read()) # Create an instance of the Uber class From 9292c758d46edebe2c8db85970d51a8e36671d98 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:05:36 -0400 Subject: [PATCH 07/25] Documentation updates --- samples/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 558ada0b..d547b7be 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,4 +1,6 @@ -![CrowdStrike Falcon](https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png) [![Twitter URL](https://img.shields.io/twitter/url?label=Follow%20%40CrowdStrike&style=social&url=https%3A%2F%2Ftwitter.com%2FCrowdStrike)](https://twitter.com/CrowdStrike)
+![CrowdStrike Falcon](https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png) + +[![Twitter URL](https://img.shields.io/twitter/url?label=Follow%20%40CrowdStrike&style=social&url=https%3A%2F%2Ftwitter.com%2FCrowdStrike)](https://twitter.com/CrowdStrike) # FalconPy usage examples These examples are provided as a quick start for your project. From 35eab6ded5145d98f47503292fb3a4041cb5d44e Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:07:54 -0400 Subject: [PATCH 08/25] Documentation updates --- samples/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index d547b7be..a90df203 100644 --- a/samples/README.md +++ b/samples/README.md @@ -24,4 +24,6 @@ These samples are categorized by API service collection. + [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads.py) ## Suggestions -Have a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). \ No newline at end of file +Got a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). + +Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](https://github.com/CrowdStrike/falconpy/CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file From b3a2ac87fdaa02ebadbc31a37429cdf013a9fca9 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:09:01 -0400 Subject: [PATCH 09/25] Documentation updates --- samples/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/README.md b/samples/README.md index a90df203..b19fc550 100644 --- a/samples/README.md +++ b/samples/README.md @@ -14,8 +14,8 @@ In order to expedite sample delivery, we will be following a standard pattern fo This is not the only method of providing these values, and not recommended for production deployments as the config.json file is **not encrypted**. -In order to test these samples locally in your development environment, update the file config.json to reflect your current -development API credentials. +In order to test these samples locally in your development environment, rename the file `config_sample.json` to `config.json` and then +update this file to reflect your current development API credentials. ## Samples by API These samples are categorized by API service collection. From f4d54975ad2df2a067a5b40b89040ba87db6db06 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:16:29 -0400 Subject: [PATCH 10/25] Documentation updates --- samples/README.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/samples/README.md b/samples/README.md index b19fc550..406e08b1 100644 --- a/samples/README.md +++ b/samples/README.md @@ -6,7 +6,13 @@ These examples are provided as a quick start for your project. + [Authentication for Examples](#authentication-for-these-examples) -+ [Samples by API](#samples-by-api) ++ [Samples by API service collection](#samples-by-api-service-collection) + - [Detections](#detections) + - [Event Streams](#event-streams) + - [Falcon Discover](#falcon-discover) + - [Hosts](#hosts) + - [Real Time Response](#real-time-response) + - [Sample Uploads](#sample-uploads) + [Suggestions](#suggestions) ## Authentication for these Examples @@ -17,10 +23,25 @@ This is not the only method of providing these values, and not recommended for p In order to test these samples locally in your development environment, rename the file `config_sample.json` to `config.json` and then update this file to reflect your current development API credentials. -## Samples by API -These samples are categorized by API service collection. +## Samples by API service collection +These samples are categorized by API service collection. The list below will grow as more samples are planned. -### Sample Uploads API +### Detections +_Coming Soon_ + +### Event Streams +_Coming Soon_ + +### Falcon Discover +_Coming Soon_ + +### Hosts +_Coming Soon_ + +### Real Time Response +_Coming Soon_ + +### Sample Uploads + [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads.py) ## Suggestions From d0dd5e7962936e6337b0537c3f842bdf0e97e2f5 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:17:48 -0400 Subject: [PATCH 11/25] Documentation updates --- samples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 406e08b1..97b09ccc 100644 --- a/samples/README.md +++ b/samples/README.md @@ -47,4 +47,4 @@ _Coming Soon_ ## Suggestions Got a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). -Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](https://github.com/CrowdStrike/falconpy/CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file +Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](../CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file From 19ac67cfc8b32ac06f4646c1b7fe3a88baa6c2a2 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:19:05 -0400 Subject: [PATCH 12/25] Documentation updates --- samples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 97b09ccc..7de486c9 100644 --- a/samples/README.md +++ b/samples/README.md @@ -47,4 +47,4 @@ _Coming Soon_ ## Suggestions Got a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). -Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](../CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file +Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file From 086e476276bf126e993f881b9d663c0c4ac68526 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:20:06 -0400 Subject: [PATCH 13/25] Documentation updates --- samples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 7de486c9..5e03516e 100644 --- a/samples/README.md +++ b/samples/README.md @@ -47,4 +47,4 @@ _Coming Soon_ ## Suggestions Got a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). -Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file +Have an example you've developed yourself that you'd like to share? **_Excellent!_** Please review our [contributing guidelines](/CONTRIBUTING.md) and then submit a pull request. \ No newline at end of file From 2af4478f913e46adadb3197c352a69d98282038a Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 14:20:52 -0400 Subject: [PATCH 14/25] Documentation updates --- samples/sample_uploads/sample_uploads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py index 2589b880..5e16a4da 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads.py @@ -88,4 +88,4 @@ # Delete the file from the API response = falcon.DeleteSampleV3(ids=sha) # Print the results of our delete command -print(json.dumps(response, indent=4)) \ No newline at end of file +print(json.dumps(response, indent=4)) From f750d7b0d55a7efc49ae244100f1d53f4e9428f8 Mon Sep 17 00:00:00 2001 From: Shane Shellenbarger Date: Fri, 2 Apr 2021 14:52:05 -0400 Subject: [PATCH 15/25] Uploading simple example of containing and uncontaining a host via API --- .../real_time_response/quarantine_hosts.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 samples/real_time_response/quarantine_hosts.py diff --git a/samples/real_time_response/quarantine_hosts.py b/samples/real_time_response/quarantine_hosts.py new file mode 100644 index 00000000..3aca4a55 --- /dev/null +++ b/samples/real_time_response/quarantine_hosts.py @@ -0,0 +1,79 @@ +import json +import argparse +from pprint import pprint +from falconpy import oauth2 as FalconAuth +from falconpy import hosts as FalconHosts + +parser = argparse.ArgumentParser( + "Script that leverages Falcon API to (un)contain hosts") +parser.add_argument('-c', '--creds_file', dest='creds_file', + help='Path to creds json file', required=True) +parser.add_argument('-H', '--hostname', dest='hostname', + help='Hostname to quarantine', required=True) +parser.add_argument('-l', '--lift', dest='lift_containment', + action="store_true", help='Lift containment', + default=False) + +args = parser.parse_args() + +hostname = args.hostname +if args.lift_containment: + action = "lift_containment" +else: + action = "contain" + +creds_file = args.creds_file +with open(creds_file) as f: + creds = json.load(f) + +authorization = FalconAuth.OAuth2(creds={ + "client_id": creds['falcon_client_id'], + "client_secret": creds['falcon_client_secret'] + }) + +try: + token = authorization.token()['body']['access_token'] +except Exception as e: + print("Failed to authenticate") + print(e) + exit(-1) + +if token: + falcon = FalconHosts.Hosts(access_token=token) + + PARAMS = { + 'offset': 0, + 'limit': 10, + 'filter': f"hostname:'{hostname}'" + } + + response = falcon.QueryDevicesByFilter(parameters=PARAMS) + + contain_ids = response['body']['resources'] + print() + pprint(response) + if not contain_ids: + print(f"[-] Could not find hostname: {hostname} - Please verify \ + proper case") + exit(-2) + + PARAMS = { + 'action_name': action + } + + BODY = { + 'ids': contain_ids + } + + if action == "contain": + print(f"[+] Containing: {hostname}") + else: + print(f"[+] Lifting Containment: {hostname}") + + print() + + # TODO: Get rid of action_name="contain" once bug is resolved + # BUG: https://github.com/CrowdStrike/falconpy/issues/114 + response = falcon.PerformActionV2(parameters=PARAMS, body=BODY, + action_name="contain") + pprint(response) From bbd9017e657e73d45b5e7e3971c9b8324dadf954 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 15:29:21 -0400 Subject: [PATCH 16/25] Documentation updates --- samples/README.md | 2 +- samples/real_time_response/quarantine_hosts.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 5e03516e..1780aad6 100644 --- a/samples/README.md +++ b/samples/README.md @@ -39,7 +39,7 @@ _Coming Soon_ _Coming Soon_ ### Real Time Response -_Coming Soon_ ++ [Quarantine a host](real_time_response/quarantine_hosts.py) ### Sample Uploads + [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads.py) diff --git a/samples/real_time_response/quarantine_hosts.py b/samples/real_time_response/quarantine_hosts.py index 3aca4a55..32c7c992 100644 --- a/samples/real_time_response/quarantine_hosts.py +++ b/samples/real_time_response/quarantine_hosts.py @@ -1,3 +1,13 @@ +# ____ _ _____ _ ____ +# | _ \ ___ __ _| | |_ _(_)_ __ ___ ___ | _ \ ___ ___ _ __ ___ _ __ ___ ___ +# | |_) / _ \/ _` | | | | | | '_ ` _ \ / _ \ | |_) / _ \/ __| '_ \ / _ \| '_ \/ __|/ _ \ +# | _ < __/ (_| | | | | | | | | | | | __/ | _ < __/\__ \ |_) | (_) | | | \__ \ __/ +# |_| \_\___|\__,_|_| |_| |_|_| |_| |_|\___| |_| \_\___||___/ .__/ \___/|_| |_|___/\___| +# |_| +# +# This example demonstrates how to apply or lift containment on a host using it's hostname. +# + import json import argparse from pprint import pprint From 72b237e02d1da917d4d1db1335504cdd6a4b726b Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 15:33:24 -0400 Subject: [PATCH 17/25] Documentation updates --- samples/real_time_response/quarantine_hosts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/real_time_response/quarantine_hosts.py b/samples/real_time_response/quarantine_hosts.py index 32c7c992..674df174 100644 --- a/samples/real_time_response/quarantine_hosts.py +++ b/samples/real_time_response/quarantine_hosts.py @@ -5,7 +5,7 @@ # |_| \_\___|\__,_|_| |_| |_|_| |_| |_|\___| |_| \_\___||___/ .__/ \___/|_| |_|___/\___| # |_| # -# This example demonstrates how to apply or lift containment on a host using it's hostname. +# This example demonstrates how to apply or lift containment on a host using its hostname. # import json From 5d44b68f2a20a8343a79a197a141b5f7b355eaa8 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Fri, 2 Apr 2021 15:47:32 -0400 Subject: [PATCH 18/25] Documentation updates --- samples/sample_uploads/sample_uploads.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py index 5e16a4da..b413570b 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads.py @@ -1,8 +1,8 @@ -# ____ _ _ _ _ _ _ ____ ___ -# / ___| __ _ _ __ ___ _ __ | | ___ | | | |_ __ | | ___ __ _ __| |___ / \ | _ \_ _| -# \___ \ / _` | '_ ` _ \| '_ \| |/ _ \ | | | | '_ \| |/ _ \ / _` |/ _` / __| / _ \ | |_) | | -# ___) | (_| | | | | | | |_) | | __/ | |_| | |_) | | (_) | (_| | (_| \__ \ / ___ \| __/| | -# |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ /_/ \_\_| |___| +# ____ _ _ _ _ _ +# / ___| __ _ _ __ ___ _ __ | | ___ | | | |_ __ | | ___ __ _ __| |___ +# \___ \ / _` | '_ ` _ \| '_ \| |/ _ \ | | | | '_ \| |/ _ \ / _` |/ _` / __| +# ___) | (_| | | | | | | |_) | | __/ | |_| | |_) | | (_) | (_| | (_| \__ \ +# |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ # |_| |_| # # @@ -58,7 +58,7 @@ # |____/ \___|_| \_/ |_|\___\___| \____|_|\__,_|___/___/ # # This example shows how to interact with the -# Sample Uploads API using the Uber class. +# Sample Uploads API using the Sample Uploads Service class. from falconpy import sample_uploads as FalconUploads From aaa7412618a578fdfd09bae85937f25705a2d4e4 Mon Sep 17 00:00:00 2001 From: jshcodes <74007258+jshcodes@users.noreply.github.com> Date: Fri, 2 Apr 2021 21:24:06 -0400 Subject: [PATCH 19/25] Update labeler.yml Added code samples label tagging --- .github/labeler.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 81b27792..02da9c06 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,5 +1,8 @@ documentation: -- any: ['docs/*', '*.md'] +- docs/* +- *.md +- src/falconpy/*.md +- samples/*.md package: - src/*.py @@ -13,3 +16,8 @@ pipeline: unit testing: - any: ['tests/*', 'util/*'] + +code samples: +- samples/*.py +- samples/real_time_response/*.py +- samples/sample_uploads/*.py From d9b8f02f8cf1350606f9d55e23e7038342ca1e30 Mon Sep 17 00:00:00 2001 From: jshcodes <74007258+jshcodes@users.noreply.github.com> Date: Fri, 2 Apr 2021 21:28:49 -0400 Subject: [PATCH 20/25] Update labeler.yml --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 02da9c06..91b59873 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,6 +1,6 @@ documentation: - docs/* -- *.md +- '*.md' - src/falconpy/*.md - samples/*.md From 660ffa04130cc514b113da74658c980a79328074 Mon Sep 17 00:00:00 2001 From: jshcodes <74007258+jshcodes@users.noreply.github.com> Date: Fri, 2 Apr 2021 22:07:31 -0400 Subject: [PATCH 21/25] Update wordlist.txt --- .github/wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index df98fac6..c14c785d 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -19,6 +19,7 @@ autogenerated pytest ipython dev +config cov FalconDebug Uber From ea336d2ef72565fd858a20485bc219f60b4d0eb1 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sat, 3 Apr 2021 01:04:55 -0400 Subject: [PATCH 22/25] Linting --- .../real_time_response/quarantine_hosts.py | 64 ++++++++++--------- samples/sample_uploads/sample_uploads.py | 4 +- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/samples/real_time_response/quarantine_hosts.py b/samples/real_time_response/quarantine_hosts.py index 674df174..f9e5a69a 100644 --- a/samples/real_time_response/quarantine_hosts.py +++ b/samples/real_time_response/quarantine_hosts.py @@ -6,84 +6,86 @@ # |_| # # This example demonstrates how to apply or lift containment on a host using its hostname. +# This solution makes use of Service Class legacy authentication. # - import json import argparse -from pprint import pprint +# Import necessary FalconPy classes from falconpy import oauth2 as FalconAuth from falconpy import hosts as FalconHosts -parser = argparse.ArgumentParser( - "Script that leverages Falcon API to (un)contain hosts") -parser.add_argument('-c', '--creds_file', dest='creds_file', - help='Path to creds json file', required=True) -parser.add_argument('-H', '--hostname', dest='hostname', - help='Hostname to quarantine', required=True) -parser.add_argument('-l', '--lift', dest='lift_containment', - action="store_true", help='Lift containment', - default=False) - +# Setup our argument parser +parser = argparse.ArgumentParser("Script that leverages Falcon API to (un)contain hosts") +parser.add_argument('-c', '--creds_file', dest='creds_file', help='Path to creds json file', required=True) +parser.add_argument('-H', '--hostname', dest='hostname', help='Hostname to quarantine', required=True) +parser.add_argument('-l', '--lift', dest='lift_containment', action="store_true", help='Lift containment', default=False) +# Parse our ingested arguments args = parser.parse_args() - +# Hostname of the machine to contain / release hostname = args.hostname +# Default action is to quarantine if args.lift_containment: action = "lift_containment" else: action = "contain" - +# Use the credentials file provided creds_file = args.creds_file +# Load the contents of the creds file into the creds dictionary with open(creds_file) as f: creds = json.load(f) - +# Create an instance of our OAuth2 authorization class using our ingested creds authorization = FalconAuth.OAuth2(creds={ "client_id": creds['falcon_client_id'], "client_secret": creds['falcon_client_secret'] }) - +# Try to generate a token try: token = authorization.token()['body']['access_token'] except Exception as e: + # Exit out on authentication errors print("Failed to authenticate") print(e) exit(-1) - +# If we have a token, proceed to the next step if token: + # Create an instance of the Hosts class falcon = FalconHosts.Hosts(access_token=token) - + # Create our parameter payload, using our ingested hostname as a filter PARAMS = { 'offset': 0, 'limit': 10, 'filter': f"hostname:'{hostname}'" } - + # Query the Hosts API for hosts that match our filter pattern response = falcon.QueryDevicesByFilter(parameters=PARAMS) - + # Retrieve the list of IDs returned contain_ids = response['body']['resources'] - print() - pprint(response) + # Output the result + print(json.dumps(response, indent=4)) + if not contain_ids: - print(f"[-] Could not find hostname: {hostname} - Please verify \ - proper case") + # No hosts were found, exit out + print(f"[-] Could not find hostname: {hostname} - Please verify proper case") exit(-2) + # Create our next payload based upon the action requested PARAMS = { 'action_name': action } - + # Our body payload will contain our list of IDs BODY = { 'ids': contain_ids } - + # Provide a status update to the terminal if action == "contain": - print(f"[+] Containing: {hostname}") + print(f"\n[+] Containing: {hostname}\n") else: - print(f"[+] Lifting Containment: {hostname}") - - print() + print(f"\n[+] Lifting Containment: {hostname}\n") + # Perform the requested action # TODO: Get rid of action_name="contain" once bug is resolved # BUG: https://github.com/CrowdStrike/falconpy/issues/114 response = falcon.PerformActionV2(parameters=PARAMS, body=BODY, action_name="contain") - pprint(response) + # Output the result + print(json.dumps(response, indent=4)) diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads.py index b413570b..e9c2b097 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads.py @@ -17,7 +17,7 @@ # | |_| | |_) | __/ | | |___| | (_| \__ \__ \ # \___/|_.__/ \___|_| \____|_|\__,_|___/___/ # -# This example shows how to interact with the +# This example shows how to interact with the # Sample Uploads API using the Uber class. from falconpy import api_complete as FalconSDK @@ -57,7 +57,7 @@ # ___) | __/ | \ V /| | (_| __/ | |___| | (_| \__ \__ \ # |____/ \___|_| \_/ |_|\___\___| \____|_|\__,_|___/___/ # -# This example shows how to interact with the +# This example shows how to interact with the # Sample Uploads API using the Sample Uploads Service class. from falconpy import sample_uploads as FalconUploads From 6149b510b0265fd196dea3eb76bafa18a9fd02b3 Mon Sep 17 00:00:00 2001 From: jshcodes <74007258+jshcodes@users.noreply.github.com> Date: Sat, 3 Apr 2021 01:09:08 -0400 Subject: [PATCH 23/25] Update linting.yml --- .github/workflows/linting.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 86e02834..f3b879ae 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -23,10 +23,16 @@ jobs: python -m pip install --upgrade pip python -m pip install flake8 pip install -r requirements.txt - - name: Lint with flake8 + - name: Lint package source with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 src/falconpy --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # Stop the build on all linting errors - 04.02.21 / jshcodes@CrowdStrike flake8 src/falconpy --count --max-complexity=15 --max-line-length=127 --statistics + - name: Lint samples with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 samples --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 samples --exit-zero --count --max-complexity=15 --max-line-length=127 --statistics From 301dc98405d1489b0bb7e6d946f6c6b4dacf5861 Mon Sep 17 00:00:00 2001 From: Joshua Hiller Date: Sat, 3 Apr 2021 01:14:23 -0400 Subject: [PATCH 24/25] Sample Uploads sample adjustments --- samples/README.md | 3 +- ...e_uploads.py => sample_uploads_service.py} | 55 ++----------------- samples/sample_uploads/sample_uploads_uber.py | 47 ++++++++++++++++ 3 files changed, 55 insertions(+), 50 deletions(-) rename samples/sample_uploads/{sample_uploads.py => sample_uploads_service.py} (53%) create mode 100644 samples/sample_uploads/sample_uploads_uber.py diff --git a/samples/README.md b/samples/README.md index 1780aad6..c58534d5 100644 --- a/samples/README.md +++ b/samples/README.md @@ -42,7 +42,8 @@ _Coming Soon_ + [Quarantine a host](real_time_response/quarantine_hosts.py) ### Sample Uploads -+ [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads.py) ++ [Upload, Retrieve and then Delete a file (Service Class)](sample_uploads/sample_uploads_service.py) ++ [Upload, Retrieve and then Delete a file (Uber Class)](sample_uploads/sample_uploads_uber.py) ## Suggestions Got a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions). diff --git a/samples/sample_uploads/sample_uploads.py b/samples/sample_uploads/sample_uploads_service.py similarity index 53% rename from samples/sample_uploads/sample_uploads.py rename to samples/sample_uploads/sample_uploads_service.py index e9c2b097..134d38fe 100644 --- a/samples/sample_uploads/sample_uploads.py +++ b/samples/sample_uploads/sample_uploads_service.py @@ -5,61 +5,18 @@ # |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ # |_| |_| # -# -# These examples show how to interact with the Sample Uploads API using -# both the Uber class, and the regular Service class. -# -import json - -# _ _ _ ____ _ -# | | | | |__ ___ _ __ / ___| | __ _ ___ ___ -# | | | | '_ \ / _ \ '__| | | | |/ _` / __/ __| -# | |_| | |_) | __/ | | |___| | (_| \__ \__ \ -# \___/|_.__/ \___|_| \____|_|\__,_|___/___/ -# -# This example shows how to interact with the -# Sample Uploads API using the Uber class. - -from falconpy import api_complete as FalconSDK - -# Grab our config parameters -with open('../config.json', 'r') as file_config: - config = json.loads(file_config.read()) - -# Create an instance of the Uber class -falcon = FalconSDK.APIHarness(creds={ - "client_id": config["falcon_client_id"], - "client_secret": config["falcon_client_secret"] - } -) - -# Define our file -FILENAME = "testfile.jpg" -# Open the file for binary read, this will be our payload -PAYLOAD = open(FILENAME, 'rb').read() -# Upload the file using the Sample Uploads API, name this file "newfile.jpg" in the API -response = falcon.command('UploadSampleV3', file_name="newfile.jpg", data=PAYLOAD, content_type="application/octet-stream") -# Grab the SHA256 unique identifier for the file we just uploaded -sha = response["body"]["resources"][0]["sha256"] -# Download a copy of this file, use the SHA256 ID to retrieve it -response = falcon.command("GetSampleV3", ids=sha) -# Save the result to a new file -open('uberclass.jpg', 'wb').write(response) -# Delete the file from the API -response = falcon.command("DeleteSampleV3", ids=sha) -# Print the results of our delete command -print(json.dumps(response, indent=4)) - - # ____ _ ____ _ # / ___| ___ _ ____ _(_) ___ ___ / ___| | __ _ ___ ___ # \___ \ / _ \ '__\ \ / / |/ __/ _ \ | | | |/ _` / __/ __| # ___) | __/ | \ V /| | (_| __/ | |___| | (_| \__ \__ \ # |____/ \___|_| \_/ |_|\___\___| \____|_|\__,_|___/___/ # -# This example shows how to interact with the -# Sample Uploads API using the Sample Uploads Service class. - +# +# These examples show how to interact with the Sample Uploads API using the Service Class +# This example uses Credential authentication and supports token refresh / authentication free usage. +# +import json +# Import the Sample Uploads service class from falconpy import sample_uploads as FalconUploads # #Grab our config parameters diff --git a/samples/sample_uploads/sample_uploads_uber.py b/samples/sample_uploads/sample_uploads_uber.py new file mode 100644 index 00000000..794c7008 --- /dev/null +++ b/samples/sample_uploads/sample_uploads_uber.py @@ -0,0 +1,47 @@ +# ____ _ _ _ _ _ +# / ___| __ _ _ __ ___ _ __ | | ___ | | | |_ __ | | ___ __ _ __| |___ +# \___ \ / _` | '_ ` _ \| '_ \| |/ _ \ | | | | '_ \| |/ _ \ / _` |/ _` / __| +# ___) | (_| | | | | | | |_) | | __/ | |_| | |_) | | (_) | (_| | (_| \__ \ +# |____/ \__,_|_| |_| |_| .__/|_|\___| \___/| .__/|_|\___/ \__,_|\__,_|___/ +# |_| |_| +# +# +# _ _ _ ____ _ +# | | | | |__ ___ _ __ / ___| | __ _ ___ ___ +# | | | | '_ \ / _ \ '__| | | | |/ _` / __/ __| +# | |_| | |_) | __/ | | |___| | (_| \__ \__ \ +# \___/|_.__/ \___|_| \____|_|\__,_|___/___/ +# +# These examples show how to interact with the Sample Uploads API using the Uber class. +# +import json +# Import the Uber Class +from falconpy import api_complete as FalconSDK + +# Grab our config parameters +with open('../config.json', 'r') as file_config: + config = json.loads(file_config.read()) + +# Create an instance of the Uber class +falcon = FalconSDK.APIHarness(creds={ + "client_id": config["falcon_client_id"], + "client_secret": config["falcon_client_secret"] + } +) + +# Define our file +FILENAME = "testfile.jpg" +# Open the file for binary read, this will be our payload +PAYLOAD = open(FILENAME, 'rb').read() +# Upload the file using the Sample Uploads API, name this file "newfile.jpg" in the API +response = falcon.command('UploadSampleV3', file_name="newfile.jpg", data=PAYLOAD, content_type="application/octet-stream") +# Grab the SHA256 unique identifier for the file we just uploaded +sha = response["body"]["resources"][0]["sha256"] +# Download a copy of this file, use the SHA256 ID to retrieve it +response = falcon.command("GetSampleV3", ids=sha) +# Save the result to a new file +open('uberclass.jpg', 'wb').write(response) +# Delete the file from the API +response = falcon.command("DeleteSampleV3", ids=sha) +# Print the results of our delete command +print(json.dumps(response, indent=4)) From 87d206ffa51c4afe5ccded0c9c488b949f81f105 Mon Sep 17 00:00:00 2001 From: jshcodes <74007258+jshcodes@users.noreply.github.com> Date: Sat, 3 Apr 2021 01:18:52 -0400 Subject: [PATCH 25/25] Update bandit.yml --- .github/workflows/bandit.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml index ab16033f..6ee7989a 100644 --- a/.github/workflows/bandit.yml +++ b/.github/workflows/bandit.yml @@ -23,6 +23,9 @@ jobs: python -m pip install --upgrade pip python -m pip install bandit pip install -r requirements.txt - - name: Analyze with bandit + - name: Analyze package with bandit run: | bandit -r src + - name: Analyze samples with bandit + run: | + bandit -r samples