Skip to content

Commit 8d04a0e

Browse files
1 parent 85cef50 commit 8d04a0e

2 files changed

Lines changed: 134 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-m74m-f7cr-432x",
4+
"modified": "2026-03-27T18:00:43Z",
5+
"published": "2026-03-27T18:00:43Z",
6+
"aliases": [
7+
"CVE-2026-33992"
8+
],
9+
"summary": "pyLoad: Server-Side Request Forgery via Download Link Submission Enables Cloud Metadata Exfiltration",
10+
"details": "## Summary\n\nPyLoad's download engine accepts arbitrary URLs without validation, enabling Server-Side Request Forgery (SSRF) attacks. An authenticated attacker can exploit this to access internal network services and exfiltrate cloud provider metadata. On DigitalOcean droplets, this exposes sensitive infrastructure data including droplet ID, network configuration, region, authentication keys, and SSH keys configured in user-data/cloud-init.\n\n## Details\n\nThe vulnerability exists in PyLoad's download package functionality (`/api/addPackage` endpoint), which directly passes user-supplied URLs to the download engine without validating the destination. The affected code in `src/pyload/webui/app/blueprints/api_blueprint.py`:\n\n```python\n@bp.route(\"/addPackage\", methods=[\"POST\"], endpoint=\"add_package\")\n@login_required\ndef add_package():\n name = flask.request.form[\"add_name\"]\n links = flask.request.form[\"add_links\"].split(\"\\n\")\n # ... validation omitted ...\n api.add_package(name, links, dest) # No URL validation\n```\n\nThe download engine in `src/pyload/core/managers/download.py` accepts any URL scheme and initiates HTTP requests to arbitrary destinations, including internal network addresses and cloud metadata endpoints.\n\n## Proof of Concept\n\n**Live Demo Instance:** http://143.244.141.81:8000 \n**Credentials:** `pyload` / `pyload`\n\n- Login into the pyload application\n- Navigate to package tab and enter the package name and fill the Link section with the following URL\n\n```\nhttp://169.254.169.254/metadata/v1.json\n```\n\n<img width=\"1851\" height=\"786\" alt=\"image\" src=\"https://github.com/user-attachments/assets/18e7aedf-7663-4a57-8f3e-5200be2c958e\" />\n\n- Now navigate to Files section and download the link.\n\n<img width=\"1429\" height=\"870\" alt=\"image\" src=\"https://github.com/user-attachments/assets/9b8b9cd6-afb7-461c-b058-a3cc4f26e2e6\" />\n\n- It was observed that we are able to Read the Digital Ocean Metadata\n\n<img width=\"1872\" height=\"837\" alt=\"image\" src=\"https://github.com/user-attachments/assets/d30d2d74-53e9-46f8-8206-894a275ac831\" />\n\nThe downloaded `v1.json` file contains sensitive cloud infrastructure data:\n- **Droplet ID**: Unique identifier for the instance\n- **Network Configuration**: Public/private IP addresses, VPC topology\n- **Authentication Keys**: Cloud provider auth tokens\n- **SSH Keys**: Public keys configured in droplet metadata\n- **Region and Datacenter**: Infrastructure location\n\n## Impact\n\n**Vulnerability Type:** Server-Side Request Forgery (SSRF) \n**CVSS Score:** 7.7 - 9.1 (High to Critical, depending on cloud deployment)\n\n### Affected Systems\n- All PyLoad installations (version 0.5.0 and potentially earlier)\n- **Critical Impact** on cloud deployments (AWS EC2, DigitalOcean, Google Cloud, Azure) where metadata contains:\n - IAM credentials (AWS)\n - SSH private keys (configured in user-data)\n - API tokens and secrets\n - Database credentials stored in cloud-init\n\n### Attack Requirements\n- Valid PyLoad user account (any role - ADMIN or USER)\n- Network connectivity to PyLoad instance\n\n### Security Impact\n1. **Cloud Metadata Theft**: Complete exfiltration of instance metadata\n2. **Lateral Movement**: Discovery and enumeration of internal network services\n3. **Credential Exposure**: Theft of cloud IAM credentials, SSH keys, API tokens\n4. **Infrastructure Mapping**: Network topology, IP addressing, service discovery\n\n## Remediation\n\nImplement URL validation in the download engine:\n1. Whitelist allowed URL schemes (http/https only)\n2. Block requests to private IP ranges (RFC 1918, link-local addresses)\n3. Block cloud metadata endpoints (169.254.169.254, metadata.google.internal, etc.)\n4. Implement request destination validation before initiating downloads",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "pyload-ng"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "0.5.0b3.dev96"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/pyload/pyload/security/advisories/GHSA-m74m-f7cr-432x"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/pyload/pyload/commit/b76b6d4ee5e32d2118d26afdee1d0a9e57d4bfe8"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/pyload/pyload"
50+
}
51+
],
52+
"database_specific": {
53+
"cwe_ids": [
54+
"CWE-918"
55+
],
56+
"severity": "CRITICAL",
57+
"github_reviewed": true,
58+
"github_reviewed_at": "2026-03-27T18:00:43Z",
59+
"nvd_published_at": null
60+
}
61+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-vc8f-x9pp-wf5p",
4+
"modified": "2026-03-27T17:58:48Z",
5+
"published": "2026-03-27T17:58:48Z",
6+
"aliases": [
7+
"CVE-2026-33994"
8+
],
9+
"summary": "Locutus Prototype Pollution due to incomplete fix for CVE-2026-25521",
10+
"details": "## Summary\n\nA prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard.\n\nThis vulnerability stems from an incomplete fix for [CVE-2026-25521](https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh). The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another.\n\n## Package\n\nlocutus (npm)\n\n## Affected versions\n\n>= 2.0.39, <= 3.0.24\n\nTested and confirmed vulnerable on **2.0.39** and **3.0.24** (latest). Version 2.0.38 (pre-fix) uses a different guard (`String.prototype.includes`) and is not affected by this specific bypass.\n\n---\n\n## Description\n\n### Details\n\nThe vulnerability resides in `parse_str.js` where the `RegExp.prototype.test()` function is used to check whether user-provided input contains forbidden keys:\n\n```javascript\nif (/__proto__|constructor|prototype/.test(key)) {\n break\n}\n```\n\nThe previous guard (fixed in CVE-2026-25521) used `String.prototype.includes()`:\n\n```javascript\nif (key.includes('__proto__')) {\n break\n}\n```\n\nThe CVE-2026-25521 fix correctly identified that `String.prototype.includes` can be hijacked. However, the replacement guard using `RegExp.prototype.test()` suffers from the same class of weakness — `RegExp.prototype.test` is a writable method on the prototype chain and can be overridden to always return `false`, completely disabling the guard.\n\nThe robust fix is to use direct string comparison operators (`===`) in native control flow (`for`/`if`) instead of prototype methods like `RegExp.prototype.test()`, since `===` is a language-level operator that cannot be overridden.\n\n### PoC\n\n#### Steps to reproduce\n\n1. Install locutus using `npm install locutus`\n2. Run the following code snippet:\n\n```javascript\nconst parse_str = require('locutus/php/strings/parse_str');\n\n// Hijack RegExp.prototype.test (simulates a prior prototype pollution gadget)\nconst original = RegExp.prototype.test;\nRegExp.prototype.test = function () { return false; };\n\n// Payload\nconst result = {};\nparse_str('__proto__[polluted]=yes', result);\n\n// Check\nRegExp.prototype.test = original;\nconsole.log(({}).polluted); // 'yes' — prototype is polluted\n```\n\n#### Expected behavior\n\nPrototype pollution should be prevented and `({}).polluted` should print `undefined`.\n\n```\nundefined\n```\n\n#### Actual behavior\n\n`Object.prototype` is polluted. This is printed on the console:\n\n```\nyes\n```\n\n### Impact\n\nThis is a prototype pollution vulnerability with the same impact as CVE-2026-25521. The attack requires a chaining scenario — an attacker needs a separate prototype pollution gadget (e.g., from another npm package in the same application) to override `RegExp.prototype.test` before exploiting `parse_str`. This is realistic in Node.js applications that use multiple npm packages, where one package's vulnerability can disable another package's defenses.\n\nAny application that processes attacker-controlled input using `locutus/php/strings/parse_str` may be affected. It could potentially lead to:\n\n1. Authentication bypass\n2. Denial of service\n3. Remote code execution (if polluted property is passed to sinks like `eval` or `child_process`)\n\n### Resources\n\n- Original advisory: https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh\n- Fix commit (incomplete): https://github.com/locutusjs/locutus/commit/042af9ca7fde2ff599120783e720a17f335bb01c\n- Vulnerable file: https://github.com/locutusjs/locutus/blob/main/src/php/strings/parse_str.js#L77\n\n## Maintainer response\n\nThank you for the follow-up report. This issue was reproduced locally against `locutus@3.0.24`, confirming that the earlier `parse_str` guard was incomplete: if `RegExp.prototype.test` was already compromised, the guard could be bypassed and `parse_str('__proto__[polluted]=yes', result)` could still pollute `Object.prototype`.\n\nThis is now fixed on `main` and released in `locutus@3.0.25`.\n\n## Fix Shipped In\n\n- **PR:** [locutusjs/locutus#597](https://github.com/locutusjs/locutus/pull/597)\n- **Merge commit on `main`:** `345a6211e1e6f939f96a7090bfeff642c9fcf9e4`\n- **Release:** [v3.0.25](https://github.com/locutusjs/locutus/releases/tag/v3.0.25)\n\n## What the Fix Does\n\nThe new fix no longer relies on a regex-prototype guard for safety. Instead, `src/php/strings/parse_str.ts` now rejects dangerous key paths during parsed-segment assignment, so the sink itself is hardened even if `RegExp.prototype.test` has been tampered with beforehand.\n\n## Tested Repro Before the Fix\n\n- Override `RegExp.prototype.test` to always return `false`\n- Call `parse_str('__proto__[polluted]=yes', result)`\n- Observe `({}).polluted === 'yes'`\n\n## Tested State After the Fix in `3.0.25`\n\n- Dangerous key paths are skipped during assignment\n- The same chained repro no longer pollutes `Object.prototype`\n- The regression is covered by `test/custom/parse_str-prototype-pollution.vitest.ts`\n\n---\n\nThe locutus team is treating this as a real package vulnerability with patched version `3.0.25`. The vulnerable range should end at `< 3.0.25`.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "locutus"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "2.0.39"
29+
},
30+
{
31+
"fixed": "3.0.25"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/locutusjs/locutus/security/advisories/GHSA-vc8f-x9pp-wf5p"
46+
},
47+
{
48+
"type": "WEB",
49+
"url": "https://github.com/locutusjs/locutus/pull/597"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/locutusjs/locutus/commit/345a6211e1e6f939f96a7090bfeff642c9fcf9e4"
54+
},
55+
{
56+
"type": "PACKAGE",
57+
"url": "https://github.com/locutusjs/locutus"
58+
},
59+
{
60+
"type": "WEB",
61+
"url": "https://github.com/locutusjs/locutus/releases/tag/v3.0.25"
62+
}
63+
],
64+
"database_specific": {
65+
"cwe_ids": [
66+
"CWE-1321"
67+
],
68+
"severity": "MODERATE",
69+
"github_reviewed": true,
70+
"github_reviewed_at": "2026-03-27T17:58:48Z",
71+
"nvd_published_at": null
72+
}
73+
}

0 commit comments

Comments
 (0)