Reverse HTTP Listener
The HTTP Listener establishes an HTTP/HTTPS server on a specified port, managing bidirectional data traffic with agents. It provides secure and discreet communication channels between the C2 infrastructure and deployed agents, blending with regular web traffic patterns.
Plugin ID: shelldot.listener.agent-reverse-http
Configuration Reference
The If omitted column describes hand-written JSON sent directly to the server. It is
different from the plugin's named default example: that example explicitly supplies port
8080, hosts localhost and 127.0.0.1, getUri /get, postUri /post, HTTPS enabled,
and a 5-second sleep with up to 5 seconds of jitter.
Connection
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
port |
int | Yes | Invalid | Port number (1-65535) the agent calls home to. Also the listener port unless bindToPort is set. |
bindToPort |
int | No | Uses port |
Port number (1-65535) the listener actually binds to. Useful when a reverse proxy or redirector sits in front of the listener. |
bindAddress |
string | No | null |
Address the listener binds to. When null, binds to all interfaces. |
https |
bool | Yes | Invalid | Protocol flag. false = HTTP, true = HTTPS. |
startTime |
string | No | null |
Optional UTC start time for the listener (e.g. 2025-04-10T11:02:09Z). |
instantResponses |
bool | No | true |
When true, the agent sends data immediately. When false, the agent waits for the sleep timeout before responding - better OPSEC but higher latency. |
keepConnectionsAlive |
bool | No | Agent default | When true, keeps the underlying HTTP connection open between agent requests instead of closing after each round-trip. The named default example sets this to true. |
URIs and Metadata
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
getUri |
string | Yes | Invalid | URI the agent uses for GET requests when it has no data to send (polling). |
postUri |
string | Yes | Invalid | URI the agent uses for POST requests to send data (also receives data in the response). |
stagedUri |
string | No | Disabled | URI for downloading any generated payload artifact. The endpoint is enabled only when stagedUriPayloadId is also set. |
stagedUriPayloadId |
string | No | Disabled | GET parameter name whose value selects a payload by numeric ID or unique name. The endpoint is enabled only when stagedUri is also set. |
filename |
string | No | Payload filename | Template string for the Content-Disposition filename when serving payloads via stagedUri. null or an empty string preserves the payload's own filename. See filename Template Functions below. |
fileStorageUri |
string | No | Disabled | Base URI for retrieving files from file storage. |
metadataCookieName |
string | Yes | Invalid | Cookie name used to carry agent metadata. |
metadataPrefix |
string | Yes | Invalid | String prepended to metadata before encoding. Set to an empty string to disable the prefix. The named default example uses metaPrefix. |
metadataSuffix |
string | Yes | Invalid | String appended to metadata before encoding. Set to an empty string to disable the suffix. The named default example uses metaSuffix. |
httpSpecificMetadataHeader |
string | No | null |
If set, the agent includes an additional HTTP header with this name in each request, carrying listener-specific metadata alongside the main metadata cookie. |
TLS Certificate
When https is true, the listener self-generates a self-signed TLS certificate by default. Supply a PKCS#12 keystore to use your own certificate. Any keystore file, password, or alias requires HTTPS. A custom keystore file also requires tls.keystorePassword; if an alias is supplied, it must exist in the keystore.
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
keystoreFile |
file | No | Self-signed certificate | PKCS#12 keystore containing the TLS certificate and private key. Sent as a multipart form field. |
tls.keystorePassword |
string | No | Generated when self-signing | Password for the keystore. Required when keystoreFile is supplied. |
tls.keystoreAlias |
string | No | jetty when self-signing |
Alias of the certificate entry inside the keystore. |
Sleep
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
sleep |
int | Yes | Invalid | Nonnegative global base sleep time in seconds between agent requests. Can be overridden per callback group via httpCallbacks[].sleep. |
sleepRandom |
int | No | 0 |
Nonnegative global jitter in seconds added to the sleep time. Can be overridden per callback group via httpCallbacks[].sleepRandom. The named default example uses 5. |
Web Proxy
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
webProxy |
string | No | Disabled | URL of the web proxy for agent connections (e.g. http://proxy.corp:8080). |
webProxyUsername |
string | No | null |
Username for proxy authentication. |
webProxyPassword |
string | No | null |
Password for proxy authentication. |
webProxyWindowsAuth |
bool | No | Agent default | Use Kerberos/NTLM authentication for the proxy (Windows only). The named default example sets this to true. |
HTTP Headers
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
headers[] |
array | No | No extra headers | Additional HTTP headers sent with every agent request. Each entry is an object with required, non-null name and value fields. |
Example:
Custom User-Agent
By default, agents use a built-in User-Agent string. Override it by adding a User-Agent entry to the headers array. The first request uses the default value; subsequent requests use the custom value.
Callback Groups (httpCallbacks[])
httpCallbacks is required. Each callback group defines a nonempty set of valid IP addresses or hostnames the agent can connect to, along with independent sleep and rotation settings. Multiple callback groups allow the agent to use completely different connection profiles.
| Parameter | Type | Required | If omitted | Description |
|---|---|---|---|---|
hosts[] |
string[] | Yes | Invalid | One or more valid IP addresses or hostnames the agent can connect to. |
sleep |
int | No | Inherits sleep |
Nonnegative base sleep time in seconds for this callback group. |
sleepRandom |
int | No | Inherits sleepRandom |
Nonnegative jitter in seconds for this callback group; effectively 0 when neither value is set. |
hostHeaders[] |
string[] | No | [] |
HTTP Host header values used when connecting - enables domain fronting. |
hostsRotation |
object | No | No rotation rule | Rotation rules for host selection. See Rotation Rules. |
hostHeaderRotation |
object | No | No rotation rule | Rotation rules for host header selection. See Rotation Rules. |
Multiple hosts shorthand
If hosts[] contains more than one entry, each host is treated as if it were its own callback group with the same rotation, sleep, and header settings. This is a shorthand to keep configurations compact. See the equivalent configurations example below.
Rotation Rules
Rotation rules define when the agent should switch to the next host or change the HTTP Host header. This is used for resilience (failover to backup endpoints) and evasion (domain fronting rotation).
Rotation Object
| Parameter | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | Rotation strategy: FAILOVER, ROTATE, or RANDOM. |
counter |
int | Yes | Nonnegative numeric threshold that triggers a rotation. Interpretation depends on type. |
unit |
string | Yes | Unit for the counter: TRIES, SECONDS, MINUTES, or HOURS. |
Rotation Types
FAILOVER - Switch to the next host/header after a number of consecutive failures.
- Counter +
TRIES: switch after N consecutive failed connection attempts - Counter + time unit: switch after N seconds/minutes/hours since the last successful connection
ROTATE - Switch to the next host/header after a number of total attempts (successful or not).
- Counter +
TRIES: switch after every N connection attempts - Counter + time unit: switch after N seconds/minutes/hours since the last rotation
RANDOM - Switch randomly before each request.
- The probability of switching is
1/counterper request - The
unitvalue is ignored for this type
Rotation Examples
| Scenario | Type | Counter | Unit | Behavior |
|---|---|---|---|---|
| Switch after 7 requests (success or fail) | ROTATE |
7 |
TRIES |
Cycles through hosts every 7 attempts |
| Switch after 2 consecutive failures | FAILOVER |
2 |
TRIES |
Only switches when connectivity breaks |
| Switch after 3 min without success | FAILOVER |
3 |
MINUTES |
Time-based failover |
| Switch every hour | ROTATE |
1 |
HOURS |
Periodic rotation regardless of success |
| 12.5% chance per request | RANDOM |
8 |
TRIES |
Random switching (1/8 probability) |
Rotation Logic
The rotation logic follows the workflow below. Host rotation and host header rotation operate independently, each with their own counters. When both trigger simultaneously, the host rotation takes priority and the host header counter resets.

Counter independence
A successful connection resets the FAILOVER counter for host rotation but does not affect the host header rotation counter. Each counter tracks its own state independently.
Configuration Scenarios
Scenario 1 - Basic HTTP Beaconing
The minimal configuration for an agent calling back over plain HTTP. Suitable for lab environments or controlled exercises.
Scenario 2 - HTTPS with Domain Fronting
The agent connects to a CDN edge IP but sends a different Host header pointing to a CDN-fronted domain. Traffic appears as legitimate CDN activity. Requires an HTTPS listener and a valid (or self-signed) certificate.
Info
Domain fronting requires that the CDN or proxy permits requests with a mismatched Host header. Some CDN providers have restrictions on this technique. Test in your environment before relying on it operationally.
Scenario 3 - Resilient Multi-Endpoint Rotation
The agent alternates between multiple C2 endpoints across different IPs. Designed for long-haul operations where individual endpoints may go offline or get blocked. The FAILOVER strategy ensures the agent only switches when connectivity actually breaks.
How it works:
- Agent connects to
c2-primary.example.com - After 3 consecutive failures, it switches to
c2-secondary.example.com - After 3 more consecutive failures, it moves to
c2-tertiary.example.com - The cycle repeats - the agent keeps retrying all endpoints indefinitely
- A successful connection resets the failure counter for that host
Tip
For maximum resilience, host each endpoint on a different cloud provider or geographic region. Combine with long sleep intervals (sleep: 300+) to reduce detection surface during low-activity periods.
Scenario 4 - Detailed Rotation Walkthrough
A configuration with two IPs and three host headers, showing exactly how rotation counters interact.
Step-by-step rotation behavior:
- Agent starts connecting to
1.2.3.4with host headeralpha - After 3 consecutive failures, the host header switches to
bravo(host stays at1.2.3.4- only 3 of 9 failures reached) - After 3 more consecutive failures, the host header switches to
charlie(host still1.2.3.4- only 6 of 9 failures) - After 3 more failures, both rotation rules trigger simultaneously. Host rotation takes priority:
- Connection switches to
5.6.7.8 - Host header counter resets back to
alpha
- Connection switches to
- The pattern repeats for
5.6.7.8: header rotates throughalpha→bravo→charlieevery 3 failures - After 9 total failures on
5.6.7.8, the host rotates back to1.2.3.4(circular selection) and headers reset toalpha
Info
The counters for host rotation and host header rotation operate independently. If there are three failed connections followed by a successful connection, and then six subsequent failures, the connection will not switch to 5.6.7.8 - its counter resets after the successful connection. Similarly, host header counters reset after each rotation trigger, so in this case the host header will have rotated three times and returned to "charlie".
Equivalent Configurations
When the hosts[] array in a callback group contains more than one entry, it is shorthand for multiple callback groups with identical settings. These two configurations are equivalent:
Compact form (two hosts in one callback group):
Expanded form (each host in its own callback group):
filename Template Functions
Template tokens for the filename parameter, which controls the Content-Disposition header when payloads are served via stagedUri.
| Template | Description |
|---|---|
{arch} |
Payload architecture in lowercase (for example, x64). |
{rand_str_X} |
Random mixed-case alphanumeric string of length X, capped at 64 characters (e.g. {rand_str_10} produces A9JfMQRwq8). |
{rand_str_X_Y} |
Random mixed-case alphanumeric string with an inclusive length between X and Y; each bound is capped at 64 (e.g. {rand_str_9_20}). |
{rand_int_X} |
Random integer from 0 (inclusive) to X (exclusive). |
{rand_int_X_Y} |
Random integer from X (inclusive) to Y (exclusive) (e.g. {rand_int_9_2000000000}). |
{ext} |
File extension of the requested payload, including the dot (e.g. .exe, .dll). |
{payload_id} |
Payload ID of the requested payload. |
Example:
Result: