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
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. |
requestType |
string | Yes | - | Type of DNS requests: A or TXT. |
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. |
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 dnsCallbacks[].sleep. |
sleepRandom |
int | No | 0 |
Global jitter in seconds added to the sleep time. Can be overridden per callback group via dnsCallbacks[].sleepRandom. |
Request Limits
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
maxRequestPerCycle |
int | No | - | Maximum number of DNS requests per one sleep cycle. |
maxIpsPerResponse |
int | No | - | For A type requests, maximum number of IP addresses per response. |
Callback Groups (dnsCallbacks[])
Each callback group defines a set of 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 | Default | Description |
|---|---|---|---|---|
domains[] |
string[] | Yes | - | Array of base domain names to use. Agent data is encoded as subdomain labels of these domains. |
sleep |
int | Yes | - | Base sleep time in seconds for this callback group. |
sleepRandom |
int | No | 0 |
Jitter in seconds for this callback group. |
resolver |
string | No | - | Hardcoded endpoint for DNS requests. If not set, the system-wide resolver is used. |
domainsRotation |
object | No | - | 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 | 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/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 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.
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.
How it works:
- Agent sends DNS queries with data encoded as subdomains of
primary.example.com - After 3 consecutive failures, it switches to
backup.example.com - After 3 more consecutive failures, it moves to
tertiary.example.com - The cycle repeats - the agent keeps retrying all domains indefinitely
- 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.
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.