Repository navigation
Handlers
A handler is the teamserver-side component that actually speaks to implants over a transport. A listener is the configuration + socket ("listen for HTTPS on 443 with these URIs"); the handler is the request-processing logic behind it: validating incoming traffic, parsing agent headers, registering sessions, and returning tasking. Handler implementations live in teamserver/pkg/handlers/ (http.go, smb.go, external.go, and the shared funnel handlers.go).
Every implant request, from any listener type, passes through a single dispatch point, parseAgentRequest (teamserver/pkg/handlers/handlers.go). This is where the framework splits into its two implant sides:
- Requests whose magic value is
0xDEADBEEFare routed tohandleDemonAgent. - This path knows Demon's wire format natively: it parses the Demon header (
teamserver/pkg/agent/agent.go→ParseHeader), decrypts/handles the implant crypto, dispatches task results and check-ins (pkg/agent/demons.go), registers the session, and encrypts queued tasking into the HTTP response. - This is the side documented in Demon Agent (wire format) and Demon Console Commands (operator-facing commands).
- Requests with any other magic value are routed to
handleServiceAgent(handlers.go:339): the teamserver looks up a registered third-party agent by magic value (ServiceAgentExist/ServiceAgent) and forwards the raw request body to that agent's controller over the Service WebSocket; the controller's reply becomes the listener's HTTP response. - Third-party agent types are registered via the Service API (
RegisterAgent), and their listeners can be created withListener/ListenerAddExC2or anExternallistener block in the profile. This is documented in Service API Reference and Building a Third-Party Agent.
If you are building a new implant (Building a Third-Party Agent), you do not modify pkg/handlers at all. You implement a controller that:
- Registers with the teamserver over the Service WebSocket and declares an arbitrary magic value.
- Receives whatever bytes arrive at your
Externallistener endpoint (validation-free, you control traffic shaping) viaAgent/AgentResponse. - Decodes/encodes your own agent protocol inside those bytes and tasks your implant, replying with
Agent/AgentResponsecarrying the sameRandID(the teamserver blocks up to 5 minutes, then fails).
The Demon side is a special case of this architecture with the controller logic built into the teamserver itself.
| Type | Constant | File | Socket on teamserver? |
|---|---|---|---|
| HTTP/S | LISTENER_HTTP 1 |
http.go |
Yes, gin engine on HostBind:PortBind (POST = agent traffic, GET = fake 404) |
| SMB pivot | LISTENER_PIVOT_SMB 2 |
smb.go |
No, named-pipe channel relayed through a parent agent (Demon-only) |
| External | LISTENER_EXTERNAL 3 |
external.go |
POST /<Endpoint> on the teamserver's gin engine, validation-free |
| Service | LISTENER_SERVICE 4 |
n/a | Registered dynamically by controllers over the Service API (ListenerAddExC2) |
Per-listener options and validation details are in Listeners; profile fields in Profile Reference.
pkg/handlers parses network-controlled data. Bounds checks and the HostHeader / Headers / Uris / UserAgent validation (mismatch → fake nginx 404, handlers/404.html) are load-bearing; preserve them when modifying. The same applies to pkg/agent/agent.go (ParseHeader) on the Demon side and to the Service API path for third-party agents.