Skip to content

Listeners

laptop tester edited this page Sep 6, 2026 · 2 revisions

Listeners

All listener implementations live in teamserver/pkg/handlers/. Constants (types.go):

LISTENER_HTTP     = 1
LISTENER_PIVOT_SMB = 2
LISTENER_EXTERNAL = 3
LISTENER_SERVICE  = 4

AGENT_HTTPS     = "Https"
AGENT_HTTP      = "Http"
AGENT_EXTERNAL  = "External"
AGENT_PIVOT_SMB = "Smb"

HTTP / HTTPS (http.go, type HTTP, NewConfigHttp())

  • Start() registers POST /*endpoint (agent requests) and GET /*endpoint (fake 404) on a dedicated gin engine and serves it on GetInterfaceIpv4Addr(HostBind) + ":" + PortBind.
  • If Secure is set, a self-signed RSA cert is generated for the bind IP (stored under data/loot/<ts>/listener/<Name>/server.crt|.key) unless the profile supplies Cert.Cert/Cert.Key; served with ListenAndServeTLS.
  • request(ctx):
    1. Reads the POST body.
    2. Determines the agent's external IP (X-Forwarded-For when TrustXForwardedFor, else RemoteAddr). With TrustXForwardedFor, lenient BehindRedir parsing is enabled: multi-hop X-Forwarded-For chains left by redirector relays are handled (http.go:105-116).
    3. Validates the profile's Headers (case-insensitive, ignoring Connection/Accept-Encoding), Uris, UserAgent, and, if HostHeader is configured, the request's Host / X-Forwarded-Host. Any mismatch → fake nginx 404 page (handlers/404.html, sets X-Havoc: true).
    4. Applies Response.Headers, calls parseAgentRequest(...), and writes the returned raw bytes as the response body.
  • Only POST builds: requesting an HTTP listener payload with Method = "get" fails at build time with "GET method is not supported" (teamserver/pkg/common/builder/builder.go:914-917).
  • Hosts entries may be host:port pairs; the per-host port overrides PortConn for that callback host (builder.go:932-952).
  • Stop() does a graceful http.Server.Shutdown (5s timeout).

SMB pivot (smb.go, type SMB, NewPivotSmb())

The SMB listener opens no socket on the teamserver. It is a named-pipe pivot channel through an existing agent (SMBConfig{ Name, PipeName, KillDate, WorkingHours }). Start() only logs and emits the ListenerAdd event. Pivot tasking is encoded via COMMAND_PIVOT (2520) / DEMON_PIVOT_SMB_* sub-commands and links are persisted in the TS_Links table. This channel is Demon-only.

External (external.go, type External, NewExternal())

ExternalConfig{ Name, Endpoint }. ListenerStart wires an Endpoint{ Endpoint, Function: ExtConfig.Request } into the teamserver's gin engine (POST /<Endpoint>). Request behaves like the HTTP handler minus the header/URI/user-agent validation: the body goes straight into parseAgentRequest, so external C2 integrations can shape traffic however they like.

External listeners can be created two ways:

  1. Statically via the profile (Listeners.External { Name, Endpoint }).
  2. Dynamically through the Service API (Listener/ListenerAddExC2), used by third-party controllers; see Service API Reference.

Shared agent-request handling (handlers.go)

parseAgentRequest(Teamserver, Body, ExternalIP) (bytes.Buffer, bool) is the single funnel for all implant traffic:

  1. agent.ParseHeader(Body)Header{ Size, MagicValue, AgentID, Data } (12-byte big-endian header + body).
  2. MagicValue == 0xDEADBEEFhandleDemonAgent (Demon crypto + job dispatch).
  3. Otherwise → handleServiceAgent: look up the registered third-party agent by magic value (ServiceAgentExist/ServiceAgent) and forward the body to its controller (the Service API connection that registered it); the controller's reply becomes the HTTP response.

Clone this wiki locally