An unauthenticated path to operator APIs
Covenant’s /covenanthub endpoint exposes a listener-creation operation without requiring authentication. Creating a listener also creates a service account and returns its credential. Because the operator APIs’ shared authorization policy accepts an authenticated account without checking its role, that credential crosses from listener access into operator functionality.
An attacker needs network access to the affected Covenant HTTPS entry point, including /covenanthub. No existing account, credentials, privileged role, or user interaction is required. Initial administrator registration is not a prerequisite. The declared affected range is Covenant 0.6 and earlier; the complete chain was demonstrated on v0.6, while earlier versions were not individually tested or compared at source level.
How listener creation issues the credential
A listener is a Covenant component configured to accept network connections. Covenant exposes listener creation through SignalR, a framework that lets clients invoke server methods over a logical connection. Startup.cs maps /covenanthub to the CovenantHub class, but that class lacks the [Authorize] attribute that would require an authenticated caller. The other five hub classes have authorization attributes. In v0.6, CreateHttpListener is the only hub method outside commented-out blocks, making this operation the entry point for the chain.
An unauthenticated client can negotiate a SignalR connection through /covenanthub/negotiate, complete the JSON handshake, and invoke CreateHttpListener. The demonstrated chain used HTTPS with LongPolling, a transport in which the client polls the server for messages. The attacker supplies listener metadata, its GUID identifier, and its status.
The status matters because creating the database record and starting a network listener are separate operations. Selecting ListenerStatus.Uninitialized, represented by Status=0, persists the listener without starting it. StartInitialHttpListener and network port binding occur only for an Active listener. The attacker can therefore obtain the credential without opening an active listener.
Inside CovenantService.CreateHttpListener, the server creates a user with a generated name and assigns the Listener role. It then calls Utilities.GenerateJwtToken and places the resulting credential in the listener’s CovenantToken property. A JSON Web Token (JWT) is a signed credential that carries claims such as a role and an expiration time. This token carries the Listener role, expires after 2,000 days, and is signed with HS256, a symmetric signing algorithm, using the server’s configured JwtKey.
The method returns the listener object, exposing the token as result.covenantToken in the SignalR completion response. The attacker obtains a legitimately server-signed credential without knowing the signing key. The failure is the unauthenticated issuance and disclosure of that credential, rather than a forged signature.
Why a Listener token reaches operator functionality
All 17 API controller classes use Authorize(Policy = RequireJwtBearer). This policy selects JWT bearer authentication and calls RequireAuthenticatedUser(), but imposes no role requirement. A bearer token is a credential presented with a request; possession of an accepted token authenticates its holder. When the attacker supplies the returned token in the Authorization: Bearer header, it satisfies this shared check despite carrying only the Listener role.
The demonstrated requests to GET /api/grunts and GET /api/users succeeded, and the users response disclosed the operator roster. Grunts are Covenant’s managed implants. The grunts response was empty, and the tested instance had no active grunt, so this result establishes access to the endpoint without demonstrating disclosure of implant data or control of an implant.
Three user-management actions have an additional boundary: DeleteUser, CreateUserRole, and DeleteUserRole require RequireAdministratorRole. Their authorization rules indicate that a Listener token cannot access them, although rejection of those requests was not tested. The shared policy weakness should therefore not be read as proof of unrestricted access to every API action.
File writes and conditional impact
The same token also enabled hosted-file creation through POST /api/listeners/{id}/hostedfiles. The attacker supplied a listener identifier, a filename, and file content. ListenerApiController.CreateHostedFile calls HttpListener.HostFile, which uses File.WriteAllBytes to store that content in the selected listener’s Static directory. Control of the listener GUID and filename makes the destination within that directory predictable.
This is a directory-constrained write. Listener GUIDs are limited to alphanumeric characters, and hosted-file paths are checked for directory traversal, which would otherwise allow a path to escape its intended directory. Direct host code execution through this write was not established, and availability impact was not tested.
Additional potential capabilities include reading stored credentials, downloading files, accessing events, issuing tasks to implants, and generating launchers. These capabilities are inferred from the routes and their shared authorization policy; they were not individually demonstrated. Actual disclosure and implant-related effects depend on the target instance’s stored data and implants.
Correcting the access boundaries
The available evidence identifies no upstream fixed version or confirmed patch behavior. At the code level, remediation should require authentication before listener creation and enforce explicit role or permission checks for operator operations, so a Listener credential does not pass solely because it is validly signed. These are engineering recommendations derived from the failed access checks, not confirmed changes in a released fix.
Closing unauthenticated access to the hub alone would not invalidate previously issued tokens or change their 2,000-day expiration. The operator API authorization checks also need to exclude credentials that lack the privileges required for the requested operation.