Executing and interacting with ./havoc container tasks can be accomplished through the `/task-control` API endpoint but the ./havoc Python SDK has pre-built methods that can be used to achieve the same end result.

A connection to the Campaign API must be created before you can launch or interact with container tasks. To connect to the Campaign API:

Start python:

ο»Ώ

Import the havoc package and create the connection:

ο»Ώ

# Launching Container Tasks

Container tasks can be launched as ECS tasks in your AWS account by using the `run_task` or `task_startup` methods of the havoc Python package. This approach will launch the container task using the ECS Cluster and Task Definitions that are provisioned as part of the ./havoc Campaign deployment. The `run_task` method launches the task and immediately returns the ./havoc API response indicating whether or not the request to launch the task was successful. The `task_startup` method launches the task and then waits for the task to report that it is ready.

ο»Ώ
  • `task_name` - (Required) a uniquely recognizable identifier to associate with the task.

  • `task_type` - (Required) specify the type of container task to be executed. See the [Task Types](πŸ”—ο»Ώ) section of the [Administration Through SDK](πŸ”—ο»Ώ) page for details on listing the available task types.

  • `task_host_name` - specify a host name for the task. The `task_domain_name` value must also be set for the `task_host_name` value to be used. If `task_host_name` is set, a resource record will be created in the hosted zone that corresponds with the domain name provided as the `task_domain_name`. If not provided, it defaults to `'None'`.

  • `task_domain_name` - specify a domain name for the task. The domain name must be tracked as a domain resource by the Campaign API. See the [Domains](πŸ”—ο»Ώ) section of the [Administration](πŸ”—ο»Ώ) page for more details. The `task_host_name` value must also be set for the `task_domain_name` value to be used. If not provided, it defaults to `'None'`.

  • `portgroups` - include portgroups to be assigned to the task. The portgroups must be provided in the form of a list and each portgroup specified must be a valid, existing portgroup. If not set, it defaults to `['None']`.

  • `end_time` - (Optional) specify a future time to automatically terminate the task. The value must be a date/time string that matches the format `'%m/%d/%Y %H:%M:%S %z'`. If not set, it defaults to `'None'`.

ο»Ώ
  • `task_name` - (Required) a uniquely recognizable identifier to associate with the task.

  • `task_type` - (Required) specify the type of container task to be executed. See the [Task Types](πŸ”—ο»Ώ) section of the [Administration Through SDK](πŸ”—ο»Ώ) page for details on listing the available task types.

  • `task_host_name` - specify a host name for the task. The `task_domain_name` value must also be set for the `task_host_name` value to be used. If `task_host_name` is set, a resource record will be created in the hosted zone that corresponds with the domain name provided as the `task_domain_name`. If not provided, it defaults to `'None'`.

  • `task_domain_name` - specify a domain name for the task. The domain name must be tracked as a domain resource by the Campaign API. See the [Domains](πŸ”—ο»Ώ) section of the [Administration](πŸ”—ο»Ώ) page for more details. The `task_host_name` value must also be set for the `task_domain_name` value to be used. If not provided, it defaults to `'None'`.

  • `portgroups` - include portgroups to be assigned to the task. The portgroups must be provided in the form of a list and each portgroup specified must be a valid, existing portgroup. If not set, it defaults to `['None']`.

  • `end_time` - (Optional) specify a future time to automatically terminate the task. The value must be a date/time string that matches the format `'%m/%d/%Y %H:%M:%S %z'`. If not set, it defaults to `'None'`.

# Launching Remote Container Tasks

One of the nice benefits of containerized attack tools is that the containers can run on any platform that supports Docker containers. In the ./havoc context, the term "remote container task" describes any container task that is not running in the ECS Cluster that is provisioned by the ./havoc Campaign deployment. Remote container tasks cannot be launched directly from the Campaign API but ./havoc container tasks are designed such that they can be run on any system that can run Docker containers. With the proper environment variables applied, container tasks will check in and register themselves with the Campaign so that they may be controlled via the Campaign API. To run a ./havoc container task directly in Docker, use the following command:

