Skip to content

HTTP/HTTPS Listeners

HTTP and HTTPS are the primary C2 transports for Stentor operations. They provide reliable, high-bandwidth communication that blends with normal web traffic. HTTPS is the recommended default for all engagements; HTTP should only be used in controlled lab environments where TLS overhead is unnecessary.


Architecture

The HTTP/HTTPS transport chain connects the implant on the target to the Stentor backend through the relay:

sequenceDiagram
    participant Implant as Implant (Windows)
    participant Relay as Relay C2 Server (Kali)
    participant Backend as Backend API Server

    Implant->>Relay: HTTPS request (beacon check-in)
    Relay->>Backend: WebSocket event (beacon registration)
    Backend-->>Relay: WebSocket command (task queue)
    Relay-->>Implant: HTTPS response (task payload)
    Implant->>Relay: HTTPS request (task result)
    Relay->>Backend: WebSocket event (result delivery)

The relay terminates TLS and handles the HTTP C2 protocol. The backend manages state (beacon registry, task queue) and the operator UI. All operator interactions flow through the backend API, which dispatches commands to the relay over a persistent WebSocket connection.


Creating an HTTP/HTTPS Listener

Listeners are created via the REST API and start in stopped status. You must explicitly start a listener before it accepts connections.

Create an HTTPS Listener

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Primary",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 8443,
    "tls_auto_generate": true
  }' | jq

Create an HTTP Listener

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTP Lab",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "http",
    "port": 8080
  }' | jq

HTTP is unencrypted

HTTP listeners transmit C2 traffic in plaintext. Only use HTTP in isolated lab environments where network monitoring is not a concern. For all production engagements, use HTTPS.

Start, Stop, and Restart

After creating a listener, start it to begin accepting connections:

curl -s -X POST https://stentor.app/api/v1/listeners/$LISTENER_ID/start \
  -H "Authorization: Bearer $TOKEN" | jq '.status'
curl -s -X POST https://stentor.app/api/v1/listeners/$LISTENER_ID/stop \
  -H "Authorization: Bearer $TOKEN" | jq '.status'
curl -s -X POST https://stentor.app/api/v1/listeners/$LISTENER_ID/restart \
  -H "Authorization: Bearer $TOKEN" | jq '.status'

Listener lifecycle

Newly created listeners always have status stopped. The start endpoint transitions to running. If the relay encounters an error binding the port, the status changes to error with details in the error_message field.


Configuration Options

The table below lists all fields relevant to HTTP and HTTPS listeners. Fields marked required must be provided when creating a listener.

Field Type Default Description
name string -- Required. Display name for the listener.
type string -- Required. "http" or "https".
relay_id string -- Required. UUID of the relay that will host this listener.
port int -- Required. TCP port the relay binds to.
bind_address string "0.0.0.0" Network interface to bind. Use "127.0.0.1" to restrict to localhost.
beacon_port int null Separate port for payload generation. When set, port is the relay bind port and beacon_port is embedded in generated payloads. See Beacon Port Separation.
tls_cert string null PEM-encoded TLS certificate. Only used when type is "https".
tls_key string null PEM-encoded TLS private key. Only used when type is "https".
tls_auto_generate bool true Auto-generate a self-signed certificate if no tls_cert/tls_key provided.
profile_name string null Malleable C2 profile name for HTTP traffic customization.
profile_variant string null Malleable profile variant (e.g., "jquery", "amazon").
host_rotation string null Host rotation strategy: "round-robin", "random", or "failover".
max_retry_strategy string null Retry behavior when all hosts fail: "exit", "rotate", or "sleep".
c2_hosts string null Comma-separated list of callback hosts for rotation.
proxy_type string null Proxy protocol: "http" or "socks".
proxy_host string null Proxy server hostname or IP.
proxy_port int null Proxy server port.
proxy_user string null Proxy authentication username.
proxy_pass string null Proxy authentication password. Not returned in API responses.
front_domains string null Comma-separated list of CDN front domains for domain fronting.
guardrails JSON null Beacon filtering rules. See Guardrails.

TLS Certificate Management

HTTPS listeners require a TLS certificate and private key. Stentor provides two options:

Auto-Generated Certificates (Default)

When tls_auto_generate is true (the default) and no tls_cert/tls_key are provided, the relay generates a self-signed certificate at startup. This is the fastest way to get an HTTPS listener running.

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Auto-TLS",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "tls_auto_generate": true
  }' | jq

Custom Certificates

For production engagements, provide your own certificates (Let's Encrypt, internal CA, or purchased certificates). Set tls_auto_generate to false and supply PEM-encoded tls_cert and tls_key:

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Production",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "tls_auto_generate": false,
    "tls_cert": "-----BEGIN CERTIFICATE-----\nMIID...your cert...\n-----END CERTIFICATE-----",
    "tls_key": "-----BEGIN PRIVATE KEY-----\nMIIE...your key...\n-----END PRIVATE KEY-----"
  }' | jq

Use legitimate certificates for production

Self-signed certificates trigger browser warnings and are easily detected by network monitoring. For production engagements, use Let's Encrypt (free), certificates from your organization's internal CA, or purchased certificates that match your cover domain.


Domain Fronting

Domain fronting exploits the difference between the DNS/TLS SNI hostname and the HTTP Host header to route C2 traffic through a CDN, making it appear as legitimate traffic to the CDN provider's domain.

How it works:

  1. The implant connects to a CDN edge server (e.g., cdn.cloudfront.net) via TLS
  2. The TLS SNI shows the CDN domain -- normal traffic
  3. The HTTP Host header inside the TLS tunnel contains the actual C2 domain
  4. The CDN routes the request to your relay based on the Host header

