Feb 5, 2026·5 min read·20 visits
Authenticated operators on a Sliver C2 server (v1.6.10 and below) can read arbitrary files on the host system by using directory traversal sequences (`../`) in the `WebsiteAddContent` RPC. Fixed in v1.6.11.
Bishop Fox Sliver, a premier command and control framework, suffers from an authenticated path traversal vulnerability in its website management subsystem. By manipulating the logical path of hosted content, an operator can coerce the server into reading arbitrary files from the host filesystem. This turns the hunters into the hunted, allowing low-privileged operators to exfiltrate sensitive server configurations, SSH keys, or system credentials.
Sliver is the darling of the modern red team arsenal. Written in Go, supporting multi-player operations, and packed with features, it's the tool used to manage implants inside compromised networks. We usually think of the C2 server as the fortress—the unassailable base of operations. But what happens when the fortress has a back door unlocked from the inside?
This vulnerability (GHSA-2286-HXV5-CMP2) isn't an exploit against the implants or the victims. It's a fratricidal flaw in the C2 server itself. It allows any authenticated operator—even one with restricted permissions—to read files off the server's disk.
Imagine a scenario where a Blue Team captures a low-level operator's client configuration. Normally, they might just kick the operator or see some logs. With this bug, they can pivot from that stolen config to reading /etc/shadow on your infrastructure. It is a classic case of "trusted input" going rogue.
The vulnerability lies in the Website Content Subsystem. Sliver allows operators to host static content (like phishing pages or payload delivery sites) directly from the C2 server. When an operator adds content, they specify a Path (e.g., /index.html) and the raw bytes.
The logic flaw here is a textbook misuse of Golang's path/filepath package. When the server needed to retrieve this content later, it didn't look up a database ID or a hash; it looked up the file on disk using the path provided by the user.
Specifically, the server took the root directory for web content and simply joined it with the user-supplied path using filepath.Join. While filepath.Join cleans up dirty paths, it does not sandbag them against directory traversal if the resulting path is syntactically valid. If you ask for ../../etc/passwd, the server happily resolves the path relative to the web root, realizes it points outside, and—crucially—proceeds to read it anyway.
Let's look at the smoking gun in server/db/models/website.go. In the vulnerable versions, the server reconstructed the file path blindly using the user's input stored in webcontent.Path.
Vulnerable Code:
// The server blindly joins the base directory with the user-controlled Path
contents, _ := os.ReadFile(filepath.Join(webContentDir, webcontent.Path))If webcontent.Path is ../../../../etc/shadow, the code effectively runs os.ReadFile("/root/.sliver/web/../../../../etc/shadow"). Since the Sliver server often runs with high privileges (sometimes root, though it shouldn't), this reads the system's shadow file.
The Fix (Commit 818127349ccec812876693c4ca74ebf4350ec6b7):
The Bishop Fox team fixed this by decoupling the logical path (the URL) from the physical path (the file on disk). Instead of using the user's string as a filename, they now use the database's internal UUID for the content.
// The server now uses a generated UUID, which cannot contain traversal characters
contents, err := os.ReadFile(filepath.Join(webContentDir, webcontent.ID.String()))Now, even if the user sets the path to ../../etc/passwd, the server ignores it during file retrieval and looks for a file named 550e8400-e29b-41d4-a716-446655440000 (or similar UUID) inside the web content directory. Simple, elegant, and secure.
Exploiting this requires an authenticated mTLS connection to the Sliver server. This means you need a valid operator config file (.cfg). Once connected, we abuse the gRPC interface.
The Attack Chain:
WebsiteAddContent request. We set the Name to a dummy website and, critically, we set the Path to a traversal string like ../../../../../../etc/hosts.Website RPC to request details about our site. The server iterates through the content entries. When it hits our malicious entry, it executes the vulnerable os.ReadFile logic./etc/hosts from the host OS, packages the bytes into the Protobuf response, and sends it back to our client.Here is a snippet of how a researcher might construct the traversal path in Go:
// Calculate traversal depth dynamically or just spam ../
targetPath := "/etc/passwd"
webPath := "../../../../../../../../" + strings.TrimPrefix(targetPath, "/")
// Send the malicious gRPC call
rpc.WebsiteAddContent(ctx, &clientpb.WebsiteAddContent{
Name: "exploit-site",
Contents: map[string]*clientpb.WebContent{
webPath: {
Path: webPath,
ContentType: "text/plain",
Content: []byte("irrelevant"),
},
},
})You might argue, "If I have an operator config, I already own the C2." Not necessarily. Sliver supports multi-user environments with different roles. This vulnerability collapses those boundaries.
The Real Danger:
/root/.ssh/id_rsa or /etc/shadow allows the attacker to SSH into the C2 server itself, gaining full root shell access outside the Sliver environment./etc/hosts or network config files.This is a classic privilege escalation: from "Application User" to "System Administrator".
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Sliver Bishop Fox | < 1.6.11 | 1.6.11 |
| Attribute | Detail |
|---|---|
| CVE ID | N/A (GHSA-2286-HXV5-CMP2) |
| CVSS v3.1 | 6.5 (Medium) |
| Attack Vector | Network (Authenticated gRPC) |
| CWE | CWE-22 (Path Traversal) |
| Impact | Arbitrary File Read |
| Privileges Required | Low (Operator) |
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')