Stored XSS in attachment-display exploitable through SameSite
Medium
Vulnerability Details
## Summary:
The compose attachments preview endpoint renders user-uploaded HTML files inline without a restrictive Content Security Policy, allowing JavaScript execution. By uploading an HTML file and opening it via display-attachment, the script runs in the Roundcube origin.
Attacking a user is only possible by setting cookies on the domain, which can be done from any subdomain of the site where Roundcube is hosted.
## Steps To Reproduce:
1. Set up a local Roundcube instance on an IP address (eg. 127.0.0.1)
2. Edit `C:\Windows\System32\drivers\etc\hosts` on Windows or `/etc/hosts` on Unix to contain the following entries:
```conf
127.0.0.1 mail.target.local
127.0.0.1 xss.target.local
```
(This simulates a single domain hosting both Roundcube and another service vulnerable to XSS)
3. Log in to Roundcube on http://mail.target.local as the *victim*
4. As the *attacker*, fill `USERNAME` and `PASSWORD` in {F5496770} and run it
```shell
$ python3 poc.py
{'roundcube_sessid': '1798cbb4c1d7c7f9ca26069b52aac1aa', 'roundcube_sessauth': 'GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000'}
compose_id='183727919869aecb6499f76'
{"action":"upload","exec":"this.add2attachment_list(\"rcmfile11773063013009066400\",{\"html\":\"<a href=\\\"#load\\\" class=\\\"filename\\\" onclick=\\\"return rcmail.command('load-attachment','rcmfile11773063013009066400', this, event)\\\"><span class=\\\"attachment-name\\\">xss.html</span><span class=\\\"attachment-size\\\">(30 B)</span></a><a href=\\\"#delete\\\" onclick=\\\"return rcmail.command('remove-attachment','rcmfile11773063013009066400', this, event)\\\" title=\\\"Delete\\\" class=\\\"delete\\\" aria-label=\\\"Delete xss.html\\\"></a>\",\"name\":\"xss.html\",\"mimetype\":\"text/html\",\"classname\":\"text html\",\"complete\":true},\"u128763271\");\nthis.auto_save_start(false);\n"}
att_id='11773063013009066400'
----------------------------------------------------------------------------------------------------
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Path=/index.php/xss; Domain=.target.local'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Path=/index.php/xss; Domain=.target.local'
location.href = 'http://mail.target.local/index.php/xss?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';
```
5. Copy the JavaScript code under the `-----` line and open <http://xss.target.local>. Open DevTools and paste the copied JavaScript into the Console (in a real scenario you would do this through a real XSS vulnerability on whatever application runs under `xss.target.local`)
6. After pressing ENTER, notice the XSS popup from `mail.target.tld` proving the XSS on a different origin was abused to get XSS on Roundcube:
{F5496958}
## Supporting Material/References:
Roundcube's compose flow lets users upload attachments and preview them through the [mail/attachment_display](https://github.com/roundcube/roundcubemail/blob/master/program/actions/mail/attachment_display.php#L30-L39) action, which calls [`rcube_uploads::display_uploaded_file()`](https://github.com/roundcube/roundcubemail/blob/master/program/lib/Roundcube/rcube_uploads.php#L208-L264). For HTML attachments, this path sets only `Content-Type` and streams the file body directly without the defensive handling used by the general attachment viewer ([mail/get.php](https://github.com/roundcube/roundcubemail/blob/master/program/actions/mail/get.php)), which correctly wraps HTML in a safe container and sets CSP (script-src 'none'). As a result, when a user uploads an HTML file (e.g., `xss.html`) and clicks the attachment preview link (display-attachment), the browser executes the embedded script under the Roundcube origin. This is distinct from viewing HTML attachments in received messages, where CSP is explicitly enforced. The issue manifests in the compose attachment display flow, specifically the display-attachment endpoint, which does not add CSP headers or force safe download for active content types.
This vulnerable endpoint requires a specific key in `$SESSION` to be able to access a certain compose file due to a check in [`init()`](https://github.com/roundcube/roundcubemail/blob/master/program/actions/mail/attachment_upload.php#L169). This is only set randomly whenever a new mail is composited, so an attacker cannot prepare this payload for a victim directly. They need to have specific cookies to be authorized to view the XSS payload.
To overcome this, an attacker can make use of other services external to Roundcube that live on the same site (domain). These can all set cookies over the whole site, not just their origin, in a technique called "Cookie Tossing". All it takes is one vulnerable service on the same site to execute arbitrary JavaScript, which can then write to `document.cookie` with a domain of `.target.local`. All subdomains (including Roundcube) will then use that cookie. Then they can redirect to the vulnerable XSS URL on Roundcube to trigger the final XSS.
To preserve the original user's session, an attacker can use a specific `Path=` attribute on the cookie so any fetches to `/` will still use the victim's session, completely taking over their account.
In the DevTools it becomes clear how the cookies look combined in the end. Note the `.target.local` domains are the attacker's cookies, specific to the `/index.php/xss` path. And the other cookies were already there from the victim being logged in before the attack.
{F5496959}
## Impact
An attacker who has an account on Roundcube, and can find any XSS vulnerability on a same-site service next to Roundcube, can prepare a an attack that takes over the victim's Roundcube account. With this they can read all emails (eg. take over accounts through password resets), send emails from the victim's account, and make any changes to the settings.
In summary, the security of Roundcube now relies on the security of every other hosted application under the same domain.
## Fix
Apply the same CSP rules as other attachment view endpoints so that HTML content cannot execute arbitrary JavaScript.
Actions
View on HackerOneReport Stats
- Report ID: 3594137
- State: Closed
- Substate: resolved
- Upvotes: 3