Mar 1, 2026·5 min read·37 visits
Vulnerable Erlang/Elixir build tools deserialize untrusted API responses using unsafe methods. An attacker controlling a package mirror or network position can inject malicious Erlang terms, causing VM crashes (DoS) or executing arbitrary code.
A critical unsafe deserialization vulnerability exists in `hex_core`, the reference implementation for the Hex package manager API, affecting downstream tools like `hex` and `rebar3`. The flaw stems from the use of the unsafe `binary_to_term/1` function when processing HTTP response bodies, allowing attackers to trigger Denial of Service via atom table exhaustion or potentially achieve Remote Code Execution through object injection.
The vulnerability resides in hex_core, a foundational library used by the Erlang and Elixir ecosystems to interact with the Hex package registry. Specifically, the library failed to validate serialized Erlang terms received from the Hex API before decoding them. This affects hex (the Mix package manager) and rebar3 (the Erlang build tool), meaning the vulnerability extends to the majority of build pipelines in this ecosystem.
When a client requests package information, the Hex API often returns data encoded in the Erlang External Term Format. The vulnerable code used the native binary_to_term/1 function to decode this data. This function is inherently unsafe when applied to untrusted input because it deserializes data into memory without restriction, creating atoms and other complex structures directly within the Erlang VM.
By spoofing a Hex API response—either through a compromised mirror, a Man-in-the-Middle (MitM) attack, or a malicious proxy—an attacker can supply a crafted binary payload. This payload is processed automatically by the build tool, leading to immediate resource exhaustion or the instantiation of malicious objects.
The root cause is the misuse of binary_to_term/1 for processing external data (CWE-502). In the Erlang Beam VM, atoms (literals used as identifiers) are stored in a non-garbage-collected global table with a fixed limit (default 1,048,576). Once created, an atom persists until the VM terminates. The binary_to_term/1 function creates new atoms if they do not already exist in the table.
Mechanism 1: Atom Table Exhaustion (DoS) An attacker can construct a payload containing hundreds of thousands of unique, random atoms. When the client deserializes this payload, the VM attempts to register all new atoms. This rapidly fills the atom table. Once the limit is reached, the Erlang VM allocates no further memory for atoms and crashes instantly, terminating the build process.
Mechanism 2: Object Injection (RCE)
The Erlang External Term Format supports the serialization of anonymous functions (funs). If an attacker injects a serialized fun into the response, and the application logic subsequently invokes that term (or passes it to a function expecting a callback), the attacker's code executes within the context of the build tool. This allows for arbitrary system command execution via os:cmd/1 wrapped inside the injected function.
The vulnerability existed in the HTTP response handling logic within src/hex_api.erl. The code implicitly trusted the Content-Type: application/vnd.hex+erlang header and passed the body directly to the deserializer.
Vulnerable Code (Pre-Patch):
%% src/hex_api.erl
case binary:match(ContentType, ?ERL_CONTENT_TYPE) of
{_, _} ->
%% CRITICAL: Unsafe deserialization of untrusted binary data
%% This permits atom creation and function instantiation
{ok, {Status, RespHeaders, binary_to_term(RespBody)}};
nomatch ->
{ok, {Status, RespHeaders, nil}}
end.Patched Code (Post-Patch):
The fix introduces two layers of defense. First, it switches to binary_to_term/2 with the safe option, which forbids the creation of new atoms. Second, it implements a recursive validator that ensures the deserialized term only contains inert data types (lists, tuples, maps, numbers, binaries) and explicitly rejects dangerous types like fun or port.
%% src/hex_api.erl
case binary:match(ContentType, ?ERL_CONTENT_TYPE) of
{_, _} ->
%% FIX 1: Use the [safe] flag to prevent atom creation
case binary_to_term(RespBody, [safe]) of
Term ->
%% FIX 2: Recursively validate the structure against a whitelist
case hex_safe_binary_to_term:validate(Term) of
ok ->
{ok, {Status, RespHeaders, Term}};
{error, Reason} ->
{error, Reason}
end;
Error ->
{error, Error}
end;
nomatch ->
...
end.To exploit this vulnerability, an attacker requires a position in the network to modify traffic or control over a repository mirror configured by the victim. The attack does not require authentication to the victim's machine, but it does require the victim to initiate a build command (User Interaction).
mix deps.get). If TLS validation is disabled or compromised, this is trivial. Alternatively, the attacker sets up a rogue mirror.%% DoS Payload: List of 1M unique atoms
Payload = term_to_binary([list_to_atom(integer_to_list(I)) || I <- lists:seq(1, 1000000)]).
%% RCE Payload: Embedded fun
Payload = term_to_binary(fun() -> os:cmd("rm -rf /") end).Content-Type: application/vnd.hex+erlang.fun and the client logic executes it (e.g., expecting a callback), the payload triggers.Despite the CVSS v4.0 score of 2.0 (which heavily penalizes the Attack Requirements and User Interaction), the technical impact is critical for affected environments. Development and CI/CD pipelines are the primary targets.
rebar3 or mix halts deployment pipelines, potentially blocking releases or causing operational downtime in automated environments.CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
hex_core hexpm | < 0.12.1 | 0.12.1 |
hex hexpm | < 2.3.2 | 2.3.2 |
rebar3 erlang | < 3.27.0 | 3.27.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-502 (Deserialization of Untrusted Data) |
| Secondary CWE | CWE-400 (Uncontrolled Resource Consumption) |
| Attack Vector | Network |
| CVSS v4.0 | 2.0 (Low) |
| Impact | DoS / RCE |
| Exploit Status | PoC Available |
The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.
CVE-2026-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.
CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.
A critical hostname verification bypass vulnerability exists in the Netty network application framework when configured as a TLS client. When a developer registers a custom plain X509TrustManager, Netty wraps it inside an X509TrustManagerWrapper to adapt it to the X509ExtendedTrustManager API. However, this wrapper discards the SSLEngine context, bypassing critical hostname checks. Because the wrapper is identified as an X509ExtendedTrustManager, standard cryptographic engines and Netty's OpenSSL wrappers do not re-wrap it, failing to execute any hostname validation. Consequently, clients silently accept certificates for any host, enabling unauthenticated Man-in-the-Middle (MitM) attacks.
An uncontrolled resource pre-allocation flaw in the Netty Redis codec module allows remote unauthenticated attackers to cause a denial of service (OutOfMemoryError) by sending a crafted Redis Serialization Protocol (RESP) array header.
CVE-2026-50020 is a medium-severity HTTP Request Smuggling/Response Smuggling vulnerability (CWE-444) within the Netty asynchronous network application framework. The flaw resides in Netty's HTTP codec implementation, specifically the HttpObjectDecoder class, which silently consumes arbitrary ISO control bytes preceding the first request line.