Configuration:

Set the front_domains field to a comma-separated list of CDN front domains:

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Fronted",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "front_domains": "d1234567.cloudfront.net,cdn.azureedge.net"
  }' | jq

CDN provider restrictions

Many CDN providers (AWS CloudFront, Azure CDN, Google Cloud CDN) have updated their policies to prevent domain fronting. Test your setup before relying on it in production. Some providers actively block requests where the SNI and Host header do not match.


HTTP Proxy Configuration

When implants operate behind a corporate proxy, configure the listener with proxy settings so generated payloads include the correct proxy configuration:

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS via Proxy",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "proxy_type": "http",
    "proxy_host": "proxy.corp.local",
    "proxy_port": 8080,
    "proxy_user": "svc_account",
    "proxy_pass": "proxy_password"
  }' | jq
Field Description
proxy_type "http" for HTTP CONNECT proxy, "socks" for SOCKS⅘ proxy
proxy_host Hostname or IP of the proxy server
proxy_port Port of the proxy server
proxy_user Username for proxy authentication (optional)
proxy_pass Password for proxy authentication (optional, never returned in API responses)

Proxy settings in payloads

Proxy configuration is embedded into generated payloads. The implant uses these settings to route all C2 traffic through the specified proxy. This is essential in environments where direct internet access is blocked.


Host Rotation

Host rotation allows beacons to cycle through multiple callback addresses, improving resilience if one host is blocked or goes down.

Field Description
c2_hosts Comma-separated list of callback hosts (e.g., "c2-1.example.com,c2-2.example.com,10.0.0.50")
host_rotation Strategy for cycling hosts: "round-robin" (sequential), "random" (random selection), "failover" (next on failure)
max_retry_strategy Behavior when all hosts fail: "exit" (terminate beacon), "rotate" (restart cycle), "sleep" (extended sleep then retry)

Example -- HTTPS listener with host rotation:

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Resilient",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "c2_hosts": "c2-primary.example.com,c2-backup.example.com,10.0.0.50",
    "host_rotation": "failover",
    "max_retry_strategy": "sleep"
  }' | jq

Failover strategy

Use "failover" with "sleep" for long-term persistence. The beacon tries each host in order, and if all fail, sleeps for an extended period before restarting the cycle. This prevents rapid connection attempts that could alert defenders.


Beacon Port Separation

The beacon_port field separates the port the relay binds to (port) from the port embedded in generated payloads (beacon_port). This is essential when a reverse proxy or load balancer sits in front of the relay.

Use case: A reverse proxy (nginx, HAProxy) listens on port 443 and forwards traffic to the relay on port 8443. Without beacon_port, payloads would tell implants to connect to port 8443, bypassing the proxy.

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Behind Proxy",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 8443,
    "beacon_port": 443
  }' | jq
Field Purpose
port Where the relay process binds (e.g., 8443). Internal to the relay host.
beacon_port What goes into generated payloads (e.g., 443). The port implants connect to.

When beacon_port is not set, port serves as both the bind port and the payload callback port (backward-compatible behavior).


Guardrails

Guardrails are per-listener beacon filtering rules that restrict which beacons the listener accepts. Beacons that do not match the guardrail patterns are rejected at check-in.

JSON schema:

{
  "ip": "10.10.10.*",
  "hostname": "DC*",
  "username": "admin*",
  "domain": "corp.local",
  "server_name": "FILE*"
}

All fields are optional. Wildcard patterns (*) are supported. A beacon must match all specified fields to be accepted. Fields not specified are not checked (accept any value).

Example -- restrict to a specific subnet and domain:

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS Scoped",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "guardrails": {
      "ip": "10.10.10.*",
      "domain": "corp.local"
    }
  }' | jq

Test guardrails before production

Overly restrictive guardrails can block legitimate beacons. Test your patterns in a lab environment first. A beacon rejected by guardrails will not appear in the cockpit and will not generate any alerts -- it is silently dropped.


Hosting Files

HTTP/HTTPS listeners can serve arbitrary files at custom URIs. This is useful for hosting payloads, scripts, decoy documents, or landing pages directly on the C2 server.

Host a File

curl -s -X POST https://stentor.app/api/v1/listeners/$LISTENER_ID/host \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "uri": "/downloads/update.exe",
    "content_type": "application/octet-stream",
    "filename": "update.exe",
    "data": "TVqQAAMAAAAEAAAA..."
  }' | jq
Field Type Description
uri string Required. Path to serve the file at (must start with /).
content_type string MIME type. Auto-detected from filename extension if omitted.
filename string Original filename (used for content-type detection and Content-Disposition).
data string Required. Base64-encoded file content.

Remove a Hosted File

curl -s -X DELETE "https://stentor.app/api/v1/listeners/$LISTENER_ID/host?uri=/downloads/update.exe" \
  -H "Authorization: Bearer $TOKEN" | jq

URI restrictions

Hosted file URIs cannot start with /api/ or /c2/ -- these prefixes are reserved for C2 protocol routes. Files are served with priority over the C2 handler, so hosted URIs take precedence for matching paths.


Malleable Profile Integration

The profile_name and profile_variant fields attach a malleable C2 profile to the listener, customizing all HTTP traffic indicators including URIs, headers, parameters, body encoding, and User-Agent strings. This transforms beacon traffic to mimic legitimate web applications (jQuery CDN requests, Amazon shopping, Microsoft Office updates, etc.).

curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HTTPS jQuery Profile",
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "type": "https",
    "port": 443,
    "profile_name": "jquery",
    "profile_variant": "jquery"
  }' | jq

For full documentation on malleable profile syntax, traffic transforms, and available profiles, see the Malleable Profiles page.