Security AWS

MITRE Caldera on Ubuntu 24.04 on AWS User Guide

| Product: MITRE Caldera on AWS

Overview

MITRE Caldera is an open source adversary emulation platform maintained by MITRE. It automates the work of a red team: you choose an adversary profile assembled from a library of abilities mapped to MITRE ATT&CK, Caldera plans the sequence, deploys agents to the hosts in scope, executes each step and collects the results. Blue-team plugins support the defensive side of the same exercise. It is the tooling security teams use to test, repeatably, whether their detection and response actually work.

Caldera is, functionally, command-and-control (C2) infrastructure: the server issues instructions that agents execute on target hosts. That is exactly what makes it powerful and exactly why it must be run carefully. Upstream's own documentation states the web interface is "not hardened and thoroughly pentested" and should be deployed on isolated networks rather than exposed to the public internet. The cloudimg image is built around that reality.

The cloudimg image installs Caldera 5.3.0 at /opt/caldera in a dedicated Python virtual environment, running as the non-login caldera user, bound to loopback 127.0.0.1:8888, behind nginx on port 80. The VueJS console is pre-built into the image, and the Go toolchain the sandcat agent compiler needs is installed, so you can build an adversary profile, deploy an agent and run an operation immediately after launch.

Ships closed, because this is C2. Upstream's sample configuration publishes working logins (red/admin, admin/admin, blue/admin) and API keys (ADMIN123, BLUEADMIN123). Every one of those is treated as compromised: this image removes them from the tree, refuses them at runtime, and empties the seeded user map so that even starting the server insecurely yields no working login. On the first boot of every instance, a one-shot service generates seven per-instance secrets — the reverse-proxy gate password, the red and blue account passwords, the red and blue REST API keys, the encryption key and the salt — and writes them to a root-only file.

No open interface. HTTP Basic Authentication is enforced at the nginx reverse proxy across the whole platform — the console, the entire REST API, agent tasking, ability execution and file upload — with only a static, unauthenticated /healthz probe left open for load balancers. Behind the gate, Caldera enforces its own per-instance accounts and API keys.

No published C2 port. Caldera's defaults expose six agent-contact listeners on all interfaces (HTTP 8888, DNS 8853, SSH-tunnel 8022, FTP 2222, TCP 7010, UDP 7011, WebSocket 7012). In this image every one is bound to 127.0.0.1, so a freshly launched instance publishes no command-and-control port. Opening a contact for a real engagement is a deliberate, documented step covered later in this guide.

Authorised use only. Caldera is adversary-emulation infrastructure. Use it only against systems you are explicitly authorised to test. Before you expose this instance to anything, restrict its EC2 security group to your own source IP address (covered in the first section below).

What is included:

  • MITRE Caldera 5.3.0 (Apache-2.0), pinned to the release tag with its plugin submodules pinned to the exact commits that tag records (bundled precompiled offensive Windows payloads are removed for marketplace malware-scan compliance; the full ability library and on-demand agent compilation are unaffected, and you can add your own payloads)
  • caldera.service bound to 127.0.0.1:8888, running as the dedicated non-login caldera user, gated so it cannot start before per-instance secrets exist
  • The pre-built magma VueJS console, the ATT&CK-mapped stockpile ability library, and the Go toolchain the sandcat agent compiler requires
  • nginx reverse proxy on port 80 with per-instance HTTP Basic Auth over everything except /healthz, ready for your TLS certificate
  • caldera-firstboot.service that generates seven per-instance secrets, resolves the instance URL, and proves the auth gate is closed before completing
  • Ubuntu 24.04 LTS base, fully patched, unattended security upgrades enabled
  • 24/7 cloudimg support

Prerequisites

Before you deploy this image you need:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 and port 80 from your own source IP only
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line

Step 1: Launch the instance from the AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images choose AWS Marketplace AMIs and search for MITRE Caldera. Select the cloudimg listing and choose Select, then Continue on the subscription summary.

Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and create or select a security group that opens ports 22 and 80 from your own IP address only (see "Scope the security group to your own IP" below). Leave the root volume at the default size or larger.

Select Launch instance. First-boot initialisation takes a short time after the instance state becomes Running and the status checks pass: the image resolves the instance address, generates the seven per-instance secrets, writes the credentials file, and starts the services.

