
Two chained CVEs in Frappe LMS let a student-level user achieve RCE on the server. CVE-2026-39405 (path traversal to RCE) and CVE-2026-34606 (stored XSS sanitization bypass) combine into a full exploit chain. Patch to v2.50.1 immediately. - CVE-2026-39405: The extract_package function passes an unsanitized chapter title into os.path.join before extracting a SCORM ZIP. A title like ../../../../../apps/lms/lms/lms lets an attacker overwrite http://api.py with a backdoored version. The backdoor listens on the public get_job_details endpoint, executing shell commands when the string |$> appears in the job parameter. - CVE-2026-34606: Frappe LMS sanitizes rich-text on submission, but passes stored HTML through BeautifulSoup's get_text() to generate profile previews. get_text() reassembles split text nodes into a valid script tag without re-sanitizing, so a bio payload like p tags splitting script>alert()< reassembles into a live XSS payload visible to any viewer. - The chain: student injects XSS into their bio, an admin views the profile, the payload triggers the SCORM upload and overwrites http://api.py, then the backdoor endpoint is open to unauthenticated callers. Confirmed vulnerable: v2.44.0. Fixed: v2.50.1. PoC code is published in the Rhino Security Labs CVE GitHub repository. Hunt for unexpected modifications to apps/lms/lms/api.py and review web server logs for POST requests to /api/method/lms.lms.api.get_job_details containing |$>. #DFIR_Radar

