CompanyResources
Resources

One SSRF, Five Customers: How a Forgotten RSS Reader Leaked a MySQL Password From Every Tenant

Mohammed Abalkhail, Aziz5

May 13, 2026

One SSRF, Five Customers: How a Forgotten RSS Reader Leaked a MySQL Password From Every Tenant

We ran Shax against a multi-tenant enterprise portal, and within a single engagement it extracted per-customer database credentials through one overlooked endpoint. The vulnerability shipped to every tenant in the fleet.

The Target:

The platform under test was a multi-tenant enterprise portal serving five customer organizations, each on its own branded subdomain under

.redacted.com

. The engagement scope was SSRF discovery and exploitation across all five tenants. Authenticated test credentials were supplied for each tenant. Scope was wildcard.

This kind of multi-tenant application has more risk of having identical vulnerabilities. The five tenants look like five separate products from the outside, different logos, different styles, some using Azure AD and some using Okta. But underneath, they share the same code. A single vulnerability in vendor code is inherited by every customer, and the only effective barrier to fleet-wide compromise is per-tenant authentication.

Reconnaissance:

Shax began with the analysis of response headers, static asset paths, and the JavaScript bundles served to authenticated users to fingerprint the target stack:

  • Backend: custom enterprise portal framework on Python 2.7
  • Frontend: ExtJS 4.2 SPA, jQuery 2.1.3, Bootstrap
  • Deployment: Kubernetes cluster, pod hostnames matching http-<REDACTED>-<suffix>

The JavaScript bundles exposed the routing convention used by every endpoint on every tenant: POST /<namespace>/<action>, with namespaces split across roughly two dozen functional areas (File, Import, Entity, Search, Rssviewer, Dwpeer, Richtext, Editor, Calendar, Charts, Tile, Main, and others). Bundle analysis surfaced 375 unique endpoints across the five subdomains.

Two URL-handling endpoints were identified for SSRF behaviour:

  1. POST /<...>/file/create, which accepted four URL-shaped parameter names (url, source, file_source, remote_url) and returned HTTP 200 with errtype:auth rather than a parameter-validation error. An authentication error, not a parameter error, indicates that the server recognised and accepted the URL fields and only blocked the request because the session was not authorised for the action. An endpoint that did not handle URL input would have rejected the parameters themselves.
  2. POST /<...>/rssviewer/getFeedData, an authenticated endpoint taking a single JSON parameter (feed). This endpoint is an RSS feed reader, and fetching arbitrary URLs is its documented purpose. On the face of it, that makes the SSRF surface intentional rather than vulnerable, which is the reason such endpoints are often given a quick once-over and skipped. The interesting question is not whether the endpoint fetches URLs, but what restrictions it applies to the URLs it accepts: scheme allowlist, host filtering, response handling. That question is rarely asked on endpoints where fetching is part of the spec. We as humans usually assume that this functionality is specifically made for handling URLs so it might not be vulnerable.

But Shax did not make any assumption and have tested both. The file-create handler accepted all four parameter names and returned {"success":true,"fileEntId":11722XXXX}. Webhook callbacks confirmed the server-side fetch was working (User-Agent Python-urllib/2.7, source IP within the platform's egress range), but the response body of the fetched URL was never returned to the caller. Payloads targeting the cloud-metadata service at 169.254.169.254 reached the endpoint but produced no observable output, since the response body was not reflected back. The endpoint was logged as a Medium-severity unvalidated URL parameter and set aside.

The RSS viewer (/rssviewer/getFeedData) endpoint was tested next, and this is where the real impact resides.

Probing the RSS Viewer

The first tested payload was a webhook URL, sent to confirm server-side fetch behaviour:

POST /[...]/rssviewer/getFeedData HTTP/1.1
Host: [REDACTED].redacted.com
Cookie: ploff="https://[REDACTED].redacted.com/"; l="[SESSION]"
Content-Type: application/json

{"feed": "<https://shax.webhook/><token>"}

The webhook fired in 655ms, with User-Agent Python-urllib/2.7 and a source IP matching the platform's egress range, confirming the server-side fetch had taken place. More importantly, the HTTP response to the POST contained the rendered HTML returned by shax.webhook itself, including the request inspector page's <title> and body markup. The endpoint was not just fetching the URL; it was returning the response body to the caller. But that alone isn't enough. What we really needed was to reach something on the internal network.

The second payload tested protocol handling by using [file:///](file:///) schema to achieve local file read :

Be among the first to use Nua Cybersecurity digital employees

Links

CompanyCareer

Resources

Blogs

Legal

Terms & ConditionsTerms of UsePrivacy Policy
info@nuasecurity.com

© 2026 NUA USA.

footer logofooter logo hoverfooter background effect