For my blog I have tried several content management systems and a while ago I decided to use Ghost for its simplicity and ease of use. Recently, I found XSS in the Ghost platform from at least version 1.24.9 and still in the latest version on GitHub. Most likely the XSS exists in much earlier versions as well.
Note: this XSS does require administrator or owner roles and due to CORS/Same-Origin restrictions the likelyhood of exploitation outside of lab conditions is low.
XSS in the API
While auditing Ghost I discovered that there is an API call that is vulnerable to XSS. The particular API call is
/ghost/api/v0.1/settings/. The API call can be triggered from several of the settings tabs when an administrator or owner saves the settings. The API call sends a PUT request with JSON parameters. There are four parameters in the JSON that are vulnerable to XSS: logo, cover_image, ghost_head and ghost_foot. All four of the parameters will cause a XSS payload to be executed on every page of the website served by Ghost. The payload itself is very simple and only involves closing a script block before providing a new script block containing the XSS payload.
The full settings JSON does not need to be sent with the request in order to exploit the XSS. Only sending the desired parameter and payload is sufficient.
Note: the ghost_head and ghost_foot parameters are the same ones where in the administrator interface a user can place custom scripts. So they are meant to contain scripts so the XSS ends up being intended functionality for those two parameters.
After finding the XSS I decided to do some more digging in Ghost past and found out I rediscovered an old XSS. VoidSec had previously discovered XSS in the same API call. It should be noted Ghost claimed in version 0.5.9 to have remediated this vulnerability and the original payload from VoidSec's report didn't work in the version I tested.
Due to the API call being an authenticated PUT request and limitations of CORS exploiting the vulnerability an attacker would have to have to be able to access the session token for an administrator or owner. It is possible to bypass the CORS restrictions by sending the request through a proxy such as CORS Anywhere But modern browsers won't send the session token with the request since it doesn't comply with the same origin policy. Bypassing the same origin policy is the harder part. There are ways to by pass the same origin policy but thats for another blog post.
I initially disclosed to [email protected] and until I sent a third email about the impending public disclosure I never received a response. After the thrid email they responded referencing their now updated SECURITY.md.
I did verify the XSS still exist as of version 2.2.0.
21 July 2018 - Initial reported to Ghost via email
30 July 2018 - Follow-up email sent to Ghost
4 October 2018 - Notified Ghost of upcoming public disclosure
5 October 2018 - Response from Ghost referencing updated SECURITY.md
19 October 2018 - Public disclosure after 90 days since initial report