Skip to content

DOM XSS: folder name/id rendered unescaped in the folder-size dialog #50

Description

@MiMoHo

Summary

The folder-size dialog builds its HTML by interpolating the IMAP folder name and folder id directly into an HTML string without escaping, then inserts it via rcmail.simple_dialog(). A folder whose name (or path) contains HTML results in DOM-based XSS in the victim's authenticated Roundcube session.

  • Type: DOM-based XSS (CWE-79)
  • Reviewed at commit: 52b6ed6 (current master)
  • Severity: Medium — script execution in the webmail origin. Exploitability depends on an attacker being able to place a folder with a crafted name into the victim's view (e.g. a shared/public IMAP namespace the victim is subscribed to); self-inflicted otherwise.

Root cause

In assets/main.js the per-folder row is assembled with template literals and no escaping:

html += `
  <tr>
    <td class="name ${mailbox.virtual ? 'virtual' : ''}"
        onclick="return ${mailbox.virtual ? 0 : 1} ? rcmail.command('list', '${id}', this, event) : ''"
        title="${id}">
      <div style="margin-left: ${level * 1.5}em">${mailbox.name}</div>
    </td>
    ...

The resulting string is handed to rcmail.simple_dialog(generatePopupContent(resp), ...), which inserts it as HTML.

mailbox.name and id come from rcmail.env.mailboxes, which Roundcube core populates with the raw (unescaped) folder name/path — core escapes it with rcube::Q() only at its own render sites, and expects client code to escape before injecting into HTML. Here ${mailbox.name} (HTML text context), ${id} in title="…" (attribute context) and ${id} inside the onclick="…rcmail.command('list', '${id}', …)" (JS-string-in-attribute context) are all unescaped, so a folder name/path containing e.g. <img src=x onerror=…> or a '/" breaks out.

Proof of concept

On a server where the attacker can create a folder visible to the victim (shared namespace), create a folder named:

<img src=x onerror=alert(document.domain)>

When the victim opens Folder options → Show folder size, the payload executes. (A folder id/path containing a single quote similarly breaks out of the onclick handler.)

Recommended fix

HTML-escape all untrusted values before interpolation, per context: escape mailbox.name for HTML text, and id for both the title attribute and the JS-string in onclick. Prefer building the row with DOM APIs (document.createElement + textContent + dataset) and attaching the click handler in JS, or run values through an escaping helper (e.g. a small escapeHtml() and JSON.stringify for the JS-string).

Disclosure

The repository has no SECURITY.md and GitHub private vulnerability reporting is not enabled, so there is no private channel; reporting here so it can be fixed. Happy to open a PR. Found during a security review of Roundcube and commonly-used plugins.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions