
Proof-of-concept demonstrating stored XSS in Appsmith Table Widget leading to vertical privilege escalation and full admin takeover via XSS-to-CSRF attack.
Proof-of-Concept for Privilege Escalation in Appsmith via Stored XSS
Disclosure: Originally reported by me via GHSA-5hw4-whxv-6794
⚠️ Authorized pentesting/research use only.
| Field | Value |
|---|---|
| CVE ID | CVE-2026-30862 |
| Severity | 🔴 Critical |
| CVSS Score | 9.1 |
| CVSS Vector | [CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H] |
| CWE | CWE-79, CWE-269, CWE-352 |
| Affected Product | Appsmith ≤ 1.95 |
| Patched Version | 1.96 |
| Advisory | GHSA-5hw4-whxv-6794 |
A Critical Stored XSS vulnerability exists in the Table Widget (TableWidgetV2). The root cause is a lack of HTML sanitization in the React component rendering pipeline, allowing malicious attributes to be interpolated into the DOM. By leveraging the "Invite Users" feature, an attacker with a regular user account ([email protected]) can force a System Administrator to execute a high-privileged API call (/api/v1/admin/env), resulting in a Full Administrative Account Takeover.
1. Root Cause: Lack of Output Sanitization Sink The vulnerability resides in the Table Widget's rendering engine
File: app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.tsx
The BasicCell component fails to sanitize data when the columnType is set to URL or Plain Text. It returns raw user-supplied values that are directly rendered as React children, which the browser interprets as executable HTML.
Vulnerable Code Snippet (Lines 132-143 & 172-173):
const contentToRender = useMemo(() => {
switch (columnType) {
case ColumnTypes.URL:
// Direct interpolation of 'url' into href without sanitization
return <a href={url} target="_blank">{value}</a>;
default:
return value; // Line 141: Raw value returned
}
}, [columnType, url, value]);
// Line 173: Final sink where unsanitized content is injected into the DOM
<Content ref={contentRef}>{contentToRender}</Content>
2. Attack Vector: XSS-to-CSRF via Social Engineering (Invite Feature) Although the SESSION cookie is protected by HttpOnly, the XSRF-TOKEN is accessible via document.cookie. Since Appsmith allows any user to "Invite" others to their app, an attacker can use this as a delivery mechanism to execute a Cross-Privilege Request Forgery (CPRF) within the Admin's active session.
Step 1: XSS Vulnerability Verification
Log in with a regular user account: [email protected].
Create a new application and add a Table Widget.
In the Table Data property, inject the following:
[{ "id": 1, "payload": "" }]
-- Step 2: Weaponization (Full Admin Takeover)
[email protected], update the Table Data with the following payload designed to modify the administrative environment:[
{
"id": 1,
"Status": "System Update Required",
"payload": "alert('Admin Privileges Granted to [email protected]'));\">"
}
]
Use the "Share" feature to invite the System Administrator ([email protected]) to the app. This forces the Admin to view the malicious table upon opening the invitation.
Once the Admin launches the app, the script executes in the background. It reads the Admin's XSRF-TOKEN and sends a PUT request to add [email protected] to the administrative whitelist.
Result: Log in again as [email protected]. You will now have full access to the Admin Settings and all instance configurations.
Vulnerability Type: Stored XSS / Vertical Privilege Escalation.
Severity: 9.1 (Critical) | CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H
Risk: Complete compromise of the Appsmith instance. Attackers gain access to sensitive environment variables, database credentials, and the ability to modify any application within the instance.
Sanitize Sinks: Wrap all dynamic outputs in BasicCell.tsx and related components with DOMPurify.sanitize().
Hardened CSP: Implement a strict connect-src policy to prevent unauthorized API calls to administrative endpoints from XSS payloads.
https://github.com/user-attachments/assets/206a10e4-1619-4887-a552-99761baa0a29