Usage Through CLI Console

Running the ./havoc CLI with the -p option will drop you into an interactive console. If a profile is specified, the profile's corresponding connection parameters will be used. If no profile is specified, the default profile will be used. If a default profile does not exist, the ./havoc CLI will terminate with an error.

To start the ./havoc CLI in console mode with a specific profile, use the following command:
./havoc -p <profile>

To exit the ./havoc CLI console, type exit or press Ctrl+D.

The ./havoc CLI console supports the following usage commands:

Launching Containers Tasks

Container tasks can be launched as ECS tasks in your AWS account by using the run_task or task_startup commands. 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 command 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 command launches the task and then waits for the task to report that it is ready.

run_task --task_name=<task_name> --task_type=<task_type_name> --task_host_name=<task_host_name> --task_domain_name=<task_domain_name> --portgroups=<portgroup1,portgroup2,...> --end_time=<time_string>

  • --task_name - (required) a unique identifier to associate with the task.
  • --task_type - (required) specify the type of container task to be executed. See the [Task Types] (doc:administration-through-cli-console#task-types) section of the Administration Through CLI Console page for details on listing the available task types.
  • --task_host_name - (optional) a host name to associate with 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 - (optional) a domain name to associate with the task. The domain name must be tracked as a domain resource by the Campaign API. See the Domains section of the Administration Through CLI Console 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 - (optional) a comma separated list of portgroups to associate with the task. 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_startup --task_name=<task_name> --task_type=<task_type_name> --task_host_name=<task_host_name> --task_domain_name=<task_domain_name> --portgroups=<portgroup1,portgroup2,...> --end_time=<time_string>

  • --task_name - (required) a unique identifier to associate with the task.
  • --task_type - (required) specify the type of container task to be executed. See the [Task Types] (doc:administration-through-cli-console#task-types) section of the Administration Through CLI Console page for details on listing the available task types.
  • --task_host_name - (optional) a host name to associate with 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 - (optional) a domain name to associate with the task. The domain name must be tracked as a domain resource by the Campaign API. See the Domains section of the Administration Through CLI Console 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 - (optional) a comma separated list of portgroups to associate with the task. 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 or ./havoc CLI 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:

sudo docker run -d \
  --name=<container-name> \
  --network host \
  --cap-add SYS_ADMIN \
  -e "LOCAL_IP=$(hostname -I)" \
  -e "CAMPAIGN_ID=<campaign-id>" \
  -e "USER_ID=<campaign-user-id>" \
  -e "TASK_NAME=<task-name>" \
  -e "TASK_CONTEXT=<task-context>" \
  -e "REMOTE_TASK=true" \
  -e "API_KEY=<api-key>" \
  -e "SECRET=<secret>" \
  -e "API_DOMAIN_NAME=<api-domain-name>" \
  -e "API_REGION=<api-region>" \
  public.ecr.aws/havoc_sh/<task-type>:latest \
  /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
  • --name - (optional) provide a name that Docker will assign to the container.
  • --network - (required) specify host networking to ensure that the container can expose ports as needed when running various operations.
  • --cap-add (optional) specify SYS_ADMIN if you are running a Trainman Container Task with intention of running Samba as an Active Directory Domain Controller.
  • -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 initial ./havoc deployment.
  • -e "API_REGION=<api-region>" - (required) this should be the value of your campaign's api_region that was provided as output from the initial ./havoc deployment.
  • 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 executing automated playbooks, it can be helpful to verify that a required container task is present. The verify_task command 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 command returns a "task not found" message.

verify_task --task_name=<task_name> --task_type=<task_type>

  • --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 command is used to send instructions (a.k.a. instruct_command) to a container task and then retrieve the results of the instruction. This command combines the operations of the instruct_task and get_task_results commands listed below. As such, there's no need to run the instruct_task and get_task_results commands individually if you're using the interact_with_task command.

interact_with_task --task_name=<task_name> --instruct_instance=<instruct_instance> --instruct_command=<instruct_command> --instruct_args=<dict>

  • --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 command is used to send instructions (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.

instruct_task --task_name=<task_name> --instruct_instance=<instruct_instance> --instruct_command=<instruct_command> --instruct_args=<dict>

  • --task_name - (required) the name of the task you want to instruct.
  • --instruct_instance - (required) a unique string to associate with the instruction (defaults to 'havoc').
  • --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.

If using the instruct_task command, another command 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 commands are used to query the queue.

get_task_results --task_name=<task_name>

  • --task_name - (required) the name of the task to retrieve results from.

get_filtered_task_results --task_name=<task_name> --instruct_command=<instruct_command> --instruct_instance=<instruct_instance>

  • --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 command will periodically query a task and check its status. When 'idle' is returned as the status, the wait_for_idle_task command will return the task's details.

wait_for_idle_task --task_name=<task_name>

  • --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 command can be used for this purpose. When executed, the wait_for_c2 command 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 command returns the container task and agent/session details.

wait_for_c2 --task_name=<task_name>

  • --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 command exists for this purpose.

verify_agent --task_name=<task_name> --agent_name=<agent_name>

  • --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 commands but both of those methods require using the get_task_results or get_filtered_task_results command to gather the shell command's output. The execute_agent_shell_command command 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.

execute_agent_shell_command --task_name=<task_name> --agent_name=<agent_name> --command=<command> --wait_for_results=<True|False> --completion_string=<completion_string>

  • --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 command 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 commands but both of those methods require using the get_task_results or get_filtered_task_results command to gather the module's output. The execute_agent_module command 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.

execute_agent_module --task_name=<task_name> --agent_name=<agent_name> --module=<module> --module_args=<module_args> --wait_for_results=<True|False> --completion_string=<completion_string>

  • --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 command 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 command. 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 commands 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 command for that purpose. Technically, the interact_with_task and instruct_task commands 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 command automatically decodes and decompresses the results.

get_agent_results --task_name=<task_name> --agent_name=<agent_name> --task_id=<task_id>

  • --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 command or send the terminate instruction through the instruct_task command. Both of these approaches instructs the container task to shut itself down, which allows the container task to generate confirmation output indicating that it 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 command provides the added benefit of waiting for the task's "terminating" message to return, thereby confirming that the task was shutdown cleanly.

task_shutdown --task_name=<task_name>

  • --task_name - (required) the name of the task that to shutdown.

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

instruct_task --task_name=my_task --instruct_instance=foo --instruct_command=terminate

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.

kill_task --task_name=<task_name>

  • --task_name - (required) the name of the task to kill.

Remote container tasks can be terminated with either of the task_shutdown and instruct_task commands above however, the kill_task command 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 command to clean up the remote container task references in the Campaign API.