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/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.