GHSA-grp7-v8xh-rj7h: Erlang/grpc vulnerability
Summary
GRPC.Codec.Erlpack.decode/2 calls :erlang.binarytoterm/1 directly on the raw gRPC message body without the :safe option. Any unauthenticated peer that can reach a gRPC endpoint with Content-Type: application/grpc+erlpack can crash the entire BEAM node via atom table exhaustion or, if a decoded fun term flows into a call site that invokes it, achieve remote code execution inside the server process.
Details
Root cause — lib/grpc/codec/erlpack.ex implements decode/2 as a bare :erlang.binarytoterm(binary) call with no :safe flag, no size limit, and no type validation. This has two independent exploitation paths:
1. DoS via atom exhaustion — BEAM atoms are never garbage-collected and the global atom table is bounded (~1,048,576 entries). A crafted payload encoding large numbers of fresh atoms saturates the table and crashes the entire VM, taking down all applications on the node.
2. RCE via fun materialization — Without :safe, binarytoterm/1 reconstructs fun and external-fun terms from wire data. If the decoded value reaches any call site that applies it (e.g. Enum.map, Task.async, direct invocation), attacker-controlled code executes inside the server process.
Configuration requirement: GRPC.Codec.Erlpack is not registered by default and must be explicitly added to the server's codecs option.
PoC
1. Start a gRPC server with codecs: [GRPC.Codec.Erlpack]. 2. Open an HTTP/2 connection to the server. 3. Send a gRPC-framed POST to any RPC path with Content-Type: application/grpc+erlpack and a body of :erlang.termtobinary(fn -> <maliciouscode> end). 4. The server's decode/2 materializes the fun; any downstream call site that invokes the decoded value executes the attacker's code. 5. For DoS only: send payloads encoding fresh atoms in a loop until the atom table is exhausted and the VM crashes.
Impact
Affects grpc ≥ 0.4.0. Any server that explicitly registers GRPC.Codec.Erlpack is vulnerable to unauthenticated node-level DoS and potentially RCE.
References
Introduction commit: https://github.com/elixir-grpc/grpc/commit/25bcc569fe2cc4478531a6c546c923205fc751c9 Patch commit: https://github.com/elixir-grpc/grpc/commit/272a97a5ea1b46af1819f14a831fcf35fc91f992
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
erlang/grpcto a version that resolves this vulnerability.Fixed in 1.0.0 - Upgrade
Upgrade
elixir-grpc/grpcto a version that resolves this vulnerability.Patch 272a97a5ea1b46af1819f14a831fcf35fc91f992 - Configuration
Ensure GRPC.Codec.Erlpack is not explicitly added to the server's `codecs` option. Since GRPC.Codec.Erlpack is not registered by default, remove it from `codecs` to prevent `GRPC.Codec.Erlpack.decode/2` from calling `:erlang.binary_to_term(binary)` on untrusted payloads.
gRPC server (GRPC.Codec.Erlpack) codecs = Do not register GRPC.Codec.Erlpack (leave it out of the server's codecs option) - Compensating control
Use a proxy/WAF/LB rule to block requests to gRPC endpoints where the request header `Content-Type` is `application/grpc+erlpack`, since unauthenticated peers using that content type can trigger atom table exhaustion/VM crashes.
Event History
Frequently Asked Questions
Is authentication required to exploit this issue?
No. An unauthenticated peer can exploit it if they can reach a gRPC endpoint that accepts the application/grpc+erlpack content type.
When does the impact extend from denial of service to code execution?
Atom-table exhaustion can crash the entire BEAM node. Remote code execution requires a decoded fun or external-fun term to flow to an application call site that invokes it, such as direct invocation, Enum.map, or Task.async.