Skip to content

Commit

Permalink
Merge pull request #12 from CrowdStrike/feature/policy-support
Browse files Browse the repository at this point in the history
 Policy Describe/Import/Export
  • Loading branch information
ChristopherHammond13 authored Feb 13, 2023
2 parents 6a84480 + 9aaa2a2 commit 177675a
Show file tree
Hide file tree
Showing 9 changed files with 679 additions and 138 deletions.
67 changes: 57 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ The toolkit provides:
- Multiple profile support, including support for MSSP / Falcon Flight Control configurations.
- A shell allowing you to interface with many hosts via RTR at once, and get the output via CSV.
- Scriptability! You can program the shell by providing pre-written routines via a file on disk, and a full Python extensibility API is provided.
- More functionality is coming soon! Already on the roadmap are Policy import/export and IOA import/export. Want more functionality? Open an [Issue](https://github.com/CrowdStrike/Falcon-Toolkit/issues/new)!
- Prevention policy import and export
- Response policy import and export
- More functionality is coming soon! Want more functionality? Open an [Issue](https://github.com/CrowdStrike/Falcon-Toolkit/issues/new)!

Since this is built on top of Caracara, you get a bunch of great functionality and flexibility free, including the ability to filter hosts using dynamically generated FQL queries, full debug logging where desired, Falcon Flight Control integration, and more! Plus, the tool is lightning quick as it leverages Caracara's parallelisation tricks to pull more information quickly.

Expand Down Expand Up @@ -180,13 +182,17 @@ Two types of configuration backends are provided out of the box: the default, wh

Your API keys should have the following scopes enabled in the Falcon dashboard:

| ↓ API Scopes // Commands → | `host_search` | `shell` |
|--------------------------------------|:-------------:|:-------:|
| **Falcon Flight Control: Read** | X<br>*When using parent<br>CID API Keys* | X<br>*When using parent<br>CID API Keys* |
| **Hosts: Read** | X | X |
| **Real Time Response: Read** | | X |
| **Real Time Response: Write** | | X |
| **Real Time Response: Admin** | | X<br>*for admin commands* |
| &darr; API Scopes // Commands &rarr; | `host_search` | `shell` | `policies`<br>(Prevention) | `policies`<br>(Response) |
|--------------------------------------|:-------------:|:-------:|:--------------------------:|:-------------------------:|
| **Falcon Flight Control: Read** | X<br>*When using parent<br>CID API Keys* | X<br>*When using parent<br>CID API Keys* | X<br>*When using parent<br>CID API Keys* | X<br>*When using parent<br>CID API Keys* |
| **Hosts: Read** | X | X | | |
| **Prevention Policies: Read** | | | X<br>`describe` / `export` sub-commands | |
| **Prevention Policies: Write** | | | X<br>`import` sub-command | |
| **Real Time Response: Read** | | X | | |
| **Real Time Response: Write** | | X | | |
| **Real Time Response: Admin** | | X<br>*for admin commands* | | |
| **Response Policies: Read** | | | | X<br>`describe` / `export` sub-commands |
| **Response Policies: Write** | | | | X<br>`import` sub-command |

### Showing Your Profiles

Expand Down Expand Up @@ -289,7 +295,7 @@ falcon -p ProfileName shell -d abcdef12345,ghijkl67890

Sometimes it is not practical to provide a list of Device IDs at the command line, often because the length of the string containing all the IDs would exceed the maximum command length allowable within your shell. To get around this, Falcon Toolkit provides another parameter (`--device-id-file` / `-df`), which allows you to provide a path to a file containing a list of AIDs, one per line. For example, let's say you wanted to connect to two devices with the AIDs `abcdef12345` and `ghijkl67890`, you may have a file named `device_ids.txt` with the following contents:

```
```text
abcdef12345
ghijkl67890
```
Expand All @@ -300,7 +306,6 @@ Then, you could jump into a shell with these devices via this Falcon Toolkit com
falcon -p ProfileName shell -df device_ids.txt
```


### Real Time Response (RTR) Scripting

The RTR shell is fully scriptable. There are two different scripting methods supported:
Expand Down Expand Up @@ -350,6 +355,48 @@ Some example usages of this functionality are as follows:

</details>

## Policy Manipulation

You can `describe`, `import` and `export` two types of policies: Prevention and Response. The three verbs are applied to the `falcon policies` command to specify what you would like to do with policies, and a command line switch is used to specify the policy type to work with. Exported policies are written to disk as JSON with some Falcon Toolkit-specific data needed to import a policy back again.

### Examples

Show all Prevention policies within the `MyCompany` Falcon profile:

```shell
$ falcon -p MyCompany policies -p describe
platform_default (Platform: Windows)
Platform default policy
...
```

Show all Response policies when only one profile is configured:

```shell
$ falcon policies -r describe
platform_default (Platform: Windows)
Platform default policy
...
```

Export a Response policy from the `MyCompany` tenant to disk:

```shell
$ falcon -p MyCompany policies -r export
Please choose a policy to export

* My Response Policy [Windows]
My Other Response Policy [Linux]
...
```

Import a Prevention policy to the one configured Falcon tenant:

```shell
$ falcon policies -p import MyExportedPolicy.json
...
```

## Support & Community Forums

Falcon Toolkit is an open source project, and not a formal CrowdStrike product, designed to assist users with managing their Falcon tenants and executing commands at scale. As such, it carries no formal support, express or implied. This project originated out of the CrowdStrike Services Incident Response (IR) team's need to execute commands across Falcon tenants quickly, at scale, and with auditing, and is maintained by [Chris Hammond](mailto:[email protected]).
Expand Down
4 changes: 3 additions & 1 deletion falcon_toolkit/falcon.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from falcon_toolkit.common.logging_config import configure_logger
from falcon_toolkit.common.utils import configure_data_dir
from falcon_toolkit.hosts.cli import cli_host_search
from falcon_toolkit.policies.cli import cli_policies
from falcon_toolkit.shell.cli import cli_shell


Expand Down Expand Up @@ -222,6 +223,7 @@ def cli_list_filters():


# Load all commands into the main cli object, ready for use as root falcon commands
cli.add_command(cli_shell)
cli.add_command(cli_host_search)
cli.add_command(cli_list_filters)
cli.add_command(cli_policies)
cli.add_command(cli_shell)
4 changes: 4 additions & 0 deletions falcon_toolkit/policies/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Falcon Toolkit: Policy Management.
This sub-module contains the implementation of the falcon policies commands.
"""
162 changes: 162 additions & 0 deletions falcon_toolkit/policies/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""Falcon Toolkit: Policy Management.
This file contains the command line interface for the policies commands. The implementation
of the logic itself is contained in other files, including policies.py
"""
import os

from typing import List

import click
import pick

from caracara import Client
from caracara.common.policy_wrapper import Policy

from click_option_group import (
optgroup,
RequiredMutuallyExclusiveOptionGroup,
)

from falcon_toolkit.common.cli import get_instance
from falcon_toolkit.policies.constants import PoliciesAPIModule
from falcon_toolkit.policies.describe import pretty_print_policies
from falcon_toolkit.policies.container import PolicyContainer


@click.group(
name='policies',
help='Manage Falcon Prevention and Response policies',
)
@click.pass_context
@optgroup.group(
"Policy Type",
cls=RequiredMutuallyExclusiveOptionGroup,
help="Choose whether to interface with [-p]revention or [-r]esponse policies",
)
@optgroup.option(
'-p',
'--prevention',
'prevention_policies_option',
is_flag=True,
help="Interface with Prevention policies",
)
@optgroup.option(
'-r',
'--response',
'response_policies_option',
is_flag=True,
help="Interface with Response policies",
)
def cli_policies(
ctx: click.Context,
prevention_policies_option: bool,
response_policies_option: bool,
):
"""Configure the future profiles commands by getting the context in shape."""
instance = get_instance(ctx)
client: Client = instance.auth_backend.authenticate()
ctx.obj['client'] = client

if prevention_policies_option:
ctx.obj['policies_api'] = client.prevention_policies
ctx.obj['policies_type'] = "prevention"
elif response_policies_option:
ctx.obj['policies_api'] = client.response_policies
ctx.obj['policies_type'] = "response"
else:
raise ValueError("Impossible scenario: no policy type specified")


@click.command(
name='describe',
help='List and describe the policies within the Falcon tenant.'
)
@click.pass_context
def policies_describe(ctx: click.Context):
"""List and describe the Prevention or Response policies within the Falcon tenant."""
policies_api: PoliciesAPIModule = ctx.obj['policies_api']
policies_type: str = ctx.obj['policies_type']
click.echo(click.style(f"Describing all {policies_type} policies", fg='green', bold=True))
policies = policies_api.describe_policies()
pretty_print_policies(policies)


@click.command(
name='export',
help='Export a Prevention or Response policy to disk.',
)
@click.pass_context
def policies_export(ctx: click.Context):
"""Allow a user to choose a Prevention or Response policy to export to disk."""
# pylint: disable=too-many-locals
policies_api: PoliciesAPIModule = ctx.obj['policies_api']
policies_type: str = ctx.obj['policies_type']
click.echo("Loading policies...")
policies = policies_api.describe_policies()

options: List[pick.Option] = []
for policy in policies:
option_text = f"{policy.name} [{policy.platform_name}]"
option = pick.Option(label=option_text, value=policy)
options.append(option)

chosen_option, _ = pick.pick(options, "Please choose a policy to export")
chosen_policy: Policy = chosen_option.value
default_filename = f"{chosen_policy.name}.json"
reasonable_filename = False
while not reasonable_filename:
filename: str = click.prompt("Policy filename", type=str, default=default_filename)
if not filename.endswith(".json"):
click.echo(click.style("Filename must end in .json", fg='yellow'))
continue

if os.path.exists(filename):
click.echo(click.style("File already exists!", fg='yellow'))
continue

reasonable_filename = True

policy_container = PolicyContainer(
policy=chosen_policy,
policy_type=policies_type,
)

with open(filename, 'wt', encoding='utf-8') as export_file_handle:
export_file_handle.write(policy_container.dumps())

click.echo("Export complete")


@click.command(
name='import',
help='Import a Prevention or Response policy from disk.',
)
@click.pass_context
@click.argument(
'filename',
type=click.STRING,
)
def policies_import(
ctx: click.Context,
filename: str,
):
"""Import a Prevention or Response policy from the JSON file named FILENAME."""
policies_api: PoliciesAPIModule = ctx.obj['policies_api']

click.echo(f"Loading policy in the file: {filename}")

with open(filename, 'rt', encoding='utf-8') as policy_file_handle:
policy_str = str(policy_file_handle.read())

policy_container = PolicyContainer.loads(policy_str)

click.echo(f"Uploading the {policy_container.policy.name} policy to Falcon")
policies_api.push_policy(policy_container.policy)

click.echo("Done!")


cli_policies.add_command(policies_describe)
cli_policies.add_command(policies_export)
cli_policies.add_command(policies_import)
35 changes: 35 additions & 0 deletions falcon_toolkit/policies/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Falcon Toolkit: Policy Management.
This file contains constants that are required to properly display and manage Prevention and
Response policies within a Falcon tenant.
"""
from typing import Union

from caracara.modules.prevention_policies import PreventionPoliciesApiModule
from caracara.modules.response_policies import ResponsePoliciesApiModule
from colorama import Fore


ASCII_OFF_BUTTON = (
f"{Fore.RED}-----------{Fore.RESET}\n"
f"{Fore.RED}| OFF |{Fore.RESET}\n"
f"{Fore.RED}-----------{Fore.RESET}\n"
)
ASCII_ON_BUTTON = (
f"{Fore.GREEN}-----------{Fore.RESET}\n"
f"{Fore.GREEN}| ON |{Fore.RESET}\n"
f"{Fore.GREEN}-----------{Fore.RESET}\n"
)

# List of policy types as strings for a quick reference
POLICY_TYPES = [
"prevention",
"response",
]

# Union type of all types of all Caracara modules providing policy data
# They all expose a common API, so can be assumed to be one type where convenient
PoliciesAPIModule = Union[
PreventionPoliciesApiModule,
ResponsePoliciesApiModule,
]
Loading

0 comments on commit 177675a

Please sign in to comment.