A website-management dashboard let admins run queries and rendered the results as HTML. By seeding XSS into user records on the managed site and hitting the raw query endpoint, which returned HTML instead of JSON, the payload fired in the browser of anyone with console access.

The report is still pending, so call the target redacted.com: a website-management dashboard at dashboard.redacted.com that lets administrators control their hosted site at site.redacted.sites.

1. A query console

The dashboard gave the admin the ability to run queries. I tried the obvious one:

SELECT Id, FirstName, LastName, Email, Account.Name FROM User

It returned the users I had created while testing on site.redacted.sites. So I went back to the site, created a user whose fields carried XSS payloads, and re-ran the query. The data showed up in the dashboard, but encoded, so nothing fired. At that layer the output was safely escaped.

2. More digging: the raw endpoint

The console issued the query over XHR to:

https://api.redacted.com/QueryConsole?q={Query}

The interesting part: this endpoint returned its results as HTML, not JSON. Hitting it directly with the same query:

https://api.redacted.com/QueryConsole?q=SELECT Id, FirstName, LastName, Email, Account.Name FROM User

rendered the stored payload as live markup, and the alert fired straight on the page. The escaping I had seen lived only in the dashboard's renderer; the endpoint that fed it served raw HTML. That widens the impact from "admins of the dashboard" to anyone who can reach QueryConsole.

STORED PAYLOAD → SOQL CONSOLE → HTML RESPONSE attackerseeds XSS in a user recordon site.redacted.sites QueryConsoleapi.redacted.comSELECT … FROM User console useradmin or anyone withQueryConsole access store render GET /QueryConsole?q=SELECT Id,FirstName,… FROM User Content-Type: text/html - not JSON, not encoded ✕ the dashboard UI encoded the output, the raw endpoint did not payload executes for every consumer of the endpoint
Fig. 01 - the dashboard escaped query results, but the underlying QueryConsole endpoint returned raw HTML. A payload stored in a user record on the managed site executes in the browser of anyone who runs the query.

3. Trying to push it further: CSRF

I first assumed the console ran SQL, so I tried a CSRF to coerce the admin into modifying data. The forged request went through, but nothing changed, because the console actually speaks SOQL (Salesforce Object Query Language), which fetches data rather than modifying it. With no state-changing action to forge, the CSRF angle was a dead end, so I reported the stored XSS.

4. Takeaways

  • Encoding in one layer is not encoding everywhere. The dashboard escaped its output, but the API feeding it returned HTML and did not. Always test the raw data source, not just the UI that wraps it.
  • Know the query language. SOQL is read-only, which killed the CSRF-to-write idea, but the XSS stood on its own.
  • A stored XSS behind a shared endpoint hits every consumer of that endpoint, not only the obvious admin UI.

Thanks for reading.