ο»Ώ
  • `--name` - (Optional) provide a name that Docker will assign to the container.

  • `-e "LOCAL_IP=$(hostname -I)"` - (Required) if the container is being run on a Linux host, this option can be entered exactly as shown. Alternatively, the $(hostname -I) string can be replaced with a space separated list of IP addresses assigned to the host where the container is running.

  • `-e "CAMPAIGN_ID=<campaign-id>"` - (Required) this should be the value of your campaign's campaign_id that was provided as output from the `terraform apply` command.

  • ` -e "USER_ID=<campaign-user-id>"` - (Required) this should be the user ID associated with owner of the API key and secret that will be used to connect the container task to the Campaign API.

  • `-e "TASK_NAME=<task-name>"` - (Required) a unique name to associate with the task.

  • `-e "TASK_CONTEXT=<task-context>"` - (Required) the task_context should contain details about where the container task is running such as the site/location, machine name or both.

  • `-e "REMOTE_TASK=true"` - (Required) this parameter must be set to `true` for a remote container task.

  • `-e "API_KEY=<api-key>"` - (Required) replace `<api-key>` with the actual API key value that will be used to connect the remote container task to the Campaign API.

  • `-e "SECRET=<secret>"` - (Required) replace `<secret>` with the actual secret value that will be used to connect the remote container task to the Campaign API.

  • `-e "API_DOMAIN_NAME=<api-domain-name>"` - (Required) this should be the value of your campaign's api_domain_name that was provided as output from the `terraform apply` command.

  • `-e "API_REGION=<api-region>"` - (Required) this should be the value of your campaign's api_region that was provided as output from the `terraform apply` command.

  • `public.ecr.aws/havoc_sh/<task-type>:latest` - (Required) this should be the ./havoc public container registry's path for the container task type that you want to run. See the [Container Tasks](πŸ”—ο»Ώ) section for the ./havoc container registry details for each container task.

  • `/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf` - (Required) this is the command that runs the specific applications that are required for the container task to function. It should be entered exactly as shown.

# Verifying a Container Task

When creating automated playbooks, it can be helpful to verify that a required container task is present. The `verify_task` method will accept `task_name` and `task_type` inputs and query the running container tasks to verify that the task is present and that it is of the specified task type. If the task is verified, the task's details are returned. Otherwise, the method returns a `False`.

ο»Ώ
  • `task_name` - (required) than name of the task to verify.

  • `task_type` - (required) the type of task that the given task name should be.

# Interacting with Container Tasks

The `interact_with_task` method is used to send instructions (a.k.a. `instruct_command`) to a container task and then retrieve the results of the instruction. This method combines the operations of the `instruct_task` and `get_task_results` methods listed below. As such, there's no need to run the `instruct_task` and `get_task_results` methods individually if you're using the `interact_with_task` method.

ο»Ώ
  • `task_name` - (required) the name of the task you want to instruct.

  • `instruct_instance` - (optional) a unique string to associate with the instruction (defaults to a random string).

  • `instruct_command` - (required) the command to send to the task.

  • `instruct_args` - (optional) a dictionary of arguments to pass with the command. Some commands have required arguments. See the [Container Tasks](πŸ”—ο»Ώ) section for details about available container task commands and their arguments.

    The value specified in the `instruct_instance` parameter is used by the container task to run the instruct_command in a Python class instance. This has the benefit of being able to send multiple commands to a container task and have them executed within the same class instance. For example, when working with a Metasploit container task, the process of staging an exploit requires sending several commands to configure the exploit. In this scenario, you could stage and configure an exploit using multiple commands with a shared `instruct_instance` and then stage and configure the same exploit targeting a different host using a different shared `instruct_instance`. This way, you can execute an exploit against multiple targets while keeping the executions and their resulting outputs separated from one another. This has an added side benefit of making it easy to group operations together. By using the same `instruct_instance` value for commands executed across different container tasks, the resulting output from the tasks can be searched or filtered by their common `instruct_instance` value. One drawback to this approach is that executing the same `instruct_command` multiple times while using the same `instruct_instance` will cause the `interact_with_task` command to return only the results for the first execution of the command, which may cause unintended results during custom playbook executions. As such, care should be taken to ensure that executing the same command multiple times is done using different `instruct_instance` values for each command execution.

The `instruct_task` method is used to send a command (a.k.a. `instruct_command`) to a container task. See the [Container Tasks](πŸ”—ο»Ώ) section for information about commands available for each of the supported container tasks.