Step 2: Launch the instance from the AWS CLI

The following block launches an instance from the cloudimg MITRE Caldera Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens ports 22 and 80 from your own IP only.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --associate-public-ip-address \
  --metadata-options "HttpTokens=required" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=mitre-caldera}]'

Connecting to your instance

Connect over SSH on port 22 as the login user for your operating system variant:

Variant SSH login user
Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<instance-public-ip>

Scope the security group to your own IP

This is the single most important step, and it comes first. Because Caldera is C2 infrastructure, the instance should never be reachable by anyone but you. In the EC2 console, open your instance's Security tab, edit the inbound rules of its security group, and set the Source of the port 22 and port 80 rules to your own IP address (My IP) rather than Anywhere / 0.0.0.0/0. From the AWS CLI, revoke any open rule and add one scoped to your address:

# Find your public IP with: curl -s https://checkip.amazonaws.com
MY_IP=203.0.113.10
aws ec2 authorize-security-group-ingress \
  --group-id <security-group-id> \
  --ip-permissions IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges="[{CidrIp=${MY_IP}/32}]"

Do this before you retrieve credentials or open the console.

Connect and retrieve your per-instance credentials

SSH to the instance as ubuntu, then read the credentials file. It is generated uniquely on this instance at first boot and is readable only by root.

sudo cat /root/caldera-credentials.txt

Expected output (the values shown are placeholders — yours are unique to this instance):

# MITRE Caldera 5.3.0 - Per-VM Credentials
# Generated on first boot by caldera-firstboot.service. Unique to THIS VM. Store them safely.
CALDERA_URL=http://<instance-public-ip>/
CALDERA_WEB_USER=caldera
CALDERA_WEB_PASSWORD=<CALDERA_WEB_PASSWORD>
CALDERA_RED_USER=red
CALDERA_RED_PASSWORD=<CALDERA_RED_PASSWORD>
CALDERA_BLUE_USER=blue
CALDERA_BLUE_PASSWORD=<CALDERA_BLUE_PASSWORD>
CALDERA_API_KEY_RED=<CALDERA_API_KEY_RED>
CALDERA_API_KEY_BLUE=<CALDERA_API_KEY_BLUE>

There are two authentication layers, by design:

  1. The nginx gateCALDERA_WEB_USER / CALDERA_WEB_PASSWORD, prompted by the browser before anything else loads.
  2. Caldera's own loginred (offensive) or blue (defensive), with CALDERA_RED_PASSWORD / CALDERA_BLUE_PASSWORD, entered on Caldera's login page.

Verify the service and the health endpoint

Confirm both services are active and the unauthenticated health probe answers:

sudo systemctl status caldera nginx --no-pager | head -18
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz

caldera.service and nginx.service should both report active (running), and the /healthz probe should print 200.

Confirm the platform is closed to unauthenticated callers

The /healthz probe is the only path reachable without a credential. Everything else — the console, the REST API, agent tasking, ability execution, file upload — returns 401 unless you present the per-instance gate credential. The image ships a helper that proves this end-to-end, including a positive control (the same requests succeed with the real credential) and a check that the published upstream defaults are refused:

sudo /usr/local/sbin/caldera-cred-roundtrip.sh

The helper prints a series of OK: lines: the gate is closed to unauthenticated callers (UI, agents, operations and file-upload all 401), a wrong gate password is refused, the positive control passes with the per-instance credential, and the published upstream default API key ADMIN123 and login red/admin are both refused.

Confirm no command-and-control port is published

Enumerate every listening socket and confirm only SSH (22) and the nginx gate (80) are bound to a non-loopback address. Every Caldera agent-contact listener is bound to 127.0.0.1:

sudo ss -lntu
sudo /usr/local/sbin/caldera-listener-audit.sh | tail -3

The audit prints LISTENER-AUDIT OK: only tcp:22 tcp:80 udp:68 are publicly bound; every Caldera agent contact is loopback-only. (udp:68 is the OS DHCP client, not an application listener.)

Open the console

