Skip to content
winfunc
Disclosure record · Ray

Unauthenticated Serve replica gRPC requests allow arbitrary code execution

Every Python Serve replica exposed an unauthenticated pickle sink on an ephemeral node port

RayDisclosedSource record

CVSS 3.1 base score

Critical
9.8/ 10

Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Record

Project
Ray
Severity
Critical9.8
CVE
None listed
Disclosed
Trace
3 steps

Summary

Source

ASGIRequest.pickled_request_metadata

src/ray/protobuf/serve.proto:525

Sink

_wrap_grpc_call.wrapper()

python/ray/serve/_private/replica.py:248

A network caller could invoke an internal Serve replica gRPC method without the configured Ray token and place a Python pickle reduction in pickled_request_metadata, causing arbitrary code execution in the replica process before normal request dispatch.

Root cause. Replica.__init__ created ASGIService with raw grpc.aio.server() and _on_initialized bound it through add_insecure_port("[::]:0"). The shared token interceptor and TLS helper were skipped, while _wrap_grpc_call performed pickle.loads() as its first operation.

Remediation evidence. Ray opened exact PR #65189 after the disclosure and merged it on 5 August 2026. The patch moves both server and client sides to Ray's shared authenticated/TLS-aware gRPC helpers and adds token-mode coverage.

Severity

Attack vectorAV
Network
Attack complexityAC
Low
Privileges requiredPR
None
User interactionUI
None
ScopeS
Unchanged
ConfidentialityC
High
IntegrityI
High
AvailabilityA
High

Metric values as published in the disclosure vector. Meters show how far each value raises exposure.

Source-to-sink trace

  1. Source · attacker-controlledsrc/ray/protobuf/serve.proto:525

    ASGIRequest.pickled_request_metadata

  2. Step 01python/ray/serve/_private/replica.py:Replica.__init__()

    Each initialized Python Serve replica created a raw asynchronous gRPC server outside Ray's authentication factory.

    python
    self._server = grpc.aio.server(options=grpc_options)
    
  3. Step 02python/ray/serve/_private/replica.py:_on_initialized()

    The internal service was exposed on an all-interface ephemeral port without TLS or token interception.

    python
    port = self._server.add_insecure_port("[::]:0")
    await self._server.start()
    
  4. Step 03python/ray/serve/_private/replica.py:248

    Any registered ASGI RPC reached an unconditional deserialization sink before admission control or user-code dispatch.

    python
    request_metadata = pickle.loads(request.pickled_request_metadata)
    
  5. Sinkpython/ray/serve/_private/replica.py:248

    _wrap_grpc_call.wrapper()

Impact

Reported impact

The attacker obtains arbitrary Python/process execution in an existing Serve replica despite token authentication. That process authority can expose inherited secrets, files, cloud identity, application state, and network access, and can tamper with or terminate the service.

Attack surface

Ephemeral ray.serve.ASGIService gRPC ports reachable on Ray cluster or VPN node networks when a Python Serve replica is initialized.

Preconditions

The attacker needs TCP reachability to a Ray node and the replica's discoverable ephemeral port. The documented token mode may be enabled; no valid token, deployment handle, or victim interaction is required.

Attack path

  1. 1

    Identify an initialized Python Serve replica's open ASGIService port.

  2. 2

    Invoke a registered gRPC method without valid authorization metadata.

  3. 3

    Place a pickle reduction in ASGIRequest.pickled_request_metadata.

  4. 4

    The handler deserializes it immediately under the replica worker identity.

Proof of concept

Reproduction5 stages
  1. 01

    Environment setup

    Use an isolated Ray cluster on the vulnerable revision with RAY_AUTH_MODE=token and deploy one harmless Python Serve replica.

  2. 02

    Target configuration

    Allow the test client to reach node ephemeral ports, but do not provide it the Ray authentication token.

  3. 03

    Exploit delivery

    Discover the ASGIService port and send one protobuf request whose pickled_request_metadata contains a harmless proof reduction, such as writing a marker in the disposable container.

  4. 04

    Expected response

    The marker appears in the replica before request metadata validation; missing or invalid authorization metadata does not block the RPC.

  5. 05

    Outcome

    PR #65189 attaches Ray's asynchronous authentication interceptor and uses the corresponding client/channel helper, so the same unauthenticated request is rejected before deserialization.

Remediation

Guidance

Upgrade to a Ray release containing PR #65189. Construct every internal gRPC server and client through the shared token- and TLS-aware helpers, and never deserialize Python pickle before authentication.

Before and after−2+4
self._server = grpc.aio.server(options=options)self._server.add_insecure_port("[::]:0")self._server = create_grpc_server_with_interceptors(    asynchronous=True, options=options)add_port_to_grpc_server(self._server, address)
View original source record

Check the upstream record for the project's current remediation status.

Your codebase

Investigate the paths that matter in your codebase.

Winfunc can trace relevant code paths, preserve supporting evidence, and prepare remediation suggestions for engineering review within an agreed scope.