scim_proton and kanidm_proto have an authenticated process abort via SCIM filter stack exhaustion
Description
Summary
A single unauthenticated GET to any /scim/v1/... endpoint with a ?filter= query string of a few thousand nested parentheses (≈ 4–12 KB) drives the recursive-descent PEG parser past the worker thread's stack guard page. Rust responds to stack overflow with std::process::abort() — the entire kanidmd process exits. The parse runs inside axum's Query extractor, before any handler body and therefore before any ACL check.
Details
The SCIM filter grammar recurses on ( and not ( with no depth bound.
**proto/src/scim_v1/mod.rs:263-433** — peg::parser! { grammar scimfilter() ... }:
// line 281
"not" separator()+ "(" e:parse() ")" { ScimFilter::Not(Box::new(e)) }
// line 293
"(" e:parse() ")" { e }
Both rules re-enter parse() without a depth counter.
**proto/src/scim_v1/mod.rs:442-447** — impl FromStr for ScimFilter calls scimfilter::parse(input) directly on the raw string with no length or depth pre-check.
**proto/src/scim_v1/mod.rs:80-81** — ScimEntryGetQuery.filter is #[serde_as(as = "Option")], so deserialising the query struct invokes ScimFilter::from_str on attacker bytes.
Unauthenticated reachability — nine handlers in server/core/src/https/v1_scim.rs (route table at lines 865-1029) take Query as an argument: /scim/v1/Entry, /scim/v1/Entry/{id}, /scim/v1/Person/{id}, /scim/v1/Application, /scim/v1/Application/{id}, /scim/v1/Class, /scim/v1/Attribute, /scim/v1/Message, /scim/v1/Message/{id}. The SCIM router is merged unconditionally for every server role (server/core/src/https/mod.rs:312).
Axum extracts handler arguments before the handler body runs. The preceding VerifiedClientInformation extractor (server/core/src/https/extractors/mod.rs:16-91) always returns Ok (line 89) regardless of credentials; authorization is deferred to the handler body, which is never reached.
The existing semantic depth limit (DEFAULT_LIMIT_FILTER_DEPTH_MAX = 12, server/lib/src/constants/mod.rs:212) is enforced in Filter::from_scim_ro (server/lib/src/filter.rs:786) after the PEG parse has already produced an AST, so it cannot prevent the parser itself from blowing the stack.
The production daemon (server/daemon/src/main.rs:735-744) uses new_multi_thread() with default 2 MiB worker stacks; hyper's max_buf_size (~400 KiB) is not lowered (server/core/src/https/mod.rs:708-727), so a 12 KB URI is accepted.
An identical unbounded grammar exists in libs/scim_proto/src/filter.rs:112-276 (not network-reachable, but should be fixed in the same patch).
PoC
curl -sk "https://idm.example.com/scim/v1/Application?filter=$(python3 -c 'print("("*3000+"a+pr"+")"*3000)')"
# → curl: (52) Empty reply from server
# → server journal: "fatal runtime error: stack overflow, aborting", SIGABRT
Release-build threshold measured at ~2 000 nesting levels / ~4 KB:
$ cargo test --release -p kanidm_proto --test scim_filter_depth -- --nocapture
parens depth=1500 len=3004
-> survived
parens depth=2000 len=4004
thread 'audit_scim_filter_nested_parens' has overflowed its stack
fatal runtime error: stack overflow, aborting
(signal: 6, SIGABRT: process abort signal)
End-to-end against an in-process server via kanidmd_testkit (no authentication performed):
Testkit server setup complete - http://localhost:18080/
audit_scim_dos: sending unauthenticated GET, url len = 12056
thread '...' has overflowed its stack
fatal runtime error: stack overflow, aborting
(signal: 6, SIGABRT: process abort signal)
Impact
Process-wide availability loss; no confidentiality or integrity impact.
- Unauthenticated, default install, no feature flag required.
- Process abort, not task panic. Stack overflow triggers libstd's guard-page handler, which calls
std::process::abort(). tokio's per-taskcatch_unwindisolation does not apply to aborts. All in-flight HTTP requests, OAuth2/OIDC sessions, LDAP binds, and the web UI are terminated. - Repeatable. One ~12 KB GET per crash; a
while true; do curl ...; doneloop holds the service down indefinitely across supervisor restarts. - The 6 011-byte variant (
depth=3000) fits under the nginx defaultlarge_client_header_bufferslimit of 8 KB, so a typical reverse proxy does not mitigate.
Affected: v1.7.0 through master @ edf50b9da.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Unauthenticated GET request with deeply nested parentheses in SCIM filter parameter triggers stack overflow in PEG parser, causing entire kanidmd process to abort.
Vulnerability
CVE-2026-46689 is an unauthenticated denial-of-service vulnerability in Kanidm's SCIM filter parsing. The recursive-descent PEG parser in proto/src/scim_v1/mod.rs allows unlimited nesting of parentheses in the ?filter= query parameter, with rules like "(" e:parse() ")" that re-enter parse() without a depth counter [1][2]. A crafted filter of a few thousand nested parentheses (≈4–12 KB) causes stack exhaustion past the worker thread's stack guard page.
Exploitation
The vulnerability is reachable via any unauthenticated GET request to nine SCIM endpoints, including /scim/v1/Entry, /scim/v1/Person/{id}, and others [1][2]. The filter parameter is parsed by axum's Query extractor before handler execution, and authorization is deferred, so no authentication is required [1][2]. The existing semantic depth limit (DEFAULT_LIMIT_FILTER_DEPTH_MAX = 12) is only enforced after the parser has already run, thus not preventing the stack overflow [1][2].
Impact
Upon stack overflow, Rust's runtime calls std::process::abort(), immediately terminating the entire kanidmd process [1][2]. This results in a complete denial of service, requiring manual restart or automated recovery mechanism.
Mitigation
As of the advisory publication, no patch is available; administrators are advised to restrict access to SCIM endpoints or apply network-level filtering of oversized query parameters.
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
0No patches discovered yet.
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
2News mentions
0No linked articles in our index yet.