CVE-2026-33419: LDAP STS Enumeration and the Throttling Chain

From uniform authentication failures to corrected refunds, proxy attribution, and account lockout: LDAP STS hardening through two rounds of counter-fixes.

Status: Released, followed by two rounds of corrections
First containing release: RELEASE.2026-04-17T00-00-00Z
Complete correction: RELEASE.2026-06-18T00-00-00Z
GitHub issue: pgsty/minio#23

The core vulnerability was straightforward: LDAP STS returned different results for “user does not exist” and “password is wrong,” creating a username oracle. The first fix unified the external authentication failure and added limits by source IP and username. Continued review then showed that success refunds, spoofable source headers, reservation accounting, and the shared username bucket could turn the security control itself into a new attack surface.

The final June design removed the username bucket that enabled precise account lockout, retained only the source-IP bucket, and made proxy attribution an explicit deployment contract.

Initial threat model

The entry point is AssumeRoleWithLDAPIdentity. An attacker needs no existing MinIO account. Access to the LDAP STS endpoint is enough to compare the code, status, or message returned for an unknown user and a wrong password, enumerate valid usernames, and combine that knowledge with password spraying, guesses about organizational naming, or social engineering.

The fix could not simply disguise every error as “wrong password.” LDAP connection, lookup-bind, and directory-service failures still needed to appear as infrastructure errors, or operators would lose the ability to diagnose the service.

First round: uniform responses and a limiter

The initial fix on 2026-04-15 did three things:

  • unknown user and bad password returned the same external STS authentication error;
  • LDAP infrastructure failures still returned 500, with the real cause retained in the server log;
  • a new in-memory limiter initially created buckets for both source IP and normalized username.

This closed the content side channel and raised the cost of brute-force attempts, but the limiter state machine and source attribution exposed more problems.

Second round: success, attribution, and accounting

The follow-up changes on April 16 addressed three classes of defects:

  1. Successful authentication must not consume the failure allowance; the reserve/commit/cancel/refund lifecycle had to be explicit.
  2. The socket peer must be used by default; X-Forwarded-For, X-Real-IP, and Forwarded cannot be trusted merely because a request supplies them.
  3. Refund and capacity need hard bounds so cancel logic cannot mint tokens.

A proxy must be placed on an explicit trusted allowlist before it can influence source attribution.

Third round: remove the username bucket

Adversarial review in June overturned the intuition that “source plus username must be stronger than source alone.” A shared username bucket could be exhausted continuously from arbitrary origins. With only a low request rate, an attacker could lock a targeted account before the legitimate user ever reached an LDAP bind.

The final fix therefore:

  • removed the per-username bucket;
  • peeled trusted hops from XFF right to left and selected the first untrusted address;
  • rejected trusted-proxy footguns such as 0.0.0.0/0 and ::/0;
  • stopped using Forwarded for security-sensitive bucketing;
  • allowed X-Real-IP only under a contract in which the proxy overwrites rather than forwards client input.

This turn in the review shows that a security control needs its own threat model. More dimensions of throttling do not automatically mean more security.

Rejected alternatives

OptionWhy it was rejected
Perform a dummy bind for unknown usersAmplifies LDAP load and creates a second, error-prone authentication path after the content side channel is already closed
Bucket all IPv6 clients by /64Legitimate users behind the same site or carrier prefix can throttle one another
Take the leftmost XFF valueClient-controlled and therefore spoofable
Fall back to the peer when XFF and X-Real-IP disagreeAn attacker can create disagreement deliberately and collapse every user behind a proxy into one bucket
Fully support RFC 7239 ForwardedSecurity-sensitive parsing complexity outweighs the practical benefit

Verification and release

The historical record covers limiter reserve/commit/cancel/refund behavior, concurrency, success and infrastructure failures, external equivalence of unknown-user and bad-password responses, and RemoteAddr, spoofed-header, trusted-proxy, multi-hop, and catch-all-CIDR cases. Focused package tests and builds were recorded as passing.

The LDAP security end-to-end test skips when _MINIO_LDAP_TEST_SERVER is absent, so an outer ok cannot be presented as proof of the full LDAP scenario.

The first public fix was 6619d0c. Follow-up corrections include c55b52c, 817a457, 084a154, and 5e40665.

Final cost and residual risk

  • The limiter now keys only on source IP and gives up a hard per-account throttle across different origins.
  • It is per-node and in-memory, not a cluster-wide password defense.
  • Botnets, distributed origins, IPv6 address rotation, and LDAP bind timing remain.
  • Incorrect trusted-proxy configuration can still destroy source attribution.
  • A Forwarded-only deployment falls back to the peer bucket and loses granularity.

Rate limiting can reduce attempts from one source. The uniform external authentication response is what actually conceals whether a username exists.

Last modified August 2, 2026: init commit (8338d5b)