PT-2026-133309

CVE-2026-115274

·

تاريخ النشر

2026-10-30

·

تاريخ التحديث

2026-10-30

الوصف من

CVSS 4.0
6.9
متوسطة
المقاييسAV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N

Summary

Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library's JSON module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.
JSON.decode/1 and JSON.decode!/1 convert each integer literal with erlang:binary to integer/1 and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises SystemLimitError. Applications generated by phx.new decode request bodies with JSON in Plug.Parsers, before routing and authentication.
This issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.

Details

1. Default integer decoder
JSON.decode/1 and JSON.decode!/1 call JSON.decode/3 without an integer decoder. Up to the removal of Erlang/OTP 26 support, JSON.decode/3 used the vendored elixir json module, whose default is fun erlang:binary to integer/1. It now calls OTP's :json.decode/3, which has the same default. The JSON.decode/3 documentation names &String.to integer/1 as the default, which converts the same way.
2. Unbounded conversion
The decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run's length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises SystemLimitError, which JSON.decode/3 does not catch.
3. Reach
phx.new sets config :phoenix, :json library, JSON, and the generated endpoint passes Phoenix.json library() to Plug.Parsers as json decoder. Plug.Parsers runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the SystemLimitError into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its decoding integer digit limit (1024 by default) before conversion.

Proof of concept

  1. Build body = "[" <> Enum.map join(1..7, ",", fn -> String.duplicate("9", 1 000 000) end) <> "]", a body of about 7 MB.
  2. JSON.decode(body) returns {:ok, ...} after about one second of CPU.
  3. JSON.decode(String.duplicate("9", 1 000 000)) takes 100 to 300 times as long as JSON.decode(""" <> String.duplicate("9", 1 000 000) <> """).
  4. JSON.decode(String.duplicate("9", 1 400 000)) raises SystemLimitError.

Impact

An unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with JSON, including new Phoenix applications by default.

Workarounds

Decode untrusted JSON with an integer decoder that rejects long digit runs before conversion:
elixir
defmodule SafeJSON do
 def decode!(binary) do
  case JSON.decode(binary, :ok, integer: &safe integer/1) do
   {decoded, :ok, ""} -> decoded
    -> raise ArgumentError, "invalid JSON"
  end
 end

 defp safe integer(digits) when byte size(digits) <= 1024, do: String.to integer(digits)
 defp safe integer( digits), do: raise(ArgumentError, "integer too long")
end
In Phoenix, pass SafeJSON to Plug.Parsers as json decoder instead of Phoenix.json library().
قابلية الاستغلال
استغلال
إصلاح
الباحثون
المورّدون
المنتجات المتأثرة
المعرّفات ذات الصلة
وجدت خطأً في الوصف؟ لديك ما تضيفه؟ لا تتردد في مراسلتنا 👾