ο»Ώ
  • `task_name` - (Required) the unique name associated with the container task that you want to instruct.

  • `instruct_instance` - (Optional) a value that can be associated with the instruction. If the parameter is excluded, a default value of 'havoc' is used.

  • `instruct_command` - (Required) the command to send to the container task.

  • `instruct_args` - (Optional) a Python dictionary containing any arguments that need to be passed along with the command. Some commands have required arguments. See the [Container Tasks](πŸ”—ο»Ώ) section for details about available container task commands and their arguments.

The value specified in the `instruct_instance` parameter is used by the container task to run the instruct_command in a Python class instance. This has the benefit of being able to send multiple commands to a container task and have them executed within the same class instance. For example, when working with a Metasploit container task, the process of staging an exploit requires sending several commands to configure the exploit. In this scenario, you could stage and configure an exploit using multiple commands with a shared `instruct_instance` and then stage and configure the same exploit targeting a different host using a different shared `instruct_instance`. This way, you can execute an exploit against multiple targets while keeping the executions and their resulting outputs separated from one another. This has an added side benefit of making it easy to group operations together. By using the same `instruct_instance` value for commands executed across different container tasks, the resulting output from the tasks can be searched or filtered by their common `instruct_instance` value.

If using the `instruct_task` method, another method must be issued to gather the `instruct_task` results. Container tasks write their output to a queue that can be queried by `task_name`. The `get_task_results` and `get_filtered_task_results` methods can be used to query the queue. Results are returned in a list within the response dictionary. The key for the list is called 'queue' and can be accessed using `response['queue']`.

ο»Ώ
  • `task_name` - (Required) the unique name associated with the container task that you want to get results for.

ο»Ώ
  • `task_name` - (required) the name of the task to retrieve results from.

  • `instruct_instance` - (optional) the instruct_instance to retrieve results for.

  • `instruct_command` - (optional) the command to retrieve results for.

# Waiting for an Idle Task

When a container task is processing an instruction, its task status is set to 'busy' and when the container task finishes processing the instruction and returns the instruction's results, the container task's status is reset to 'idle.' If you attempt to send an instruction to a busy task, you will receive a '409' error from the ./havoc API. Therefore, it's necessary to wait for a task to become idle again prior to sending another instruction. The `wait_for_idle_task` method will periodically query a task and check its status. When 'idle' is returned as the status, the `wait_for_idle_task` method will return the task's details.

ο»Ώ
  • `task_name` - (required) the name of the task that should be queried until it becomes idle.

# Waiting for a C2 Agent or Session Connection

Container tasks that provide command and control capabilities have an `agent_status_monitor` or `session_status_monitor` event type that is triggered whenever a new agent or session connection is established with the task. It can be useful to continually poll the results queue until a new C2 connection is established. The `wait_for_c2` method can be used for this purpose. When called, the `wait_for_c2` method will generate a list of existing agents associated with the specified container task and then wait for a new `agent_status_monitor` or `session_status_monitor` event from the specified task to arrive in the queue. When the event arrives, the `wait_for_c2` method returns the container task and agent/session details.

ο»Ώ
  • `task_name` - (required) the name of the task that the C2 agent or session will connect to.

# Verifying a PowerShell Empire C2 Agent Exists

Before attempting to interact with an agent, it may be necessary to verify that an agent with the expected name is connected to the PowerShell Empire Container Task. The `verify_agent` method exists for this purpose.

ο»Ώ
  • `task_name` - (required) the name of the PowerShell Empire task that the C2 agent is associated with.

  • `agent_name` - (required) the name of the C2 agent to verify.

# Executing a Shell Command on a PowerShell Empire Agent

Executing shell commands on a PowerShell Empire agent can be achieved through the `instruct_task` and `interact_with_task` methods but both of those methods require using the `get_task_results` or `get_filtered_task_results` method to gather the shell command's output. The `execute_agent_shell_command` method will deliver a shell command request to the agent, poll the agent results until the output of the shell command is available, and then return the shell command output.

ο»Ώ
  • `task_name` - (required) the name of the PowerShell Empire task that the C2 agent is associated with.

  • `agent_name` - (required) the name of the C2 agent to execute the shell command on.

  • `command` - (required) the shell command to execute on the C2 agent.

  • `wait_for_results` - (optional) indicate whether the method should wait for the shell command results (True|False). Defaults to True.

  • `completion_string` - (optional) a string that should be present in the results to indicate the command execution is done. If not specified, results are returned as soon as any results data becomes available, which may lead to incomplete results being returned.

