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. Thetask_domain_name
value must also be set for thetask_host_name
value to be used. Iftask_host_name
is set, a resource record will be created in the hosted zone that corresponds with the domain name provided as thetask_domain_name
. If not provided, it defaults toNone
.--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. Thetask_host_name
value must also be set for thetask_domain_name
value to be used. If not provided, it defaults toNone
.--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 toNone
.--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 toNone
.
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. Thetask_domain_name
value must also be set for thetask_host_name
value to be used. Iftask_host_name
is set, a resource record will be created in the hosted zone that corresponds with the domain name provided as thetask_domain_name
. If not provided, it defaults toNone
.--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. Thetask_host_name
value must also be set for thetask_domain_name
value to be used. If not provided, it defaults toNone
.--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 toNone
.--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 toNone
.
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) specifyhost
networking to ensure that the container can expose ports as needed when running various operations.--cap-add
(optional) specifySYS_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 theterraform 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 totrue
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 sharedinstruct_instance
and then stage and configure the same exploit targeting a different host using a different sharedinstruct_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 sameinstruct_instance
value for commands executed across different container tasks, the resulting output from the tasks can be searched or filtered by their commoninstruct_instance
value. One drawback to this approach is that executing the sameinstruct_command
multiple times while using the sameinstruct_instance
will cause theinteract_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 differentinstruct_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 sharedinstruct_instance
and then stage and configure the same exploit targeting a different host using a different sharedinstruct_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 sameinstruct_instance
value for commands executed across different container tasks, the resulting output from the tasks can be searched or filtered by their commoninstruct_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 Taskinstruct_command
via theinteract_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.
Updated about 1 year ago