GHSA-hx52-cv84-jr5v Sliver is Vulnerable to Authenticated Nil-Pointer Dereference through its Handlers
1. Executive SummaryA vulnerability exists in the Sliver C2 server's Protobuf unmarshalling logic due to a systemic lack of nil-pointer validation. By extracting valid implant credentials and omitting nested fields in a signed message, an authenticated actor can trigger an unhandled runtime panic. Because the mTLS, WireGuard, and DNS transport layers lack the panic recovery middleware present in the HTTP transport, this results in a global process termination. While requiring post-authentication access (a captured implant), this flaw effectively acts as an infrastructure "kill-switch," instantly severing all active sessions across the entire fleet and requiring a manual server restart to restore operations.2. Vulnerability Details2.0 Technical Workflow: From Envelope to HandlerSliver encapsulates all C2 traffic in a generic `sliverpb.Envelope`, which acts as a routing wrapper. When the server receives an Envelope with `Type = 53` (MsgBeaconRegister), the internal router strips the envelope and passes the raw `Data` bytes directly to the vulnerable `handlers.beaconRegisterHandler(implantConn, data)`. This flow is consistent across all transports, but the **error handling** of the transport itself determines the final impact.2.1 BeaconRegister Nil-Pointer Dereference**Vulnerability Type:** Remote Denial of Service via Nil-Pointer Dereference ([CWE-476](https://cwe.mitre.org/data/definitions/476.html))**Component:** `server/handlers/beacons.go`**Affected Functions:** [beaconRegisterHandler](https://github.com/BishopFox/sliver/blob/master/server/handlers/beacons.go#L46-L110).**Severity:** Critical**Complexity:** LowRoot Cause AnalysisThe core of the vulnerability lies in the architectural handling of Protobuf messages within the Go runtime. In `proto3`, all fields are optional by design. When a message contains a nested sub-message (like `Register` inside `BeaconRegister`), the Go Protobuf implementation represents this sub-message as a **pointer**.In `server/handlers/beacons.go`, the server unmarshals the incoming data without subsequent validation of its nested structures:If an attacker constructs a `BeaconRegister` message and deliberately omits the `Register` field, `proto.Unmarshal` parses the stream without error but leaves the `Register` pointer as `nil`. The subsequent attempt to access `beaconReg.Register.Uuid` triggers a **Nil-Pointer Dereference**.2.2 Expanded Inventory: System-Wide Nil-Pointer VulnerabilitiesBeyond the beacon registration, the investigation revealed a systemic pattern of missing nil-checks across various handlers. These vulnerabilities follow the same root cause: immediate dereferencing of nested Protobuf fields post-unmarshalling.2.2.1 Remote Implant Vectors (Unauthenticated)These handlers process data from implants. If an implant binary is captured, these can be triggered to crash the server:**Reverse Tunneling (`server/handlers/sessions.go`):** The `createReverseTunnelHandler` panics when `req.Rportfwd` is omitted.**SOCKS Proxying (`server/handlers/sessions.go`):** The `socksDataHandler` fails when the `SocksData` sub-message is absent.**Pivot/Peer Communication (`server/handlers/pivot.go`):** Functions `serverKeyExchange` and `peersToString` dereference `peerEnvelope.Peers` without checking if the peer list is empty or nil.2.2.2 Authenticated Operator Vectors (gRPC Layer)The Sliver RPC server (`server/rpc/`) is also susceptible. While these require an authenticated operator, they represent a significant stability risk where a malformed request from a custom client or automated script can bring down the entire C2 infrastructure.| Function            | File                            | Vulnerable Pattern                                      |
| ------------------- | ------------------------------- | ------------------------------------------------------- |
| getTimeout          | server/rpc/rpc.go               | `req.GetRequest().Timeout`                              |
| getError            | server/rpc/rpc.go               | `resp.GetResponse().Err`                                |
| Portfwd             | server/rpc/rpc-portfwd.go       | `req.Request.SessionID`                                 |
| GetSystem           | server/rpc/rpc-priv.go          | `req.GetRequest().SessionID`                            |
| GetPrivileges       | server/rpc/rpc-priv.go          | `req.Request.SessionID`                                 |
| NetConnPivot        | server/rpc/rpc-pivot.go         | `req.Request.SessionID`                                 |
| PivotListeners      | server/rpc/rpc-pivot.go         | `req.Request.SessionID`                                 |
| SocksStart          | server/rpc/rpc-socks.go         | `req.Request.SessionID`                                 |
| SocksStop           | server/rpc/rpc-socks.go         | `req.Request.SessionID`                                 |
| RPortfwd            | server/rpc/rpc-rportfwd.go      | `req.Request.SessionID`                                 |
| Shell               | server/rpc/rpc-shell.go         | `req.Request.SessionID`                                 |
| ShellResize         | server/rpc/rpc-shell.go         | `req.Request.SessionID`                                 |
| BackdoorImplant     | server/rpc/rpc-backdoor.go      | `req.Request.SessionID`, `req.Request.Timeout`          |
| CrackstationTrigger | server/rpc/rpc-crackstations.go | `statusUpdate.HostUUID` (after unmarshal of `req.Data`) |
| Tasks               | server/rpc/rpc-tasks.go         | `req.Request.SessionID`                                 |
| ImplantReconfig     | server/rpc/rpc-reconfig.go      | `req.Request.SessionID`                                 |
| MsfInject           | server/rpc/rpc-msf.go           | `req.Request.SessionID`                                 |
| Hijack              | server/rpc/rpc-hijack.go        | `req.Request.SessionID`                                 |3. Proof of Concept & Attack Feasibility3.1 Attack Feasibility: Credential ExtractionThe exploit requires valid implant credentials, which are inherently embedded in Sliver's generated binaries. Since these binaries are often deployed to untrusted or compromised environments, credential recovery is a high-probability event. During testing, it was confirmed that an attacker can obtain the required mTLS certificates and Age Secret Keys through:**Static Extraction (Trivial):** By default, running the `strings` utility on the implant binary or dumping the embedded configuration block is sufficient to recover the private keys.**Memory Forensics:** If an implant is captured during execution, the configuration structures can be carved directly from the process memory, bypassing most disk-level obfuscation.3.2 Exploit Execution FlowThe provided exploit [mtls_poc.go](https://github.com/skoveit/Sliver-Nil-Pointer-DoS-PoC/blob/main/mtls_poc.go) or [mtls_poc.go](https://gist.github.com/skoveit/a5e52b5c9197fc53e2605a861cd8aa33) demonstrates how a single captured implant can be weaponized into a "Kill Switch" for the entire C2 infrastructure. The attack follows these steps:**Authentication:** Establishes a valid mTLS connection using the extracted certificates.**Multiplexing:** Negotiates a Yamux stream, bypassing standard network-level protections.**Payload Construction:** Builds a `BeaconRegister` Protobuf message where the `ID` is defined, but the critical `Register` sub-message is explicitly omitted (set to `nil`).**Envelope Signing:** Deterministically signs the malicious envelope using the recovered Age private key to ensure it is accepted by the server.**Trigger:** Sends the malformed payload. Upon receipt, the server's handler attempts to dereference the missing `Register` pointer, leading to an immediate **Full Server DoS**.4. Transport-Specific Response & Recovery AnalysisThe impact of this panic varies significantly depending on the C2 transport used by the implant. While the nil-pointer dereference happens in the shared handler logic, the transport layer determines whether this results in a localized request failure or a total server termination.4.1 HTTP/S TransportHTTP-based beacons do **not** crash the entire Sliver server. This is because Sliver utilizes the standard Go `net/http` library.**Code Reference ([server/c2/http.go](https://github.com/BishopFox/sliver/blob/master/server/c2/http.go)):**By design, `net/http`'s `ServeHTTP` implementation wraps every connection in a `defer recover()` block. When the [beaconRegisterHandler](https://github.com/BishopFox/sliver/blob/master/server/handlers/beacons.go#L46-L109) panics, the standard library catches it, logs the trace, and simply closes that specific TCP connection. The rest of the server remains unaffected.4.2 mTLS & WireGuard Transports (Full DoS)Both mTLS and WireGuard utilize the `yamux` multiplexer to handle multiple streams over a single connection. Unlike the HTTP server, Sliver manually manages these goroutines without a global recovery mechanism.**mTLS [server/c2/mtls.go](https://github.com/BishopFox/sliver/blob/master/server/c2/mtls.go):****WireGuard [server/c2/wireguard.go](https://github.com/BishopFox/sliver/blob/master/server/c2/wireguard.go):**Because these handlers are invoked in a **raw goroutine** without a `recover()` block, the panic propagates to the top of the stack, causing the entire Go runtime to exit (SIGSEGV). This kills the `sliver-server` process immediately.4.3 DNS Transport (Full DoS)Similar to mTLS, the DNS transport reassembles messages and then forwards them to handlers in unsynchronized goroutines.**DNS [server/c2/dns.go](https://github.com/BishopFox/sliver/blob/master/server/c2/dns.go):**This asynchronous call also lacks a `recover()` block, making DNS sessions equally capable of crashing the entire server.4.4 Vulnerability Matrix by Protocol| Protocol | Uses `recover()`? | Impact of Panic | Server Crash? |
| :--- | :---: | :--- | :---: |
| **HTTP / HTTPS** | Yes (Built-in) | Request Terminated | No |
| **mTLS** | No | Process Termination | **Yes** |
| **WireGuard** | No | Process Termination | **Yes** |
| **DNS** | No | Process Termination | **Yes** |5. Impact AnalysisThe impact of this vulnerability is **Total Operational Paralysis**. Because the panic causes the entire Go runtime to terminate:**Global Disconnection:** Every active session and beacon across all transports (including the resilient HTTP transport) is instantly terminated.**Persistence Risk:** Implants waiting for their next check-in will find the server offline. Repeated failures may trigger internal implant "kill-date" or cleanup logic, or alert defensive monitoring to a failure in the C2 channel.**Operator Eviction:** All active operators are evicted from the gRPC interface, losing all unsaved state, active shell buffers, and real-time monitoring streams.**Operational Downtime:** Restoration requires manual intervention to restart the service and potentially re-establish complex pivot chains, creating a significant "Recovery Time Objective" (RTO) penalty.6. Countermeasures & RemediationAddressing these vulnerabilities requires a systemic shift towards "fail-safe" architecture. The root cause is a combination of unprotected Protobuf pointer dereferences and a lack of isolation in asynchronous transport layers.6.1 Tier 1: Tactical Defensive ProgrammingThe immediate priority is to implement strict validation for all nested Protobuf fields. In Go, omitted sub-messages are `nil` after unmarshaling; handlers must assume any pointer-typed field from an implant is potentially `nil`.Implementation Pattern: Validation-First HandlersHandlers should validate the entire message structure before proceeding to business logic.6.2 Tier 2: Infrastructure Hardening (RPC Global Accessors)To protect the gRPC/Operator interface, the server should deprecate direct access to the [Request](https://github.com/BishopFox/sliver/blob/master/server/core/sessions.go#L148-L185) metadata field in favor of safe accessors that handle missing metadata gracefully.Recommended Helper Update6.3 Tier 3: Strategic Architectural Resilience (Panic Recovery Middleware)To achieve parity with the resilience of the HTTP transport, all multiplexed transports (mTLS, WireGuard, DNS) must implement a supervisor pattern using Go's `recover()` mechanism.Implementation: Protected Handler InvokeAll handlers should be executed inside a "Safe Wrapper" that catches runtime panics, logs the failure, and terminates only the affected stream without crashing the entire C2 daemon.6.4 Tier 4: Long-Term AssuranceThe framework should move away from manual nil-checking towards automated schema validation:**`protoc-gen-validate` (PGV)**: Annotate [.proto](https://github.com/BishopFox/sliver/blob/master/protobuf/dnspb/dns.proto) files with `(validate.rules).message.required = true` and generate automatic validation code.**Static Analysis CI**: Integrate custom linters to detect unprotected pointer dereferences of Protobuf types during the PR process.By adopting this multi-tiered approach, Sliver evolves from a "fail-deadly" design to a robust, enterprise-grade C2 architecture.
CVSS v4.0 score: 2.1