| Type | software |
| Product Environment | web |
| Product Name | Frappe LMS |
| Product Vendor | Frappe |
| Product Version | 2.35.0 |
| Product Link | https://github.com/frappe/lms |
| Vulnerability Name | Cross-Site Scripting |
| Severity | High |
|
CVSS String
|
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N |
| CVSS Score | 7.3 |
| CVE ID | CVE-2025-11282 |
| Vendor Acknowledgement | Yes |
|
Affected digital Assets
|
10 |
|
Affected Users
|
50000 |
| Date of Reporting | Sep 21, 2025 |
| PoC Exploit | - |
| Credit | 0xhamy,KhanMarshai |
Frappe LMS version 2.35.0 is vulnerable to a file upload flaw that enables stored cross-site scripting (XSS).
The application incorrectly handles uploaded HTML and SVG files. Although the UI shows visual error messages, malicious files can still be uploaded and later executed in users’ browsers.
The file upload feature allows users to bypass file-type restrictions by switching from “Image Files” to “All Files” and uploading crafted payloads.
While the platform presents a visual error, the files are still saved, and references to them can be accessed.
When other users or administrators view the uploaded file, arbitrary JavaScript payloads execute in their browser.
While this did not initially lead us to account takeover via making POST requests like this report, at the time of reporting, we were still able to steal user's email addresses and admin status.
A few weeks after this vulnerability was patched, I figured that instead of making a POST request to change admin's password, why not just mimic user clicks?
For example, with JavaScript we can technically find elements and fill them with data, we can also target elements and click on them, no mouse required, JavaScript can do it just fine.
This led me to the creation of the following payload:
https://gist.github.com/0xHamy/02b03a5264ac30bcc24bc4bf306cfb3f?short_path=69ba2bd
This SVG image, when loaded in a browser, triggers a JavaScript function via its onload event to automate a password change for an "Administrator" user on Frappe LMS. It creates a hidden iframe to load the user's settings tab, waits for it to fully load, then accesses the iframe's document (or falls back to the main document if access fails due to same-origin policy). Inside the target document, it locates a collapsible "Change Password" section, expands it if collapsed, fills the new password field with newPassword-123 and dispatches input/change events to simulate user interaction and trigger any validation.
After a short delay, it finds and clicks the "Save" button to apply the change, logging progress to the console throughout, and finally removes the iframe after 10 seconds for cleanup; if the iframe creation fails entirely, it attempts the same automation directly on the current page's document.
Our existing XSS severity escalation methods has always been these two:
But both of these rely on the absence of CSRF protection, however the new payload we created can bypass CSRF because it has nothing to do with CSRF, the more efficient way to block it would be using strict CSP rules and preventing XSS itself. Because as of right now, we are able to bypass all of Frappe's framework's security protections.
The initial severity was 4.6 (Medium) with a CVSS string of CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N, but now that we are able to takeover admin's account if they visit a malicious SVG/HTML file, the severity increases to:
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:NLog in as administrator.
Navigate to:
http://127.0.0.1:8000/app/user?enabled=1
Create a student account.
LMS Student.Create an assignment.
Create a course and attach the assignment.
Log in as the student user.
Upload a malicious file.
Note: An error appears, but the file is still saved.
Trigger the payload.
x) when prompted.Set up a server to capture data.
Alternative vector: SVG uploads.