In a browser, go to CALDERA_URL (http://<instance-public-ip>/). You will first be challenged by the nginx gate — enter CALDERA_WEB_USER / CALDERA_WEB_PASSWORD. Then Caldera's own login page appears; sign in as red with CALDERA_RED_PASSWORD.

Caldera login page reached through the per-instance reverse-proxy gate

After you sign in, the dashboard summarises the platform: the number of agents checked in, operations run, and the abilities and adversary profiles available in the image. A freshly launched instance shows zero agents and zero operations — no adversary-emulation state ships in the image.

Caldera dashboard summarising agents, operations, abilities and adversaries

Explore the ATT&CK ability library

The abilities view shows the stockpile plugin's curated library of abilities that adversary profiles are built from, each tagged with its MITRE ATT&CK tactic and technique. Filter by tactic, technique, plugin or platform to find the tradecraft you want to emulate, and add your own abilities and payloads as needed.

The ATT&CK-mapped ability library

Deploy an agent and run your first operation

The quickest way to see Caldera work end-to-end is to deploy an agent on the instance itself and run a small operation against it. This is a self-contained demonstration on a host you already control. The agents view starts empty — you must deploy at least one agent before an operation can run:

Agents view on a fresh instance, ready to deploy the first agent

  1. In the console, open agents and click Deploy an agent. Choose the Sandcat agent, platform linux, and set the server address to http://127.0.0.1:8888. Copy the generated one-line delivery command.
  2. SSH to the instance and paste that command (it downloads the compiled agent and starts it). Within a few seconds the agent registers and appears in the agents view as alive and trusted.
  3. Open operations, click New Operation, choose an adversary profile (for example a simple discovery profile) and the group your agent joined, and start it. When it finishes, expand a link and click View Output to see the actual command output the agent returned through the C2 channel.

That round trip — the agent ran a real command and the result came back — is the core of the platform.

Drive Caldera from the REST API

Every action in the console is available over the REST API, which is gated by both the nginx credential and a Caldera API key. Send the API key in the KEY header:

# From the instance (loopback bypasses the nginx gate); off-box, add -u "$CALDERA_WEB_USER:$CALDERA_WEB_PASSWORD"
KEY=$(sudo grep '^CALDERA_API_KEY_RED=' /root/caldera-credentials.txt | cut -d= -f2-)
curl -s -H "KEY: ${KEY}" http://127.0.0.1:8888/api/v2/agents | head -c 400

This returns the JSON list of registered agents. Full API documentation is available in the console under api docs.

Verify the shipped software provenance

The exact Caldera version and the pinned plugin submodule commits shipped in this image are recorded on disk:

head -12 /opt/caldera/.cloudimg-plugin-pins.txt

Running against remote hosts (advanced)

By default this image publishes no C2 port, so agents can only reach the server over loopback (as in the demonstration above). To run an engagement against remote hosts you are authorised to test, you deliberately expose the contact you need:

# 1. Edit /opt/caldera/conf/local.yml and change the relevant app.contact.* binding
#    from 127.0.0.1 to 0.0.0.0 (for the HTTP contact, also set host: 0.0.0.0).
#    Keep every contact you are not using on loopback.
# 2. Restart Caldera:
sudo systemctl restart caldera
# 3. Add an inbound security-group rule for that specific port, scoped to the source
#    ranges of the hosts in scope - never 0.0.0.0/0.
# 4. Generate agents pointed at your instance public address from the agents view.

Treat an exposed contact as live C2: keep the security group tightly scoped, tear it down when the engagement ends, and never leave a contact open to the internet.

TLS

nginx serves plain HTTP on port 80 out of the box so you can reach the console immediately behind your scoped security group. For any real use, put TLS in front of it. Point a DNS name at the instance and issue a certificate with your own ACME client or certificate, then add a listen 443 ssl; server block to the nginx site at /etc/nginx/sites-available/cloudimg-caldera and reload nginx. Because Caldera derives its URLs from the request host, no application reconfiguration is needed.

Support

This image includes 24/7 cloudimg support. Our engineers help with Caldera deployment, per-instance credential management and the reverse-proxy gate, agent deployment and contact configuration, building adversary profiles and custom abilities, running and interpreting operations, REST API automation, plugin and version upgrades, and reverse-proxy and TLS configuration. Email support@cloudimg.co.uk.

This is a repackaged open source software product with additional charges for cloudimg support services. Caldera and ATT&CK are trademarks of The MITRE Corporation. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.