Skip to content

Reverse HTTP Listener

The HTTP listener opens an HTTP/HTTPS server on a specified port and manages bidirectional data traffic with agents.

Plugin ID: shelldot.listener.agent-reverse-http


Configuration Reference

The If omitted column describes hand-written JSON sent directly to the API. 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.
bindAddress string No All interfaces Address the listener binds to.
https bool Yes Invalid Protocol flag. false = HTTP, true = HTTPS.
startTime string No null Optional UTC start time (e.g. 2025-04-10T11:02:09Z).
instantResponses bool No true When true, the agent sends data immediately. When false, it waits for the sleep timeout (better OPSEC).
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 for GET requests when the agent has no data to send (polling).
postUri string Yes Invalid URI 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. null or an empty string preserves the payload's own filename. See filename Template Functions.
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. Can be overridden per callback group via httpCallbacks[].sleep.
sleepRandom int No 0 Nonnegative global jitter in seconds. Can be overridden per callback group via httpCallbacks[].sleepRandom. The named default example uses 5.

HTTP Headers

Parameter Type Required If omitted Description
headers[] array No No extra headers Additional HTTP headers (array of objects with required, non-null name and value fields).

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.

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.

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.
hostsRotation object No No rotation rule Rotation rules for host selection.
hostHeaderRotation object No No rotation rule Rotation rules for host header selection.

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.
unit string Yes Unit for the counter: TRIES, SECONDS, MINUTES, or HOURS.

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 settings. This keeps configurations compact.


Equivalent Configurations

These two configurations are equivalent - the compact form puts two hosts in one callback group, while the expanded form uses separate callback groups:

Compact form:

{
  "port": 8089,
  "httpCallbacks": [
    {
      "hosts": ["localhost", "192.168.1.100"],
      "hostsRotation": { "type": "ROTATE", "counter": 7, "unit": "TRIES" },
      "sleep": 3000,
      "sleepRandom": 1000,
      "hostHeaders": ["alpha", "bravo", "charlie"],
      "hostHeaderRotation": { "type": "FAILOVER", "counter": 2, "unit": "TRIES" }
    }
  ],
  "getUri": "/GIVEITTOME",
  "postUri": "/NOWIGIVETOYOU",
  "stagedUri": "/candy",
  "stagedUriPayloadId": "payloadId",
  "fileStorageUri": "/files/",
  "metadataCookieName": "PHPSESSID",
  "metadataPrefix": "ABC",
  "metadataSuffix": "XYZ",
  "https": false,
  "startTime": "2023-04-10T11:02:09Z",
  "headers": [
    { "name": "Extra-Header-Name", "value": "Extra-Header-Value" }
  ],
  "sleep": 1500
}

Expanded form:

{
  "port": 8089,
  "httpCallbacks": [
    {
      "hosts": ["localhost"],
      "hostsRotation": { "type": "ROTATE", "counter": 7, "unit": "TRIES" },
      "sleep": 3000,
      "sleepRandom": 1000,
      "hostHeaders": ["alpha", "bravo", "charlie"],
      "hostHeaderRotation": { "type": "FAILOVER", "counter": 2, "unit": "TRIES" }
    },
    {
      "hosts": ["192.168.1.100"],
      "hostsRotation": { "type": "ROTATE", "counter": 7, "unit": "TRIES" },
      "sleep": 3000,
      "sleepRandom": 1000,
      "hostHeaders": ["alpha", "bravo", "charlie"],
      "hostHeaderRotation": { "type": "FAILOVER", "counter": 2, "unit": "TRIES" }
    }
  ],
  "getUri": "/GIVEITTOME",
  "postUri": "/NOWIGIVETOYOU",
  "stagedUri": "/candy",
  "stagedUriPayloadId": "payloadId",
  "fileStorageUri": "/files/",
  "metadataCookieName": "PHPSESSID",
  "metadataPrefix": "ABC",
  "metadataSuffix": "XYZ",
  "https": false,
  "startTime": "2023-04-10T11:02:09Z",
  "headers": [
    { "name": "Extra-Header-Name", "value": "Extra-Header-Value" }
  ],
  "sleep": 1500
}


Examples

Example 1 - Basic Configuration with Host Rotation

