Search
Find a vulnerability
Search criteria
2 vulnerabilities found for grpc_core by elixir-grpc
CVE-2026-121808 (GCVE-0-2026-121808)
Vulnerability from nvd – Published: 2026-11-13 15:48 – Updated: 2026-11-13 15:48
VLAI
EPSS
VEX
Title
Memory exhaustion via compressed Erlang terms in the elixir-grpc Erlpack codec
Summary
Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.
GRPC.Codec.Erlpack.decode/2 (grpc_core/lib/grpc/codec/erlpack.ex, and lib/grpc/codec/erlpack.ex before the package split) passes the message body to :erlang.binary_to_term/2. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server's max_body_size limit counts the compressed bytes of the request. A request with Content-Type: application/grpc+erlpack can therefore make the server allocate a term much larger than the request body.
This issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0.
Severity
CWE
- CWE-409 - Improper Handling of Highly Compressed Data (Data Amplification)
Assigner
References
6 references
Impacted products
4 products
| Vendor | Product | Version | |
|---|---|---|---|
| elixir-grpc | grpc |
Affected:
0.4.0 , < 1.0.0
(semver)
cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:* |
|
| elixir-grpc | grpc |
Affected:
25bcc569fe2cc4478531a6c546c923205fc751c9 , < 0332faae5f5e132314794dbfdae5edecbccea292
(git)
cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:* |
|
| elixir-grpc | grpc_core |
Affected:
1.0.0 , < 1.1.0
(semver)
cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:* |
|
| elixir-grpc | grpc_core |
Affected:
0332faae5f5e132314794dbfdae5edecbccea292 , < d6bc56ac75dfb6c02db2099909210a37c9be8db6
(git)
cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:* |
{
"containers": {
"cna": {
"affected": [
{
"collectionURL": "https://repo.hex.pm",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "grpc",
"packageURL": "pkg:hex/grpc",
"product": "grpc",
"programFiles": [
"lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "1.0.0",
"status": "affected",
"version": "0.4.0",
"versionType": "semver"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "elixir-grpc/grpc",
"packageURL": "pkg:github/elixir-grpc/grpc",
"product": "grpc",
"programFiles": [
"lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "0332faae5f5e132314794dbfdae5edecbccea292",
"status": "affected",
"version": "25bcc569fe2cc4478531a6c546c923205fc751c9",
"versionType": "git"
}
]
},
{
"collectionURL": "https://repo.hex.pm",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "grpc_core",
"packageURL": "pkg:hex/grpc_core",
"product": "grpc_core",
"programFiles": [
"lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "1.1.0",
"status": "affected",
"version": "1.0.0",
"versionType": "semver"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "elixir-grpc/grpc",
"packageURL": "pkg:github/elixir-grpc/grpc",
"product": "grpc_core",
"programFiles": [
"grpc_core/lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "d6bc56ac75dfb6c02db2099909210a37c9be8db6",
"status": "affected",
"version": "0332faae5f5e132314794dbfdae5edecbccea292",
"versionType": "git"
}
]
}
],
"configurations": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003e\u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e must be explicitly registered as a codec on the gRPC server.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "`GRPC.Codec.Erlpack` must be explicitly registered as a codec on the gRPC server."
}
],
"value": "GRPC.Codec.Erlpack must be explicitly registered as a codec on the gRPC server."
}
],
"cpeApplicability": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:*",
"versionEndExcluding": "1.0.0",
"versionStartIncluding": "0.4.0",
"vulnerable": true
},
{
"criteria": "cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:*",
"versionEndExcluding": "1.1.0",
"versionStartIncluding": "1.0.0",
"vulnerable": true
}
],
"negate": false,
"operator": "OR"
}
],
"operator": "AND"
}
],
"credits": [
{
"lang": "en",
"type": "reporter",
"value": "Jace"
},
{
"lang": "en",
"type": "finder",
"value": "zx"
},
{
"lang": "en",
"type": "reporter",
"value": "zx"
},
{
"lang": "en",
"type": "finder",
"value": "Jiacheng Zhong"
},
{
"lang": "en",
"type": "finder",
"value": "Zhun Wang"
},
{
"lang": "en",
"type": "finder",
"value": "Junmin Zhu"
},
{
"lang": "en",
"type": "finder",
"value": "Hongwei Li"
},
{
"lang": "en",
"type": "finder",
"value": "Han Dai"
},
{
"lang": "en",
"type": "finder",
"value": "Suyue Guo"
},
{
"lang": "en",
"type": "finder",
"value": "Songyang Peng"
},
{
"lang": "en",
"type": "finder",
"value": "Ziyue Pan"
},
{
"lang": "en",
"type": "reporter",
"value": "Hongwei Li"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Paulo Valente"
},
{
"lang": "en",
"type": "coordinator",
"value": "Jonatan M\u00e4nnchen / EEF"
},
{
"lang": "en",
"type": "coordinator",
"value": "Peter Ullrich"
}
],
"dateAssigned": "2026-11-12T20:11:46.000Z",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eImproper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eGRPC.Codec.Erlpack.decode/2\u003c/code\u003e (\u003ccode\u003egrpc_core/lib/grpc/codec/erlpack.ex\u003c/code\u003e, and \u003ccode\u003elib/grpc/codec/erlpack.ex\u003c/code\u003e before the package split) passes the message body to \u003ccode\u003e:erlang.binary_to_term/2\u003c/code\u003e. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server\u0027s \u003ccode\u003emax_body_size\u003c/code\u003e limit counts the compressed bytes of the request. A request with \u003ccode\u003eContent-Type: application/grpc+erlpack\u003c/code\u003e can therefore make the server allocate a term much larger than the request body.\u003c/p\u003e\n\u003cp\u003eThis issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.\n\n`GRPC.Codec.Erlpack.decode/2` (`grpc_core/lib/grpc/codec/erlpack.ex`, and `lib/grpc/codec/erlpack.ex` before the package split) passes the message body to `:erlang.binary_to_term/2`. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server\u0027s `max_body_size` limit counts the compressed bytes of the request. A request with `Content-Type: application/grpc+erlpack` can therefore make the server allocate a term much larger than the request body.\n\nThis issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0."
}
],
"value": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.\n\nGRPC.Codec.Erlpack.decode/2 (grpc_core/lib/grpc/codec/erlpack.ex, and lib/grpc/codec/erlpack.ex before the package split) passes the message body to :erlang.binary_to_term/2. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server\u0027s max_body_size limit counts the compressed bytes of the request. A request with Content-Type: application/grpc+erlpack can therefore make the server allocate a term much larger than the request body.\n\nThis issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0."
}
],
"impacts": [
{
"capecId": "CAPEC-130",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eAn unauthenticated client can make a server that registers the Erlpack codec allocate memory far beyond the size of the request with one request. Memory exhaustion affects every service on that node.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "An unauthenticated client can make a server that registers the Erlpack codec allocate memory far beyond the size of the request with one request. Memory exhaustion affects every service on that node."
}
],
"value": "An unauthenticated client can make a server that registers the Erlpack codec allocate memory far beyond the size of the request with one request. Memory exhaustion affects every service on that node."
}
]
}
],
"metrics": [
{
"cvssV4_0": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"baseScore": 8.2,
"baseSeverity": "HIGH",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "NONE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"version": "4.0",
"vulnAvailabilityImpact": "HIGH",
"vulnConfidentialityImpact": "NONE",
"vulnIntegrityImpact": "NONE",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-409",
"description": "CWE-409 Improper Handling of Highly Compressed Data (Data Amplification)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-11-13T15:48:49.871Z",
"orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"shortName": "EEF"
},
"references": [
{
"name": "Memory exhaustion via compressed Erlang terms in the elixir-grpc Erlpack codec",
"tags": [
"vendor-advisory",
"related"
],
"url": "https://github.com/elixir-grpc/grpc/security/advisories/GHSA-xxp9-7hp4-rqwp"
},
{
"name": "EEF CNA record for CVE-2026-121808",
"tags": [
"related"
],
"url": "https://cna.erlef.org/cves/CVE-2026-121808.html"
},
{
"name": "OSV record EEF-CVE-2026-121808",
"tags": [
"related"
],
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-121808"
},
{
"name": "Introducing commit 25bcc56 in elixir-grpc/grpc",
"tags": [
"related"
],
"url": "https://github.com/elixir-grpc/grpc/commit/25bcc569fe2cc4478531a6c546c923205fc751c9"
},
{
"name": "Fix commit 0332faa in elixir-grpc/grpc",
"tags": [
"patch"
],
"url": "https://github.com/elixir-grpc/grpc/commit/0332faae5f5e132314794dbfdae5edecbccea292"
},
{
"name": "Fix commit d6bc56a in elixir-grpc/grpc",
"tags": [
"patch"
],
"url": "https://github.com/elixir-grpc/grpc/commit/d6bc56ac75dfb6c02db2099909210a37c9be8db6"
}
],
"source": {
"discovery": "EXTERNAL"
},
"title": "Memory exhaustion via compressed Erlang terms in the elixir-grpc Erlpack codec",
"workarounds": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eRemove \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e from the server\u0027s \u003ccode\u003ecodecs\u003c/code\u003e list if the codec is not needed.\u003c/p\u003e\n\u003cp\u003eIf the codec is needed, register a wrapper codec in its place. The wrapper implements the \u003ccode\u003eGRPC.Codec\u003c/code\u003e behaviour, returns \u003ccode\u003e\"erlpack\"\u003c/code\u003e from \u003ccode\u003ename/0\u003c/code\u003e, raises in \u003ccode\u003edecode/2\u003c/code\u003e when the body starts with the bytes \u003ccode\u003e131, 80\u003c/code\u003e (the compressed term tag), and delegates all other calls to \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Remove `GRPC.Codec.Erlpack` from the server\u0027s `codecs` list if the codec is not needed.\n\nIf the codec is needed, register a wrapper codec in its place. The wrapper implements the `GRPC.Codec` behaviour, returns `\"erlpack\"` from `name/0`, raises in `decode/2` when the body starts with the bytes `131, 80` (the compressed term tag), and delegates all other calls to `GRPC.Codec.Erlpack`."
}
],
"value": "Remove GRPC.Codec.Erlpack from the server\u0027s codecs list if the codec is not needed.\n\nIf the codec is needed, register a wrapper codec in its place. The wrapper implements the GRPC.Codec behaviour, returns \"erlpack\" from name/0, raises in decode/2 when the body starts with the bytes 131, 80 (the compressed term tag), and delegates all other calls to GRPC.Codec.Erlpack."
}
],
"x_proofOfConcept": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003col\u003e\n\u003cli\u003eStart a gRPC server that registers \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e in its \u003ccode\u003ecodecs\u003c/code\u003e list.\u003c/li\u003e\n\u003cli\u003eBuild the payload with \u003ccode\u003e:erlang.term_to_binary(:binary.copy(\u0026lt;\u0026lt;0\u0026gt;\u0026gt;, 268_435_456), [:compressed])\u003c/code\u003e. The result is 260,932 bytes.\u003c/li\u003e\n\u003cli\u003eSend the payload as one uncompressed gRPC message (compressed flag 0) to any method of the server, with \u003ccode\u003eContent-Type: application/grpc+erlpack\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eObserve that the server allocates the 268,435,456-byte binary and passes it to the application callback.\u003c/li\u003e\n\u003c/ol\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "1. Start a gRPC server that registers `GRPC.Codec.Erlpack` in its `codecs` list.\n2. Build the payload with `:erlang.term_to_binary(:binary.copy(\u003c\u003c0\u003e\u003e, 268_435_456), [:compressed])`. The result is 260,932 bytes.\n3. Send the payload as one uncompressed gRPC message (compressed flag 0) to any method of the server, with `Content-Type: application/grpc+erlpack`.\n4. Observe that the server allocates the 268,435,456-byte binary and passes it to the application callback."
}
],
"value": "* Start a gRPC server that registers GRPC.Codec.Erlpack in its codecs list.\n* Build the payload with :erlang.term_to_binary(:binary.copy(\u003c\u003c0\u003e\u003e, 268_435_456), [:compressed]). The result is 260,932 bytes.\n* Send the payload as one uncompressed gRPC message (compressed flag 0) to any method of the server, with Content-Type: application/grpc+erlpack.\n* Observe that the server allocates the 268,435,456-byte binary and passes it to the application callback."
}
],
"x_technicalAnalysis": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003e\u003cstrong\u003e1. Codec selection\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe server selects the codec from the request \u003ccode\u003eContent-Type\u003c/code\u003e. \u003ccode\u003eapplication/grpc+erlpack\u003c/code\u003e selects \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e when the server registers it. No authentication step comes before this.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Size limit on compressed bytes only\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe server limits the request body with \u003ccode\u003emax_body_size\u003c/code\u003e, which counts the bytes received. With the gRPC compressed flag set to 0, the transport decompression limit does not apply, and the message reaches the codec unchanged.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Expansion inside \u003ccode\u003ebinary_to_term/2\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eGRPC.Codec.Erlpack.decode/2\u003c/code\u003e calls \u003ccode\u003e:erlang.binary_to_term(binary, [:safe])\u003c/code\u003e. The Erlang external term format has a compressed form (version byte 131, tag 80, a 32-bit uncompressed size, then a zlib stream). \u003ccode\u003ebinary_to_term/2\u003c/code\u003e inflates it in full, and the \u003ccode\u003e:safe\u003c/code\u003e option does not limit the size. \u003ccode\u003eensure_safe_term!/1\u003c/code\u003e runs after the call returns and accepts a large binary.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Result\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIn a test against grpc_server 1.0.6, a 260,932-byte request body decoded to a 268,435,456-byte binary, which the server passed to the application callback.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "**1. Codec selection**\n\nThe server selects the codec from the request `Content-Type`. `application/grpc+erlpack` selects `GRPC.Codec.Erlpack` when the server registers it. No authentication step comes before this.\n\n**2. Size limit on compressed bytes only**\n\nThe server limits the request body with `max_body_size`, which counts the bytes received. With the gRPC compressed flag set to 0, the transport decompression limit does not apply, and the message reaches the codec unchanged.\n\n**3. Expansion inside `binary_to_term/2`**\n\n`GRPC.Codec.Erlpack.decode/2` calls `:erlang.binary_to_term(binary, [:safe])`. The Erlang external term format has a compressed form (version byte 131, tag 80, a 32-bit uncompressed size, then a zlib stream). `binary_to_term/2` inflates it in full, and the `:safe` option does not limit the size. `ensure_safe_term!/1` runs after the call returns and accepts a large binary.\n\n**4. Result**\n\nIn a test against grpc_server 1.0.6, a 260,932-byte request body decoded to a 268,435,456-byte binary, which the server passed to the application callback."
}
],
"value": "1. Codec selection\n\nThe server selects the codec from the request Content-Type. application/grpc+erlpack selects GRPC.Codec.Erlpack when the server registers it. No authentication step comes before this.\n\n2. Size limit on compressed bytes only\n\nThe server limits the request body with max_body_size, which counts the bytes received. With the gRPC compressed flag set to 0, the transport decompression limit does not apply, and the message reaches the codec unchanged.\n\n3. Expansion inside binary_to_term/2\n\nGRPC.Codec.Erlpack.decode/2 calls :erlang.binary_to_term(binary, [:safe]). The Erlang external term format has a compressed form (version byte 131, tag 80, a 32-bit uncompressed size, then a zlib stream). binary_to_term/2 inflates it in full, and the :safe option does not limit the size. ensure_safe_term!/1 runs after the call returns and accepts a large binary.\n\n4. Result\n\nIn a test against grpc_server 1.0.6, a 260,932-byte request body decoded to a 268,435,456-byte binary, which the server passed to the application callback."
}
]
}
},
"cveMetadata": {
"assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"assignerShortName": "EEF",
"cveId": "CVE-2026-121808",
"datePublished": "2026-11-13T15:48:49.871Z",
"dateReserved": "2026-11-09T22:35:50.462Z",
"dateUpdated": "2026-11-13T15:48:49.871Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
CVE-2026-121808 (GCVE-0-2026-121808)
Vulnerability from cvelistv5 – Published: 2026-11-13 15:48 – Updated: 2026-11-13 15:48
VLAI
EPSS
VEX
Title
Memory exhaustion via compressed Erlang terms in the elixir-grpc Erlpack codec
Summary
Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.
GRPC.Codec.Erlpack.decode/2 (grpc_core/lib/grpc/codec/erlpack.ex, and lib/grpc/codec/erlpack.ex before the package split) passes the message body to :erlang.binary_to_term/2. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server's max_body_size limit counts the compressed bytes of the request. A request with Content-Type: application/grpc+erlpack can therefore make the server allocate a term much larger than the request body.
This issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0.
Severity
CWE
- CWE-409 - Improper Handling of Highly Compressed Data (Data Amplification)
Assigner
References
6 references
Impacted products
4 products
| Vendor | Product | Version | |
|---|---|---|---|
| elixir-grpc | grpc |
Affected:
0.4.0 , < 1.0.0
(semver)
cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:* |
|
| elixir-grpc | grpc |
Affected:
25bcc569fe2cc4478531a6c546c923205fc751c9 , < 0332faae5f5e132314794dbfdae5edecbccea292
(git)
cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:* |
|
| elixir-grpc | grpc_core |
Affected:
1.0.0 , < 1.1.0
(semver)
cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:* |
|
| elixir-grpc | grpc_core |
Affected:
0332faae5f5e132314794dbfdae5edecbccea292 , < d6bc56ac75dfb6c02db2099909210a37c9be8db6
(git)
cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:* |
{
"containers": {
"cna": {
"affected": [
{
"collectionURL": "https://repo.hex.pm",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "grpc",
"packageURL": "pkg:hex/grpc",
"product": "grpc",
"programFiles": [
"lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "1.0.0",
"status": "affected",
"version": "0.4.0",
"versionType": "semver"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "elixir-grpc/grpc",
"packageURL": "pkg:github/elixir-grpc/grpc",
"product": "grpc",
"programFiles": [
"lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "0332faae5f5e132314794dbfdae5edecbccea292",
"status": "affected",
"version": "25bcc569fe2cc4478531a6c546c923205fc751c9",
"versionType": "git"
}
]
},
{
"collectionURL": "https://repo.hex.pm",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "grpc_core",
"packageURL": "pkg:hex/grpc_core",
"product": "grpc_core",
"programFiles": [
"lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "1.1.0",
"status": "affected",
"version": "1.0.0",
"versionType": "semver"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.GRPC.Codec.Erlpack\u0027"
],
"packageName": "elixir-grpc/grpc",
"packageURL": "pkg:github/elixir-grpc/grpc",
"product": "grpc_core",
"programFiles": [
"grpc_core/lib/grpc/codec/erlpack.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.GRPC.Codec.Erlpack\u0027:decode/2"
}
],
"repo": "https://github.com/elixir-grpc/grpc",
"vendor": "elixir-grpc",
"versions": [
{
"lessThan": "d6bc56ac75dfb6c02db2099909210a37c9be8db6",
"status": "affected",
"version": "0332faae5f5e132314794dbfdae5edecbccea292",
"versionType": "git"
}
]
}
],
"configurations": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003e\u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e must be explicitly registered as a codec on the gRPC server.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "`GRPC.Codec.Erlpack` must be explicitly registered as a codec on the gRPC server."
}
],
"value": "GRPC.Codec.Erlpack must be explicitly registered as a codec on the gRPC server."
}
],
"cpeApplicability": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:elixir-grpc:grpc:*:*:*:*:*:*:*:*",
"versionEndExcluding": "1.0.0",
"versionStartIncluding": "0.4.0",
"vulnerable": true
},
{
"criteria": "cpe:2.3:a:elixir-grpc:grpc_core:*:*:*:*:*:*:*:*",
"versionEndExcluding": "1.1.0",
"versionStartIncluding": "1.0.0",
"vulnerable": true
}
],
"negate": false,
"operator": "OR"
}
],
"operator": "AND"
}
],
"credits": [
{
"lang": "en",
"type": "reporter",
"value": "Jace"
},
{
"lang": "en",
"type": "finder",
"value": "zx"
},
{
"lang": "en",
"type": "reporter",
"value": "zx"
},
{
"lang": "en",
"type": "finder",
"value": "Jiacheng Zhong"
},
{
"lang": "en",
"type": "finder",
"value": "Zhun Wang"
},
{
"lang": "en",
"type": "finder",
"value": "Junmin Zhu"
},
{
"lang": "en",
"type": "finder",
"value": "Hongwei Li"
},
{
"lang": "en",
"type": "finder",
"value": "Han Dai"
},
{
"lang": "en",
"type": "finder",
"value": "Suyue Guo"
},
{
"lang": "en",
"type": "finder",
"value": "Songyang Peng"
},
{
"lang": "en",
"type": "finder",
"value": "Ziyue Pan"
},
{
"lang": "en",
"type": "reporter",
"value": "Hongwei Li"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Paulo Valente"
},
{
"lang": "en",
"type": "coordinator",
"value": "Jonatan M\u00e4nnchen / EEF"
},
{
"lang": "en",
"type": "coordinator",
"value": "Peter Ullrich"
}
],
"dateAssigned": "2026-11-12T20:11:46.000Z",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eImproper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eGRPC.Codec.Erlpack.decode/2\u003c/code\u003e (\u003ccode\u003egrpc_core/lib/grpc/codec/erlpack.ex\u003c/code\u003e, and \u003ccode\u003elib/grpc/codec/erlpack.ex\u003c/code\u003e before the package split) passes the message body to \u003ccode\u003e:erlang.binary_to_term/2\u003c/code\u003e. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server\u0027s \u003ccode\u003emax_body_size\u003c/code\u003e limit counts the compressed bytes of the request. A request with \u003ccode\u003eContent-Type: application/grpc+erlpack\u003c/code\u003e can therefore make the server allocate a term much larger than the request body.\u003c/p\u003e\n\u003cp\u003eThis issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.\n\n`GRPC.Codec.Erlpack.decode/2` (`grpc_core/lib/grpc/codec/erlpack.ex`, and `lib/grpc/codec/erlpack.ex` before the package split) passes the message body to `:erlang.binary_to_term/2`. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server\u0027s `max_body_size` limit counts the compressed bytes of the request. A request with `Content-Type: application/grpc+erlpack` can therefore make the server allocate a term much larger than the request body.\n\nThis issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0."
}
],
"value": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in elixir-grpc grpc_core and grpc allows unauthenticated attackers to exhaust server memory via a compressed Erlang external term in a gRPC message body.\n\nGRPC.Codec.Erlpack.decode/2 (grpc_core/lib/grpc/codec/erlpack.ex, and lib/grpc/codec/erlpack.ex before the package split) passes the message body to :erlang.binary_to_term/2. That function inflates a compressed term in full before it returns. The codec sets no limit on the decoded size, and its type checks run only on the decoded term. The server\u0027s max_body_size limit counts the compressed bytes of the request. A request with Content-Type: application/grpc+erlpack can therefore make the server allocate a term much larger than the request body.\n\nThis issue affects grpc: from 0.4.0 before 1.0.0; grpc_core: from 1.0.0 before 1.1.0."
}
],
"impacts": [
{
"capecId": "CAPEC-130",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eAn unauthenticated client can make a server that registers the Erlpack codec allocate memory far beyond the size of the request with one request. Memory exhaustion affects every service on that node.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "An unauthenticated client can make a server that registers the Erlpack codec allocate memory far beyond the size of the request with one request. Memory exhaustion affects every service on that node."
}
],
"value": "An unauthenticated client can make a server that registers the Erlpack codec allocate memory far beyond the size of the request with one request. Memory exhaustion affects every service on that node."
}
]
}
],
"metrics": [
{
"cvssV4_0": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"baseScore": 8.2,
"baseSeverity": "HIGH",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "NONE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"version": "4.0",
"vulnAvailabilityImpact": "HIGH",
"vulnConfidentialityImpact": "NONE",
"vulnIntegrityImpact": "NONE",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-409",
"description": "CWE-409 Improper Handling of Highly Compressed Data (Data Amplification)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-11-13T15:48:49.871Z",
"orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"shortName": "EEF"
},
"references": [
{
"name": "Memory exhaustion via compressed Erlang terms in the elixir-grpc Erlpack codec",
"tags": [
"vendor-advisory",
"related"
],
"url": "https://github.com/elixir-grpc/grpc/security/advisories/GHSA-xxp9-7hp4-rqwp"
},
{
"name": "EEF CNA record for CVE-2026-121808",
"tags": [
"related"
],
"url": "https://cna.erlef.org/cves/CVE-2026-121808.html"
},
{
"name": "OSV record EEF-CVE-2026-121808",
"tags": [
"related"
],
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-121808"
},
{
"name": "Introducing commit 25bcc56 in elixir-grpc/grpc",
"tags": [
"related"
],
"url": "https://github.com/elixir-grpc/grpc/commit/25bcc569fe2cc4478531a6c546c923205fc751c9"
},
{
"name": "Fix commit 0332faa in elixir-grpc/grpc",
"tags": [
"patch"
],
"url": "https://github.com/elixir-grpc/grpc/commit/0332faae5f5e132314794dbfdae5edecbccea292"
},
{
"name": "Fix commit d6bc56a in elixir-grpc/grpc",
"tags": [
"patch"
],
"url": "https://github.com/elixir-grpc/grpc/commit/d6bc56ac75dfb6c02db2099909210a37c9be8db6"
}
],
"source": {
"discovery": "EXTERNAL"
},
"title": "Memory exhaustion via compressed Erlang terms in the elixir-grpc Erlpack codec",
"workarounds": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eRemove \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e from the server\u0027s \u003ccode\u003ecodecs\u003c/code\u003e list if the codec is not needed.\u003c/p\u003e\n\u003cp\u003eIf the codec is needed, register a wrapper codec in its place. The wrapper implements the \u003ccode\u003eGRPC.Codec\u003c/code\u003e behaviour, returns \u003ccode\u003e\"erlpack\"\u003c/code\u003e from \u003ccode\u003ename/0\u003c/code\u003e, raises in \u003ccode\u003edecode/2\u003c/code\u003e when the body starts with the bytes \u003ccode\u003e131, 80\u003c/code\u003e (the compressed term tag), and delegates all other calls to \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Remove `GRPC.Codec.Erlpack` from the server\u0027s `codecs` list if the codec is not needed.\n\nIf the codec is needed, register a wrapper codec in its place. The wrapper implements the `GRPC.Codec` behaviour, returns `\"erlpack\"` from `name/0`, raises in `decode/2` when the body starts with the bytes `131, 80` (the compressed term tag), and delegates all other calls to `GRPC.Codec.Erlpack`."
}
],
"value": "Remove GRPC.Codec.Erlpack from the server\u0027s codecs list if the codec is not needed.\n\nIf the codec is needed, register a wrapper codec in its place. The wrapper implements the GRPC.Codec behaviour, returns \"erlpack\" from name/0, raises in decode/2 when the body starts with the bytes 131, 80 (the compressed term tag), and delegates all other calls to GRPC.Codec.Erlpack."
}
],
"x_proofOfConcept": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003col\u003e\n\u003cli\u003eStart a gRPC server that registers \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e in its \u003ccode\u003ecodecs\u003c/code\u003e list.\u003c/li\u003e\n\u003cli\u003eBuild the payload with \u003ccode\u003e:erlang.term_to_binary(:binary.copy(\u0026lt;\u0026lt;0\u0026gt;\u0026gt;, 268_435_456), [:compressed])\u003c/code\u003e. The result is 260,932 bytes.\u003c/li\u003e\n\u003cli\u003eSend the payload as one uncompressed gRPC message (compressed flag 0) to any method of the server, with \u003ccode\u003eContent-Type: application/grpc+erlpack\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eObserve that the server allocates the 268,435,456-byte binary and passes it to the application callback.\u003c/li\u003e\n\u003c/ol\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "1. Start a gRPC server that registers `GRPC.Codec.Erlpack` in its `codecs` list.\n2. Build the payload with `:erlang.term_to_binary(:binary.copy(\u003c\u003c0\u003e\u003e, 268_435_456), [:compressed])`. The result is 260,932 bytes.\n3. Send the payload as one uncompressed gRPC message (compressed flag 0) to any method of the server, with `Content-Type: application/grpc+erlpack`.\n4. Observe that the server allocates the 268,435,456-byte binary and passes it to the application callback."
}
],
"value": "* Start a gRPC server that registers GRPC.Codec.Erlpack in its codecs list.\n* Build the payload with :erlang.term_to_binary(:binary.copy(\u003c\u003c0\u003e\u003e, 268_435_456), [:compressed]). The result is 260,932 bytes.\n* Send the payload as one uncompressed gRPC message (compressed flag 0) to any method of the server, with Content-Type: application/grpc+erlpack.\n* Observe that the server allocates the 268,435,456-byte binary and passes it to the application callback."
}
],
"x_technicalAnalysis": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003e\u003cstrong\u003e1. Codec selection\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe server selects the codec from the request \u003ccode\u003eContent-Type\u003c/code\u003e. \u003ccode\u003eapplication/grpc+erlpack\u003c/code\u003e selects \u003ccode\u003eGRPC.Codec.Erlpack\u003c/code\u003e when the server registers it. No authentication step comes before this.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Size limit on compressed bytes only\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe server limits the request body with \u003ccode\u003emax_body_size\u003c/code\u003e, which counts the bytes received. With the gRPC compressed flag set to 0, the transport decompression limit does not apply, and the message reaches the codec unchanged.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Expansion inside \u003ccode\u003ebinary_to_term/2\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eGRPC.Codec.Erlpack.decode/2\u003c/code\u003e calls \u003ccode\u003e:erlang.binary_to_term(binary, [:safe])\u003c/code\u003e. The Erlang external term format has a compressed form (version byte 131, tag 80, a 32-bit uncompressed size, then a zlib stream). \u003ccode\u003ebinary_to_term/2\u003c/code\u003e inflates it in full, and the \u003ccode\u003e:safe\u003c/code\u003e option does not limit the size. \u003ccode\u003eensure_safe_term!/1\u003c/code\u003e runs after the call returns and accepts a large binary.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Result\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIn a test against grpc_server 1.0.6, a 260,932-byte request body decoded to a 268,435,456-byte binary, which the server passed to the application callback.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "**1. Codec selection**\n\nThe server selects the codec from the request `Content-Type`. `application/grpc+erlpack` selects `GRPC.Codec.Erlpack` when the server registers it. No authentication step comes before this.\n\n**2. Size limit on compressed bytes only**\n\nThe server limits the request body with `max_body_size`, which counts the bytes received. With the gRPC compressed flag set to 0, the transport decompression limit does not apply, and the message reaches the codec unchanged.\n\n**3. Expansion inside `binary_to_term/2`**\n\n`GRPC.Codec.Erlpack.decode/2` calls `:erlang.binary_to_term(binary, [:safe])`. The Erlang external term format has a compressed form (version byte 131, tag 80, a 32-bit uncompressed size, then a zlib stream). `binary_to_term/2` inflates it in full, and the `:safe` option does not limit the size. `ensure_safe_term!/1` runs after the call returns and accepts a large binary.\n\n**4. Result**\n\nIn a test against grpc_server 1.0.6, a 260,932-byte request body decoded to a 268,435,456-byte binary, which the server passed to the application callback."
}
],
"value": "1. Codec selection\n\nThe server selects the codec from the request Content-Type. application/grpc+erlpack selects GRPC.Codec.Erlpack when the server registers it. No authentication step comes before this.\n\n2. Size limit on compressed bytes only\n\nThe server limits the request body with max_body_size, which counts the bytes received. With the gRPC compressed flag set to 0, the transport decompression limit does not apply, and the message reaches the codec unchanged.\n\n3. Expansion inside binary_to_term/2\n\nGRPC.Codec.Erlpack.decode/2 calls :erlang.binary_to_term(binary, [:safe]). The Erlang external term format has a compressed form (version byte 131, tag 80, a 32-bit uncompressed size, then a zlib stream). binary_to_term/2 inflates it in full, and the :safe option does not limit the size. ensure_safe_term!/1 runs after the call returns and accepts a large binary.\n\n4. Result\n\nIn a test against grpc_server 1.0.6, a 260,932-byte request body decoded to a 268,435,456-byte binary, which the server passed to the application callback."
}
]
}
},
"cveMetadata": {
"assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"assignerShortName": "EEF",
"cveId": "CVE-2026-121808",
"datePublished": "2026-11-13T15:48:49.871Z",
"dateReserved": "2026-11-09T22:35:50.462Z",
"dateUpdated": "2026-11-13T15:48:49.871Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}