# Executing a Module on a PowerShell Empire Agent

Executing a module on a PowerShell Empire agent can be achieved through the `instruct_task` and `interact_with_task` methods but both of those methods require using the `get_task_results` or `get_filtered_task_results` method to gather the module's output. The `execute_agent_module` method will deliver a module execution request to the agent, poll the agent results until the output of the module is available, and then return the module output.

ο»Ώ
  • `task_name` - (required) the name of the PowerShell Empire task that the C2 agent is associated with.

  • `agent_name` - (required) the name of the C2 agent to execute the module on.

  • `module` - (required) the name, including the full path of the module to execute on the C2 agent.

  • `module_args` - (optional) a dictionary containing arguments to be passed to the module.

  • `wait_for_results` - (optional) indicate whether the method should wait for the module results (True|False). Defaults to True.

  • `completion_string` - (optional) a string that should be present in the results to indicate the module execution is done. If not specified, results are returned as soon as any results data becomes available, which may lead to incomplete results being returned.

    The list of available modules and their configuration parameters can be retrieved from a PowerShell Empire Container Task by passing the `get_modules` PowerShell Empire Container Task `instruct_command` via the `interact_with_task` method. See the [Available Commands](πŸ”—ο»Ώ) section of the [PowerShell Empire Container Task](πŸ”—ο»Ώ) page for more details.

# Getting Execution Results from a PowerShell Empire Agent

When executing a shell command or module on a PowerShell Empire C2 agent, the `execute_agent_shell_command` and `execute_agent_module` methods will automatically retrieve the output from the executed command or module. But if you need to pull shell command or module execution results independently from the execution request, you can use the `get_agent_results` method for that purpose. Technically, the `interact_with_task` and `instruct_task` methods can be used for that purpose as well but the results returned by the agent are compressed and encoded for efficiency purposes and the `get_agent_results` method automatically decodes and decompresses the results.

ο»Ώ
  • `task_name` - (required) the name of the PowerShell Empire task that the C2 agent is associated with.

  • `agent_name` - (required) the name of the C2 agent to get execution results from.

  • `task_id` - (optional) the ID associated with the specific shell command or module execution task that you would like to get results for. Defaults to None meaning that all execution results available for the agent are returned.

# Terminating Container Tasks

There are several options for terminating container tasks but the recommended approach is to use the `task_shutdown` method or send the `terminate` command through the `instruct_task` method. This approach instructs the container task to shut itself down, which allows the container task to generate confirmation output indicating that the container task is terminating. When the container task's "terminating" message is received by the Campaign API, it will do the required clean up regarding the status of the task. The `task_shutdown` method provides the added benefit of waiting for the task's "terminating" message to return, thereby confirming that the task was shutdown cleanly.

ο»Ώ
  • `task_name` - (required) the name of the task that to shutdown.

This example shows how to terminate a task using the `instruct_task` method:

ο»Ώ

If a container task is unresponsive, you can force kill it using the `kill_task` method. With this approach, the "terminating" output message will not be sent by the container task so the queue will not have confirmation from the container task that it was terminated. However, the Campaign API will still perform the required clean up regarding the status of the task including performing a forced shutdown of the ECS task.

ο»Ώ
  • `task_name` - (required) the name of the task that to kill.

Remote container tasks can be terminated with either of the `task_shutdown` and `instruct_task` methods above however, the `kill_task` method will not actually shutdown the container since the Campaign API does not have direct control over your remote Docker host. If a remote container task is unresponsive for some reason, you'll need to perform the `sudo docker stop <container-id>` and `sudo docker rm <container-id>` commands to actually shutdown and delete the container. In this scenario you would still use the `kill_task` method to clean up the remote container task references in the Campaign API.

# Examples

**Nmap scan example** The example below performs the following operations:

  • Launch an Nmap container task and verify that it is running

  • Run an Nmap scan

  • Get the scan results

  • Terminate the Nmap container task

ο»Ώ

**Metasploit exploit example** The example below performs the following operations:

  • Launch a Metasploit container task and verify that it is running

  • Stage, configure and execute an exploit

  • Get the exploit results

  • Terminate the Metasploit container task

ο»Ώ

ο»Ώ