{
  "port": 8089,
  "https": false,
  "httpCallbacks": [
    {
      "hosts": ["localhost", "192.168.1.100"],
      "hostsRotation": { "type": "ROTATE", "counter": 7, "unit": "TRIES" },
      "sleep": 3000,
      "sleepRandom": 1000,
      "hostHeaders": ["alpha", "bravo", "charlie"],
      "hostHeaderRotation": { "type": "FAILOVER", "counter": 2, "unit": "TRIES" }
    }
  ],
  "getUri": "/GIVEITTOME",
  "postUri": "/NOWIGIVETOYOU",
  "stagedUri": "/candy",
  "stagedUriPayloadId": "payloadId",
  "fileStorageUri": "/files/",
  "metadataCookieName": "PHPSESSID",
  "metadataPrefix": "ABC",
  "metadataSuffix": "XYZ",
  "startTime": "2023-04-10T11:02:09Z",
  "headers": [
    { "name": "Extra-Header-Name", "value": "Extra-Header-Value" }
  ],
  "instantResponses": false,
  "sleep": 1500
}

Example 2 - Complex Rotation Rules

{
  "port": 8089,
  "https": false,
  "httpCallbacks": [
    {
      "hosts": ["localhost"],
      "hostsRotation": { "type": "FAILOVER", "counter": 5, "unit": "MINUTES" },
      "sleep": 10000,
      "sleepRandom": 4000,
      "hostHeaders": ["alpha", "bravo", "charlie"],
      "hostHeaderRotation": { "type": "ROTATE", "counter": 30, "unit": "SECONDS" }
    },
    {
      "hosts": ["192.168.1.100"],
      "hostsRotation": { "type": "FAILOVER", "counter": 3, "unit": "TRIES" },
      "sleep": 8000,
      "sleepRandom": 6000,
      "hostHeaders": ["delta", "echo"],
      "hostHeaderRotation": { "type": "RANDOM", "counter": 2, "unit": "TRIES" }
    }
  ],
  "getUri": "/GIVEITTOME",
  "postUri": "/NOWIGIVETOYOU",
  "stagedUri": "/candy",
  "stagedUriPayloadId": "payloadId",
  "fileStorageUri": "/files/",
  "metadataCookieName": "PHPSESSID",
  "metadataPrefix": "ABC",
  "metadataSuffix": "XYZ",
  "startTime": "2023-04-10T11:02:09Z",
  "headers": [
    { "name": "Extra-Header-Name", "value": "Extra-Header-Value" }
  ],
  "instantResponses": false,
  "sleep": 1500
}

How the rotation works:

  1. Agent starts with connection to localhost using host header alpha
  2. For the localhost callback group:
    • Host rotation occurs after 5 minutes of failed connections
    • Host headers rotate every 30 seconds between alpha, bravo, and charlie
  3. For the 192.168.1.100 callback group:
    • Host rotation occurs after 3 failed connection attempts
    • Host headers randomly switch between delta and echo with 50% probability before each request

Info

Host rotation and header rotation operate independently. A successful connection resets the host rotation counter, while header rotation continues according to its own rules.


Custom User-Agent Header

By default, agents use a built-in User-Agent string. Override it using the headers array:

1
2
3
"headers": [
  { "name": "User-Agent", "value": "not-curl" }
]

Result: The first request uses the default User-Agent. After the configuration is applied, subsequent requests use the custom value:

1
2
3
4
5
6
7
8
GET /get HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Cookie: JWT_SESSION=<...metadata...>
Host: 172.16.235.129:8888

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 0
1
2
3
4
5
6
7
8
GET /get HTTP/1.1
User-Agent: not-curl
Cookie: JWT_SESSION=<...metadata...>
Host: 172.16.235.129:8888

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 0

filename Template Functions

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.
{rand_str_X_Y} Random mixed-case alphanumeric string with an inclusive length between X and Y; each bound is capped at 64.
{rand_int_X} Random integer from 0 (inclusive) to X (exclusive).
{rand_int_X_Y} Random integer from X (inclusive) to Y (exclusive).
{ext} File extension of the requested payload, including the dot (e.g. .exe, .dll).
{payload_id} Payload ID of the requested payload.

Example:

"filename": "{rand_str_10}-Little-{rand_str_9_20}-Agent-{rand_int_9_2000000000}-With-{payload_id}-{ext}"

Result:

> GET /payload?payloadId=4 HTTP/1.1
> Host: skippy.and.joe
> User-Agent: curl/1337.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
< Content-Disposition: attachment; filename="A9JfMQRwq8-Little-guhRgWYRqV-Agent-391964415-With-4-.exe"
< Content-Length: 770560