diff --git a/docs/administration/enterprise.md b/docs/administration/enterprise.md index 49d7fe2c..0d5c10b4 100644 --- a/docs/administration/enterprise.md +++ b/docs/administration/enterprise.md @@ -55,6 +55,13 @@ according to the [OpenAEV architecture](../deployment/platform/overview.md#archi The SentinelOne Agent can be leveraged to execute implants as detached processes that will then execute payloads according to the [OpenAEV architecture](../deployment/platform/overview.md#architecture) +### Palo Alto Cortex Agent + +The Palo Alto Cortex Agent can be leveraged to execute implants as detached processes that will then execute payloads +according to the [OpenAEV architecture](../deployment/platform/overview.md#architecture). + +On Windows, because Palo Alto Cortex whitelists its own process tree, OpenAEV creates a scheduled task to detach the process that will execute the payloads. + ## Remediations in CVES More detail: [CVES](taxonomies.md) and [Findings view](../usage/findings.md) diff --git a/docs/deployment/assets/paloaltocortex-agents.png b/docs/deployment/assets/paloaltocortex-agents.png new file mode 100644 index 00000000..902c27bc Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-agents.png differ diff --git a/docs/deployment/assets/paloaltocortex-endpoints.png b/docs/deployment/assets/paloaltocortex-endpoints.png new file mode 100644 index 00000000..192b7c84 Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-endpoints.png differ diff --git a/docs/deployment/assets/paloaltocortex-scripts.png b/docs/deployment/assets/paloaltocortex-scripts.png new file mode 100644 index 00000000..e93128a0 Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-scripts.png differ diff --git a/docs/deployment/assets/paloaltocortex-unix-script-general.png b/docs/deployment/assets/paloaltocortex-unix-script-general.png new file mode 100644 index 00000000..4fd51e6a Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-unix-script-general.png differ diff --git a/docs/deployment/assets/paloaltocortex-unix-script-inputs-outputs.png b/docs/deployment/assets/paloaltocortex-unix-script-inputs-outputs.png new file mode 100644 index 00000000..322f6858 Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-unix-script-inputs-outputs.png differ diff --git a/docs/deployment/assets/paloaltocortex-unix-script.png b/docs/deployment/assets/paloaltocortex-unix-script.png new file mode 100644 index 00000000..214ed411 Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-unix-script.png differ diff --git a/docs/deployment/assets/paloaltocortex-windows-script-general.png b/docs/deployment/assets/paloaltocortex-windows-script-general.png new file mode 100644 index 00000000..12fb73e8 Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-windows-script-general.png differ diff --git a/docs/deployment/assets/paloaltocortex-windows-script-inputs-outputs.png b/docs/deployment/assets/paloaltocortex-windows-script-inputs-outputs.png new file mode 100644 index 00000000..b8f85444 Binary files /dev/null and b/docs/deployment/assets/paloaltocortex-windows-script-inputs-outputs.png differ diff --git a/docs/deployment/assets/paloaltocortex_subprocessor_unix.py b/docs/deployment/assets/paloaltocortex_subprocessor_unix.py new file mode 100644 index 00000000..09c0d3a6 --- /dev/null +++ b/docs/deployment/assets/paloaltocortex_subprocessor_unix.py @@ -0,0 +1,8 @@ +import os + +def run(command): + exit_code = os.system("echo " + command + " | base64 -d | sh") + print("Exit code:", exit_code) + +if __name__ == "__main__": + run(command) \ No newline at end of file diff --git a/docs/deployment/assets/paloaltocortex_subprocessor_windows.py b/docs/deployment/assets/paloaltocortex_subprocessor_windows.py new file mode 100644 index 00000000..a9343c2e --- /dev/null +++ b/docs/deployment/assets/paloaltocortex_subprocessor_windows.py @@ -0,0 +1,155 @@ +import os +import subprocess +import sys +import traceback +import ctypes +import re + +PtyProcess = None +try: + from winpty import PtyProcess +except: + PtyProcess = None + + +def ensure_console(): + """Ensure the current process has a valid console. + + When launched from Palo Alto's agent, the process may not have + a console attached, which causes child processes (especially cmd.exe) + to fail when they try to inherit console handles. + """ + kernel32 = ctypes.windll.kernel32 + if not kernel32.AttachConsole(-1): + kernel32.AllocConsole() + hwnd = kernel32.GetConsoleWindow() + if hwnd: + ctypes.windll.user32.ShowWindow(hwnd, 0) + + +def remove_control_characters(text): + """Remove all the control characters from a string""" + text = re.sub(r"\x1b\[\??(\d;?)*\w", '', text) + text = re.sub(r"\x1b\]0;.*\x07", '\r\n', text) + text = re.sub("\r\n>> ", '', text) + return text + + +def remove_leading_cmd(text): + """Removes leading "cmd" or "cmd.exe" from the text if present. + + :param text: a string + :type text: str + :rtype: str + """ + if text.startswith("cmd.exe"): + return text[7:] + elif text.startswith("cmd"): + return text[3:] + return text + + +def is_direct_executable(command): + """Check if the command starts with a known executable.""" + first_token = command.strip().split()[0].lower() + return first_token in [ + "powershell.exe", "powershell", + "cmd.exe", "cmd", + "pwsh.exe", "pwsh" + ] + + +def run(commands_list): + """Runs a list of commands via winpty, falling back to subprocess. + + :param commands_list: list of commands + :type commands_list: list + :return: commands output dict - {: } + :rtype: dict + """ + ensure_console() + + result = {} + for command in commands_list: + sys.stdout.write(f"Running command <{command[:100]}>...\n") + args = command + try: + env = os.environ.copy() + + if 'COMSPEC' not in env: + env['COMSPEC'] = os.path.expandvars("%SYSTEMROOT%\\System32\\cmd.exe") + if 'SystemRoot' not in env: + env['SystemRoot'] = os.environ.get('SystemRoot', 'C:\\Windows') + + if is_direct_executable(command): + # Direct executable call (e.g., powershell.exe -encodedCommand ...) + # Run without shell wrapping or winpty + with subprocess.Popen( + args=args, + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + ) as process: + stdout, stderr = process.communicate(timeout=300) + if stderr: + sys.stderr.write(f"stderr: \n{stderr.decode('utf-8', errors='replace')}\n") + if stdout: + sys.stdout.write(f"stdout: \n{stdout.decode('utf-8', errors='replace')}\n") + result[command] = stdout.decode('utf-8', errors='replace').splitlines() + else: + result[command] = None + elif PtyProcess: + command = remove_leading_cmd(command) + command_prompt_path = os.path.expandvars("%SYSTEMROOT%\\System32\\cmd.exe") + proc = PtyProcess.spawn(command_prompt_path, cwd="c:\\") + proc.write("cls\r\n") + proc.read() + proc.write(command + '\r\n') + proc.write('exit\r\n') + stdout = "" + while proc.isalive(): + buff = proc.read(32768) + stdout += buff + if proc.eof(): + break + sys.stdout.write(f"stdout: {stdout}") + stdout = remove_control_characters(stdout) + stdout = stdout.split(command, 1)[1] + stdout = stdout.split(":\\>exit", 1)[0][:-1] + lines = stdout.split("\r\n") + lines = [line.strip() for line in lines if line.strip()] + result[command] = lines + proc.terminate() + else: + sys.stderr.write( + "WARNING: winpty not available, falling back to subprocess\n" + ) + with subprocess.Popen( + args=args, + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + creationflags=subprocess.CREATE_NEW_CONSOLE, + ) as process: + stdout, stderr = process.communicate(timeout=300) + if stderr: + sys.stderr.write(f"stderr: \n{stderr.decode('utf-8', errors='replace')}\n") + if stdout: + sys.stdout.write(f"stdout: \n{stdout.decode('utf-8', errors='replace')}\n") + result[command] = stdout.decode('utf-8', errors='replace').splitlines() + else: + result[command] = None + except subprocess.TimeoutExpired: + sys.stderr.write(f"Command timed out: <{command[:100]}>...\n") + result[command] = None + except Exception: + sys.stderr.write(f"Failed open command: <{command[:100]}>, error: {traceback.format_exc()}") + + return result + + +if __name__ == '__main__': + results = run(commands_list) \ No newline at end of file diff --git a/docs/deployment/assets/openaev_subprocessor_unix.sh b/docs/deployment/assets/sentinelone_subprocessor_unix.sh similarity index 100% rename from docs/deployment/assets/openaev_subprocessor_unix.sh rename to docs/deployment/assets/sentinelone_subprocessor_unix.sh diff --git a/docs/deployment/assets/openaev_subprocessor_windows.ps1 b/docs/deployment/assets/sentinelone_subprocessor_windows.ps1 similarity index 100% rename from docs/deployment/assets/openaev_subprocessor_windows.ps1 rename to docs/deployment/assets/sentinelone_subprocessor_windows.ps1 diff --git a/docs/deployment/ecosystem/executors.md b/docs/deployment/ecosystem/executors.md index bb414485..2836e424 100644 --- a/docs/deployment/ecosystem/executors.md +++ b/docs/deployment/ecosystem/executors.md @@ -14,6 +14,7 @@ architectures. This table below summarizes the information about each agent. | **Tanium Agent** | Under license | As a system service | Executable | An admin background process | As a system admin | No, always the same agent | | **Crowdstrike Falcon Agent** | Under license | As a system service | Executable | An admin background process | As a system admin | No, always the same agent | | **SentinelOne Agent** | Under license | As a system service | Executable | An admin background process | As a system admin | No, always the same agent | +| **Palo Alto Cortex Agent** | Under license | As a system service | Executable | An admin background process | As a system admin | No, always the same agent | | **Caldera Agent** | Open source | As a user session | Script | An admin background process | As a user admin | Yes, depending on the user | ## OpenAEV Agent @@ -79,7 +80,7 @@ Once configured and imported, retrieve the package IDs from the URL: ### Configure the OpenAEV Platform -To use the Tanium executor, fill the following configuration: +To use the Tanium executor, fill the following configuration in the Integrations (Executors) tab from OpenAEV menu. | Parameter | Environment variable | Default value | Description | |:------------------------------------------------------|:------------------------------------------------------|:---------------|:--------------------------------------------------------------------------------------------------------------------------------------------------| @@ -257,7 +258,7 @@ applied. Please note that the CrowdStrike API key should have the following permissions: API integrations, Hosts, Host groups, Real time response. -To use the CrowdStrike executor, just fill the following configuration. +To use the CrowdStrike executor, just fill the following configuration in the Integrations (Executors) tab from OpenAEV menu. | Parameter | Environment variable | Default value | Description | |:-----------------------------------------------------------|:------------------------------------------------------------|:-----------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------| @@ -291,6 +292,99 @@ Endpoint on the OpenAEV endpoint page. You are now ready to leverage your CrowdStrike platform to run OpenAEV payloads! +--- + +## Palo Alto Cortex Agent + +The Palo Alto Cortex agent can be leveraged to execute implants as detached processes that will then execute payloads +according to the [OpenAEV architecture](https://docs.openaev.io/latest/deployment/overview). + +On Windows, because Palo Alto Cortex whitelists its own process tree, OpenAEV creates a scheduled task to detach the process that will execute the payloads. + +The implants will be downloaded to these folders on the different assets: + +* On Windows assets: `C:\Program Files (x86)\Filigran\OAEV Agent\runtimes\implant-XXXXX` +* On Linux or MacOS assets: `/opt/openaev-agent/runtimes/implant-XXXXX` + +where XXXXX will be a completely random UUID, generated for each inject that will be executed. +This ensures that the implants are unique and will be deleted on assets' restart. + +### Configure the Palo Alto Cortex Platform + +#### Upload OpenAEV scripts + +First of all, you need to create one custom script for Unix, covering both Linux and MacOS systems and another one for Windows. + +To create these scripts, go to `Investigation & responses` > `Action Center` > `Agent Script Library` > `+ New Script`. The names +of the scripts can be changed if necessary, the ids will be put in the OpenAEV configuration. +To get the scripts IDs, it may be necessary to add the Script UID column to the scripts list view. + +*Unix Script* + +Upload the following Python script: + +[Download](../assets/paloaltocortex_subprocessor_unix.py) + +Put the following Input schema: + +![Palo Alto Cortex unix script1](../assets/paloaltocortex-unix-script-general.png) +![Palo Alto Cortex unix script2](../assets/paloaltocortex-unix-script-inputs-outputs.png) + +*Windows script* + +Upload the following Python script: + +[Download](../assets/paloaltocortex_subprocessor_windows.py) + +Put the following Input schema: + +![Palo Alto Cortex windows script1](../assets/paloaltocortex-windows-script-general.png) +![Palo Alto Cortex windows script2](../assets/paloaltocortex-windows-script-inputs-outputs.png) + +#### Create a group with your targeted assets + +To create a group, go to `Inventory` > `Endpoints` > `Groups`. + +### Configure the OpenAEV platform + +!!! warning "Palo Alto Cortex API Key" + + Please note that the Palo Alto Cortex API key created in "Settings/Configurations/API Keys" should have the following minimum role: “Instance Administrator” and security level: "Standard". + +To use the Palo Alto Cortex executor, just fill the following configuration in the Integrations (Executors) tab from OpenAEV menu. + +| Parameter | Environment variable | Default value | Description | +|:--------------------------------------------------------------|:--------------------------------------------------------------|:--------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| executor.paloaltocortex.enable | EXECUTOR_PALOALTOCORTEX_ENABLE | `false` | Enable the Palo Alto Cortex executor | +| executor.paloaltocortex.url | EXECUTOR_PALOALTOCORTEX_URL | | Palo Alto Cortex URL, the API version used is the v1 | +| executor.paloaltocortex.api-register-interval | EXECUTOR_PALOALTOCORTEX_API_REGISTER_INTERVAL | 1200 | Palo Alto Cortex API interval to register/update the accounts/sites/groups/agents in OpenAEV (in seconds) | +| executor.paloaltocortex.api-batch-execution-action-pagination | EXECUTOR_PALOALTOCORTEX_API_BATCH_EXECUTION_ACTION_PAGINATION | 100 | Palo Alto Cortex API pagination per 5 seconds to set for endpoints batch executions (number of endpoints sent per 5 seconds to Palo Alto Cortex to execute a payload) | +| executor.paloaltocortex.clean-implant-interval | EXECUTOR_PALOALTOCORTEX_CLEAN_IMPLANT_INTERVAL | 8 | Palo Alto Cortex clean old implant interval (in hours) | +| executor.paloaltocortex.api-key-id | EXECUTOR_PALOALTOCORTEX_API_KEY_ID | | Palo Alto Cortex API key id | +| executor.paloaltocortex.api-key | EXECUTOR_PALOALTOCORTEX_API_KEY | | Palo Alto Cortex API key | +| executor.paloaltocortex.group-name | EXECUTOR_PALOALTOCORTEX_GROUP_ID | | Palo Alto Cortex group name or groups names separated with commas | +| executor.paloaltocortex.windows-script-uid | EXECUTOR_PALOALTOCORTEX_WINDOWS_SCRIPT_UID | | Uid of the OpenAEV Palo Alto Cortex Windows script | +| executor.paloaltocortex.unix-script-uid | EXECUTOR_PALOALTOCORTEX_UNIX_SCRIPT_UID | | Uid of the OpenAEV Palo Alto Cortex Unix script | + +### Checks + +Once enabled, you should see Palo Alto Cortex available in your `Install agents` section + +![Palo Alto Cortex available agent](../assets/paloaltocortex-agents.png) + +Also, the assets and the asset groups in the selected groups should now be available in the endpoints and asset +groups sections in OpenAEV: + +![Palo Alto Cortex Endpoints](../assets/paloaltocortex-endpoints.png) + +NB : An Asset can only have one Palo Alto Cortex agent installed due to the uniqueness of the MAC address parameters. If you +try to install again a Palo Alto Cortex agent on a platform, it will overwrite the actual one and you will always see one +Endpoint on the OpenAEV endpoint page. + +!!! success "Installation done" + + You are now ready to leverage your Palo Alto Cortex platform to run OpenAEV payloads! + --- ## SentinelOne Agent @@ -325,7 +419,7 @@ of the scripts can be changed if necessary, the ids will be put in the OpenAEV c Upload the following script (encoded for Unix): -[Download](../assets/openaev_subprocessor_unix.sh) +[Download](../assets/sentinelone_subprocessor_unix.sh) Put the following Input schema: @@ -336,7 +430,7 @@ Put the following Input schema: Upload the following script (encoded for Windows): -[Download](../assets/openaev_subprocessor_windows.ps1) +[Download](../assets/sentinelone_subprocessor_windows.ps1) Put the following Input schema: @@ -357,7 +451,7 @@ To create a wrapper (account/site/group), go to `Settings` > `Accounts/Sites`. Please note that the SentinelOne API key created in "Settings/Users/Service Users" should have the following minimum role: “IR Team”. The API key and the scripts must be created for and with the same user and the required account/site. -To use the SentinelOne executor, just fill the following configuration. +To use the SentinelOne executor, just fill the following configuration in the Integrations (Executors) tab from OpenAEV menu. | Parameter | Environment variable | Default value | Description | |:-----------------------------------------------------------|:-----------------------------------------------------------|:--------------|:------------------------------------------------------------------------------------------------------------------------------------------------------| diff --git a/docs/usage/assets.md b/docs/usage/assets.md index 0888b55a..e1ccbb48 100644 --- a/docs/usage/assets.md +++ b/docs/usage/assets.md @@ -66,14 +66,14 @@ To register new endpoints, you will need to install an agent. You can find detai **Agents panel** -| Attribute | Meaning | -|-----------------|----------------------------------------------------------------------| -| **Name** | Local user account on the endpoint that executes the agent process | -| **Executor** | Agent type (OpenAEV, Crowdstrike, Tanium, SentinelOne or Caldera) | -| **Privilege** | Local account's privileges on the endpoint (admin, or standard user) | -| **Deployment** | Installation type (Service or Session) | -| **Status** | Active or Inactive (threshold: 1 hour) | -| **Last seen** | Last seen it has been pinged | +| Attribute | Meaning | +|-----------------|-------------------------------------------------------------------------------------| +| **Name** | Local user account on the endpoint that executes the agent process | +| **Executor** | Agent type (OpenAEV, Crowdstrike, Tanium, SentinelOne, Palo Alto Cortex or Caldera) | +| **Privilege** | Local account's privileges on the endpoint (admin, or standard user) | +| **Deployment** | Installation type (Service or Session) | +| **Status** | Active or Inactive (threshold: 1 hour) | +| **Last seen** | Last seen it has been pinged | !!! note