Exploitation Level: Medium / Remote
DREAD Score: 6.6
Vulnerability: Stored XSS on an administrative page
Patched Version: 2.4.7
Give is a WordPress plugin which allows users to setup a donation page on a website. It currently has 60k installs.
During a recent audit of the plugin, we found a severe vulnerability which allows donors to inject arbitrary code on an administrative page.
If you are using a version lower than 2.4.7, you should update immediately.
Note: A forced update was pushed by the developers, and all affected users should now be patched and protected against this vulnerability.
When creating a donation, all of the arguments are sanitized as text fields, but this method does not take into consideration where the variables are reflected. Thanks to this oversight, a malicious user can still perform an injection on the donors page after making a donation.
Due to the reflection point, the injection can only be seen by users with sufficient permissions to see the donors — this means that a malicious script could do a lot of damage while acting as these users.
Timeline
- 2019-05-03: Initial disclosure to Give
- 2019-05-07: Partial patch released (2.4.6)
- 2019-05-10: Notified vendor of possible bypasses
- 2019-05-14: Complete patch released (2.4.7)
- 2019-05-21: Patch pushed to all customers via auto-update
Technical Details
When processing a donation, the plugin recursively sanitizes every POSTed variable using the sanitize_text_field method.
Under this plugin, a Donation uses multiple data structures:
- Donation, which contains the donation content itself.
- Donor, which contains the data related to the user performing the donation.
If the user performing the donation is a guest, meaning this is the first donation using this email, a new donor will be created with the provided information.
Under the admin panel, you can see either one of the two created entities by the donation: The Donors and The Donations.
Both of these pages are tables where we can see, filter, and modify the data. On the Donors table, few custom attributes are added on tags to facilitate the website interactivity. Among these is the data-name attribute, which has the donor name value. As you can see on the donor creation code, this value is the full name of the donor. This attribute is used on two tags: The row tag and the column checkbox input.
The code can be found under class-donor-table.php:
The name of our user was sanitized using the sanitize_text_field method, which performs the following actions per the WordPress documentation:
- Checks for invalid UTF-8
- Converts single < characters to entities
- Strips all tags
- Removes line breaks, tabs, and extra whitespace
- Strips octets
You might notice it removes the tags, but it does not mention being safe as attribute. With that in mind, it is possible to escape the data-name attribute and add an event on the input.
Having the following name:
Name " onfocus="alert(document.domain)" autofocus="
gives us the following HTML on the input injection:
<input class="donor-selector" type="checkbox" name="donor[]" value="1" data-name="Name " onfocus="alert(document.domain)" autofocus="" />
Using the autofocus attributes will trigger the onfocus event without user interaction, which can execute malicious code.
Conclusion
Cross-site scripting (XSS) is a widespread vulnerability that allows an attacker to inject malicious content into a site.
In the case of Stored XSS (as seen with this particular vulnerability), the payload is stored in the site’s database and retrieved with every page request. If left unpatched, it can be very dangerous, as an attacker acting as a donor can obtain complete control of the browser environment.
To protect against this vulnerability, we strongly encourage Give users to update to version 2.4.7 as soon as possible. In the event that you can’t immediately update your plugin, you can leverage the Sucuri Firewall or equivalent technology to virtually patch the vulnerability.