Skip to content

Commit db6b90f

Browse files

File tree

5 files changed

+302
-0
lines changed

5 files changed

+302
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-4wmm-6qxj-fpj4",
4+
"modified": "2026-03-19T12:43:42Z",
5+
"published": "2026-03-19T12:43:42Z",
6+
"aliases": [
7+
"CVE-2026-33238"
8+
],
9+
"summary": "AVideo has a Path Traversal in listFiles.json.php Enables Server Filesystem Enumeration",
10+
"details": "## Summary\n\nThe `listFiles.json.php` endpoint accepts a `path` POST parameter and passes it directly to `glob()` without restricting the path to an allowed base directory. An authenticated uploader can traverse the entire server filesystem by supplying arbitrary absolute paths, enumerating `.mp4` filenames and their full absolute filesystem paths wherever they exist on the server — including locations outside the web root, such as private or premium media directories.\n\n## Details\n\nThe vulnerable code is at `objects/listFiles.json.php:8-45`:\n\n```php\nif (!User::canUpload() || !empty($advancedCustom->doNotShowImportMP4Button)) {\n return false;\n}\n$global['allowed'] = ['mp4'];\n// ...\nif (!empty($_POST['path'])) {\n $path = $_POST['path'];\n if (substr($path, -1) !== '/') {\n $path .= \"/\";\n }\n if (file_exists($path)) {\n $extn = implode(\",*.\", $global['allowed']);\n $filesStr = \"{*.\" . $extn . \",*.\" . strtolower($extn) . \",*.\" . strtoupper($extn) . \"}\";\n $video_array = glob($path . $filesStr, GLOB_BRACE);\n foreach ($video_array as $key => $value) {\n $filePath = mb_convert_encoding($value, 'UTF-8');\n // ...\n $obj->path = $filePath; // Full absolute path returned to caller\n```\n\nThe `$_POST['path']` value is used directly in `glob()` with no call to `realpath()` for normalization and no prefix check against a permitted base directory (e.g., `$global['systemRootPath'] . 'videos/'`). The response includes `obj->path` containing the full absolute filesystem path of each matched file.\n\nThe extension filter (`{*.mp4,*.mp4,*.MP4}`) limits results to `.mp4` files, which prevents reading credentials or source code but does not prevent enumeration of video files stored in access-controlled locations such as:\n- Premium/paid content directories\n- Private or unlisted media stores\n- Backup directories containing `.mp4` files\n- Paths revealing sensitive server directory structure\n\n`canUpload` is a standard low-privilege role granted to any registered uploader; it does not imply administrative trust.\n\n## PoC\n\n```bash\n# Step 1: Authenticate as any user with canUpload permission\n# (standard uploader account)\n\n# Step 2: Enumerate MP4 files in the web root (expected behavior)\ncurl -b \"PHPSESSID=<session>\" -X POST https://target.avideo.site/listFiles \\\n -d \"path=/var/www/html/videos/\"\n# Returns: [{\"id\":0,\"path\":\"/var/www/html/videos/video1.mp4\",\"name\":\"video1.mp4\"}, ...]\n\n# Step 3: Traverse outside intended directory to private content store\ncurl -b \"PHPSESSID=<session>\" -X POST https://target.avideo.site/listFiles \\\n -d \"path=/var/private/premium-content/\"\n# Returns: [{\"id\":0,\"path\":\"/var/private/premium-content/paywalled-video.mp4\",\"name\":\"paywalled-video.mp4\"}, ...]\n\n# Step 4: Enumerate root filesystem for any MP4 files\ncurl -b \"PHPSESSID=<session>\" -X POST https://target.avideo.site/listFiles \\\n -d \"path=/\"\n# Returns all .mp4 files visible to the web server process anywhere on disk\n```\n\n**Expected behavior:** Only files within the designated upload directory should be listable.\n**Actual behavior:** Files from any path readable by the web server process are returned with full absolute paths.\n\n## Impact\n\n- **Unauthorized media enumeration:** An uploader can discover private, premium, or access-controlled `.mp4` files stored outside their permitted directory.\n- **Filesystem structure disclosure:** Full absolute paths reveal server directory layout, aiding further attacks.\n- **Content bypass:** In AVideo deployments where premium video files are stored in filesystem directories not protected by application access control, this exposes the filenames and paths needed to directly access them if other path traversal or direct-file-access weaknesses are present.\n- **Blast radius:** Requires `canUpload` permission (low privilege), but this is the standard permission for all video uploaders on a multi-user AVideo instance.\n\n## Recommended Fix\n\nRestrict the supplied path to an allowed base directory using `realpath()`:\n\n```php\nif (!empty($_POST['path'])) {\n $allowedBase = realpath($global['systemRootPath'] . 'videos') . '/';\n $path = realpath($_POST['path']);\n\n // Reject paths that don't start with the allowed base\n if ($path === false || strpos($path . '/', $allowedBase) !== 0) {\n http_response_code(403);\n echo json_encode(['error' => 'Path not allowed']);\n exit;\n }\n $path .= '/';\n // ... continue with glob\n}\n```\n\n`realpath()` resolves `../` sequences before the prefix check, preventing traversal bypasses.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "wwbn/avideo"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "14.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-4wmm-6qxj-fpj4"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/WWBN/AVideo"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-22"
51+
],
52+
"severity": "MODERATE",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-03-19T12:43:42Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-f842-phm9-p4v4",
4+
"modified": "2026-03-19T12:44:28Z",
5+
"published": "2026-03-19T12:44:28Z",
6+
"aliases": [
7+
"CVE-2026-33242"
8+
],
9+
"summary": "Salvo has a Path Traversal in salvo-proxy::encode_url_path allows API Gateway Bypass",
10+
"details": "### Details\n\nA Path Traversal and Access Control Bypass vulnerability was discovered in the salvo-proxy component of the Salvo Rust framework (v0.89.2). The vulnerability allows an unauthenticated external attacker to bypass proxy routing constraints and access unintended backend paths (e.g., protected endpoints or administrative dashboards). This issue stems from the encode_url_path function, which fails to normalize \"../\" sequences and inadvertently forwards them verbatim to the upstream server by not re-encoding the \".\" character.\n\n---\n\n### Technical Details\n\nIf someone tries to attack by sending a special code like `%2e%2e`, Salvo changes it back to `../` when it first checks the path. The proxy then gets this plain `../` value. When making the new URL to send forward, the `encode_url_path` function tries to change the path again, but its normal settings do not include the `.` (dot) character. Because of this, the proxy puts `../` straight into the new URL and sends a request like `GET /api/../admin HTTP/1.1` to the backend server.\n\n```rust\n// crates/proxy/src/lib.rs (Lines 100-105)\n\npub(crate) fn encode_url_path(path: &str) -> String {\n path.split('/')\n .map(|s| utf8_percent_encode(s, PATH_ENCODE_SET).to_string())\n .collect::<Vec<_>>()\n .join(\"/\")\n}\n```\n\n---\n\n### PoC\n\n1 - Setup an Nginx Backend Server for example\n2 - Start Salvo Proxy Gateway in other port routing to /api/\n3 - Run the curl to test the bypass:\n\n```bash\ncurl -s http://127.0.0.1:8080/gateway/api/%2e%2e%2fadmin/index.html\n```\n\n---\n\n### Impact\n\nIf attackers take advantage of this problem, they can get past API Gateway security checks and route limits without logging in. This could accidentally make internal services, admin pages, or folders visible.\n\nThe attack works because the special path is sent as-is to the backend, which often happens in systems that follow standard web address rules. Attackers might also use different ways of writing URLs or add extra parts to the web address to get past simple security checks that only look for exact `../` patterns.\n\n---\n\n### Remediation\n\nInstead of changing the text of the path manually, the proxy should use a standard way to clean up the path according to RFC 3986 before adding it to the main URL. It is better to use a trusted tool like the URL crate to join paths, or to block any path parts with “..” after decoding them. But a custom implementation maybe looks like:\n\n```rust\npub(crate) fn encode_url_path(path: &str) -> String {\n let normalized = normalize_path(path);\n normalized.split('/')\n .map(|s| utf8_percent_encode(s, PATH_ENCODE_SET).to_string())\n .collect::<Vec<_>>()\n .join(\"/\")\n}\n\nfn normalize_path(path: &str) -> String {\n let mut stack = Vec::new();\n for part in path.split('/') {\n match part {\n \"\" | \".\" => (), \n \"..\" => { let _ = stack.pop(); },\n _ => stack.push(part),\n }\n }\n stack.join(\"/\")\n}\n```\n---\n\n**Vulnerable code introduced in:**\nhttps://github.com/salvo-rs/salvo/commit/7bac30e6960355c58e358e402072d4a3e5c4e1bb#diff-e319bf7afcb577f7e9f4fb767005072f6335d23f306dd52e8c94f3d222610d02R20\n\n---\n\n**Author**: Tomas Illuminati",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "crates.io",
21+
"name": "salvo"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0.39.0"
29+
},
30+
{
31+
"fixed": "0.89.3"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.89.2"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/salvo-rs/salvo/security/advisories/GHSA-f842-phm9-p4v4"
45+
},
46+
{
47+
"type": "WEB",
48+
"url": "https://github.com/salvo-rs/salvo/commit/7bac30e6960355c58e358e402072d4a3e5c4e1bb#diff-e319bf7afcb577f7e9f4fb767005072f6335d23f306dd52e8c94f3d222610d02R20"
49+
},
50+
{
51+
"type": "PACKAGE",
52+
"url": "https://github.com/salvo-rs/salvo"
53+
}
54+
],
55+
"database_specific": {
56+
"cwe_ids": [
57+
"CWE-22"
58+
],
59+
"severity": "HIGH",
60+
"github_reviewed": true,
61+
"github_reviewed_at": "2026-03-19T12:44:28Z",
62+
"nvd_published_at": null
63+
}
64+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-pp9r-xg4c-8j4x",
4+
"modified": "2026-03-19T12:44:56Z",
5+
"published": "2026-03-19T12:44:56Z",
6+
"aliases": [
7+
"CVE-2026-33241"
8+
],
9+
"summary": "Salvo Affected by Denial of Service via Unbounded Memory Allocation in Form Data Parsing",
10+
"details": "## Summary\nSalvo's form data parsing implementations (`form_data()` method and `Extractible` macro) do not enforce payload size limits before reading request bodies into memory. This allows attackers to cause Out-of-Memory (OOM) conditions by sending extremely large payloads, leading to service crashes and denial of service.\n\n## Details\n### Vulnerability Description\nThree attack vectors exist in Salvo's form handling:\n\n1. **URL-encoded form data** (`application/x-www-form-urlencoded`)\n - `Request::form_data()` calls `BodyExt::collect(body)` which reads the entire body into memory without size checking\n - Affects handlers using `req.form_data().await` directly\n\n2. **Multipart form data** (`multipart/form-data`)\n - Similar unbounded memory allocation during parsing\n - Affects handlers processing multipart uploads\n\n3. **Extractible macro**\n - `#[derive(Extractible)]` with `#[salvo(extract(default_source(from = \"body\")))]` internally calls `form_data()`\n - Vulnerabilities propagate to all extractors using body sources\n\n### Root Cause\nThe `FormData::read()` implementation prioritizes convenience over safety by reading entire request bodies before validation. Even when `Request::payload_with_max_size()` is available, it's not automatically applied in the form parsing path.\n\n### PoC\n1. run `Extract data from request` example in readme.md in docker file with limited memory say 100mb.\n2. Send `application/x-www-form-urlencoded` OR `multipart/form-data` payload to the endpoint.\n3. The server process OOM-crashes, instead of returning 413 error.\n\n\n## Impact\n### Immediate Effects\n- **Service Unavailability**: Servers crash under memory pressure\n- **Resource Exhaustion**: Single request can consume all available memory\n- **Cascading Failures**: In containerized environments, OOM can affect other services\n\n### Attack Characteristics\n- **Low Cost**: Attacker needs minimal bandwidth (header only, body can be streamed)\n- **No Authentication**: Exploitable on public endpoints\n- **Difficult to Rate-Limit**: Traditional rate limiting may not prevent single large request\n- **Amplification**: Small network cost → large memory consumption\n\n### Real-World Scenarios\n1. Public API endpoints accepting form data\n2. User registration/profile update handlers\n3. File upload endpoints using multipart forms\n4. Any endpoint using `#[derive(Extractible)]` with body sources\n\n## Suggestion: Make Multipart File Upload Handling Explicit Opt-In\n\n### Problem Statement\n\nCurrently, Salvo's multipart form data parsing automatically handles file uploads without explicit developer intent. This creates several security and usability concerns:\n\n1. **Unintended File Storage**: Developers may unknowingly accept file uploads when they only intended to handle text fields\n2. **Disk Space Exhaustion**: Automatic file buffering to disk can fill storage without proper limits\n3. **Resource Cleanup**: Temporary files may not be properly cleaned up if handlers don't expect them\n4. **Attack Surface**: Endpoints inadvertently become file upload targets",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "crates.io",
21+
"name": "salvo"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.89.3"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/salvo-rs/salvo/security/advisories/GHSA-pp9r-xg4c-8j4x"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/salvo-rs/salvo"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-770"
51+
],
52+
"severity": "HIGH",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-03-19T12:44:56Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-q382-vc8q-7jhj",
4+
"modified": "2026-03-19T12:44:07Z",
5+
"published": "2026-03-19T12:44:07Z",
6+
"aliases": [],
7+
"summary": "Improper handling of null Unicode character when parsing JSON in github.com/modelcontextprotocol/go-sdk",
8+
"details": "The Go SDK recently transitioned to the `segmentio/encoding` library for JSON parsing in version 1.3.1. While this change addressed both case-insensitivity and ASCII folding issues, the new parser implemented aggressive key matching that treated keys with `null` Unicode characters appended at the end as equivalent to their base strings.\n\n#### Impact\n\nWhen combined with duplicate keys, the described behavior leads to a \"last key wins\" resolution that could override the intended MCP message. This had the potential for:\n - **Bypassing intermediary inspection:** Proxies or policy layers that matched on exact field names may have failed to detect or filter these messages.\n - **Cross-implementation inconsistency:** Other MCP SDKs (TypeScript, Python) use case-sensitive parsing and would reject the same messages, creating potential security-boundary confusion.\n\n#### Fix:\n\nThe `segmentio/encoding` package was patched with a fix in https://github.com/segmentio/encoding/commit/7d5a25dbc5da13aed3cb047a127e4d0e96f536fb and a new version of the package was released (`v0.5.4`). The SDK switched to the patched version of the dependency in 724dd47aa. Users are advised to update to v1.4.1 to resolve this issue.\n\n#### Credits:\nThank you to Francesco Lacerenza (Doyensec) for reporting this issue.",
9+
"severity": [
10+
{
11+
"type": "CVSS_V4",
12+
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "Go",
19+
"name": "github.com/modelcontextprotocol/go-sdk"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "0"
27+
},
28+
{
29+
"fixed": "1.4.1"
30+
}
31+
]
32+
}
33+
],
34+
"database_specific": {
35+
"last_known_affected_version_range": "<= 1.4.0"
36+
}
37+
}
38+
],
39+
"references": [
40+
{
41+
"type": "WEB",
42+
"url": "https://github.com/modelcontextprotocol/go-sdk/security/advisories/GHSA-q382-vc8q-7jhj"
43+
},
44+
{
45+
"type": "WEB",
46+
"url": "https://github.com/modelcontextprotocol/go-sdk/commit/724dd47aa3431b9d4cf9ac2eebbf7b38a629afca"
47+
},
48+
{
49+
"type": "WEB",
50+
"url": "https://github.com/segmentio/encoding/commit/7d5a25dbc5da13aed3cb047a127e4d0e96f536fb"
51+
},
52+
{
53+
"type": "PACKAGE",
54+
"url": "https://github.com/modelcontextprotocol/go-sdk"
55+
}
56+
],
57+
"database_specific": {
58+
"cwe_ids": [
59+
"CWE-1395",
60+
"CWE-436"
61+
],
62+
"severity": "HIGH",
63+
"github_reviewed": true,
64+
"github_reviewed_at": "2026-03-19T12:44:07Z",
65+
"nvd_published_at": null
66+
}
67+
}

0 commit comments

Comments
 (0)