Skip to content

Reverse DNS Listener for Built Agents

The DNS Listener establishes a DNS server on a specified port, managing data traffic with agents through DNS A & TXT requests. This provides very discreet communication channels between the C2 infrastructure and agents, blending with regular DNS traffic.

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


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, which explicitly uses port 53, an A-record callback for cool.tuoni.localhost, a 5-second sleep with up to 5 seconds of jitter, and request limits of 16 requests and 32 IPs.

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 redirector sits in front of the listener.
requestType string Yes Nonfunctional Type of DNS requests: A or TXT. Although the JSON schema permits omission, a listener without it rejects both request types, so hand-written configurations must supply it.
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.

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 dnsCallbacks[].sleep.
sleepRandom int No 0 Nonnegative global jitter in seconds added to the sleep time. Can be overridden per callback group via dnsCallbacks[].sleepRandom. The named default example uses 5.

Request Limits

Parameter Type Required If omitted Description
maxRequestPerCycle int No Agent uses 4 Maximum number of DNS requests per one sleep cycle. The named default example uses 16.
maxIpsPerResponse int No Agent uses 4; server cap uses 32 For A type requests, maximum number of IP addresses per response. The named default example uses 32.

Callback Groups (dnsCallbacks[])

dnsCallbacks is required. Each callback group defines a nonempty set of valid domains the agent uses for DNS communication, along with independent sleep and rotation settings. Data is hidden in subdomain names of the configured domains.

Parameter Type Required If omitted Description
domains[] string[] Yes Invalid One or more valid base domain names to use. Agent data is encoded as subdomain labels of these domains.
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.
resolver string No System resolver Hardcoded endpoint for DNS requests.
domainsRotation object No No rotation rule Rotation rules for domain selection. See Rotation Rules.

Rotation Rules

Rotation rules define when the agent should switch to the next domain. This is used for resilience (failover to backup domains) and evasion (rotating through different domain names).

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 domain 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 domain 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 domains 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 mirrors the HTTP listener's approach, with reduced complexity. The HTTP listener supports rotation of both the host and the Host header; by contrast, the DNS listener can only rotate the domain name. For additional detail on the rotation model, see the HTTP listener documentation.

Counter independence

Each callback group maintains its own rotation counter independently. A successful connection resets the FAILOVER counter for that group but does not affect other groups.


Configuration Scenarios

Scenario 1 - Basic DNS Beaconing

The minimal configuration for an agent communicating over DNS. Suitable for lab environments or testing.

{
  "port": 53,
  "requestType": "TXT",
  "sleep": 30,
  "sleepRandom": 10,
  "instantResponses": false,
  "maxRequestPerCycle": 5,
  "maxIpsPerResponse": 3,
  "dnsCallbacks": [
    {
      "domains": ["c2.example.com"],
      "sleep": 30,
      "sleepRandom": 10
    }
  ]
}

Scenario 2 - Multi-Domain Failover

The agent rotates through multiple domains when connectivity fails. Designed for resilient operations where individual domains may get blocked or sinkholed.

{
  "port": 53,
  "requestType": "TXT",
  "sleep": 60,
  "sleepRandom": 20,
  "instantResponses": false,
  "maxRequestPerCycle": 3,
  "maxIpsPerResponse": 3,
  "dnsCallbacks": [
    {
      "domains": [
        "primary.example.com",
        "backup.example.com",
        "tertiary.example.com"
      ],
      "sleep": 60,
      "sleepRandom": 20,
      "resolver": "8.8.8.8",
      "domainsRotation": {
        "type": "FAILOVER",
        "counter": 3,
        "unit": "TRIES"
      }
    }
  ]
}

How it works:

  1. Agent sends DNS queries with data encoded as subdomains of primary.example.com
  2. After 3 consecutive failures, it switches to backup.example.com
  3. After 3 more consecutive failures, it moves to tertiary.example.com
  4. The cycle repeats - the agent keeps retrying all domains indefinitely
  5. A successful connection resets the failure counter for that domain

Tip

For maximum resilience, register domains across different registrars and TLDs. Combine with long sleep intervals to reduce detection surface during low-activity periods.


Scenario 3 - Periodic Domain Rotation

The agent periodically rotates through domains regardless of success, making traffic patterns harder to fingerprint.

{
  "port": 53,
  "requestType": "A",
  "sleep": 45,
  "sleepRandom": 15,
  "instantResponses": false,
  "maxRequestPerCycle": 4,
  "maxIpsPerResponse": 5,
  "dnsCallbacks": [
    {
      "domains": [
        "alpha.example.com",
        "bravo.example.com"
      ],
      "sleep": 45,
      "sleepRandom": 15,
      "domainsRotation": {
        "type": "ROTATE",
        "counter": 10,
        "unit": "TRIES"
      }
    }
  ]
}

Info

With ROTATE type, the agent switches domains every 10 requests whether they succeed or fail. This creates a more varied traffic pattern compared to FAILOVER, which only switches on failures.