CIRCL assigned CVE-2026-90961 today for a 9.3 unauthenticated authentication bypass in MISP’s LDAP and LinOTP login plugins — four days after the fix shipped inside a release note that called it “rejection of empty and non-string LDAP and LinOTP credentials.”
What happened
CVE-2026-90961 was reserved and published by CIRCL, the Computer Incident Response Center Luxembourg, today — September 14, 2026 — at 1:22 p.m. UTC, and appeared in the National Vulnerability Database at 2:17 p.m. UTC. CIRCL maintains MISP and is the CNA for it, so this is the vendor assigning a CVE to its own product. The score is CVSS v4.0 9.3, critical, with the vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N. No CVSS v3 score was published. The weaknesses are CWE-287, improper authentication, and CWE-20, improper input validation.
MISP is the open-source threat intelligence sharing platform used by national CSIRTs, sector ISACs, and corporate security teams to exchange indicators, malware samples, and victim information. The affected components are two optional authentication plugins, LdapAuth and LinOTPAuth, which let an instance authenticate users against a directory rather than against MISP’s own user table.
The record describes the flaw at unusual length, and the mechanism is worth reading in the vendor’s own words: “Both LdapAuthenticate and LinOTPAuthenticate replace CakePHP’s FormAuthenticate class but fail to replicate its _checkFields() input validation guard. As a result, the email and password fields extracted from the login request are passed to downstream authentication logic without verifying that they are non-empty strings. In the LDAP authenticator, an empty or null password is forwarded to ldap_bind(). Per RFC 4513 section 5.1.2, a bind request with a valid DN and an empty password constitutes an unauthenticated bind, which many LDAP directory servers accept as successful. An attacker who knows any valid user email address in the directory can therefore authenticate as that user without possessing a password.”
That is the whole attack: submit a login form with a real address and an empty password field. If the directory treats a DN plus an empty password as an unauthenticated bind and returns success, MISP accepts that as authentication and issues a session.
There are two further paths in the same record. Non-string values — null, false, arrays — are “either coerced to empty strings by ldap_bind(), raise TypeErrors, or are misinterpreted as find conditions in _findUser().” And in the LinOTP authenticator, the mixed-authentication branch accepts “an empty password … against a stored hash of the empty string.” That stored hash is not hypothetical: the record states that accounts auto-provisioned on first LDAP login “were assigned an empty password,” and because the save path skips validation, the empty string was hashed and written to the database. A user who later stops appearing in the directory falls back to local authentication against a hash of nothing.
Exploitation requires that one of the two plugins is enabled and that the attacker knows a valid email address registered in the directory or in MISP. No prior authentication is required. The record states that success “grants the attacker the full privileges of the impersonated user, which may include administrative access to threat intelligence data.”
The fix is commit 0ee058548, titled “fix: [security] Reject empty and non-string credentials in the LDAP and LinOTP authenticators.” It touches two files — app/Plugin/LdapAuth/Controller/Component/Auth/LdapAuthenticate.php and the LinOTP equivalent — and adds the guard the plugins never inherited: if (!is_string($email) || $email === '' || !is_string($password) || $password === ''), plus a specific check for an empty password in the LinOTP mixed-authentication branch.
Reading MISP’s repository at the release tags confirms where that landed. The guard is absent from LdapAuthenticate.php at v2.5.40 through v2.5.45 and present at v2.5.46. There is no v2.5.47; VERSION.json on the master branch reads major 2, minor 5, hotfix 46. So 2.5.46 is both the fixed version and the newest release, and the CVE’s affected range — everything below 2.5.46 — matches the code.
The credits name elhoim (David André) as the reporter, and list iglocska together with “Claude Opus 5 (1M context)” as the developers of the remediation. The commit carries a matching co-author line.
Why it matters
The gap between the fix and the notice is the part worth sitting with. MISP 2.5.46 was released on September 10, 2026 at 8:04 a.m. UTC. Its release notes list the security work of that cycle: SSRF fixes in event report URL imports, feed redirects and TAXII discovery, CSRF improvements, XSS prevention, SVG sandboxing for logos and report pictures, a new shared UrlEgressValidator. Somewhere in that list sits “rejection of empty and non-string LDAP and LinOTP credentials.” No CVE identifier appears in the notes, and nothing in the phrasing distinguishes a 9.3 unauthenticated bypass from the routine input-hardening around it.
An administrator who read those notes on September 10 and decided to schedule the upgrade for the next maintenance window was making that decision without the one fact that would have changed it. The fact existed — the reporter knew, the maintainer knew, the commit message says “[security]” — but it did not reach the person who had to act on it until today, four days later, through a different channel.
This is the same shape as the froxlor record this publication covered earlier today, inverted. There, the flaw’s description was accurate and the affected range was wrong by twelve releases. Here the affected range is exactly right and it arrived after the patch it describes. In both cases the vendor did the engineering work correctly and the signaling lagged it. Severity lives in the record; the record is not what most people read.
The technical lesson is older and narrower, and it keeps recurring. When application code replaces a framework’s authentication class, it inherits the framework’s interface but none of the framework’s defensive assumptions. CakePHP’s FormAuthenticate checks that the submitted fields are non-empty strings before it does anything else, precisely because everything downstream assumes they are. Two plugins reimplemented authenticate() against the same interface and dropped that check, and the flaw sat in the seam between the framework and the extension — a place neither the framework’s tests nor the plugin’s tests naturally cover.
The LDAP half of it is older still. Unauthenticated bind, where a client supplies a valid DN and an empty password, has been a documented trap since RFC 4513 was published in 2006. An application that forwards a user-supplied password straight to ldap_bind() without checking that it is non-empty inherits whatever its customer’s directory happens to do, which makes exploitability here partly a property of the defender’s environment rather than of MISP alone.
And the target matters. A MISP instance is a concentration of exactly the information an intruder would most like to have about the organization hunting them: which indicators are known, which incidents are open, which partners are sharing what. Read access alone is valuable. Write access to a sharing community lets an attacker poison what downstream members act on.
What to do
Upgrade to MISP 2.5.46, released September 10, 2026. It is the newest release and it carries the fix.
Before anything else, establish whether you are exposed at all: the flaw requires that LdapAuth or LinOTPAuth is enabled. An instance using MISP’s own user table is not affected by this CVE.
If you are on a version you cannot immediately move off, the presence of the guard is greppable against the installed tree:
grep -c 'is_string($email)' app/Plugin/LdapAuth/Controller/Component/Auth/LdapAuthenticate.php
A count of zero means the guard is absent.
On the directory side, confirm how your LDAP server handles a simple bind with a valid DN and an empty password. Products differ, and defaults have changed across versions; treat this as something to test rather than look up. If your directory rejects unauthenticated binds, the primary path does not work against you even on an unpatched MISP — but the non-string and LinOTP paths in the same record do not depend on that behavior.
Review authentication logs for successful logins that do not correspond to a real credential presentation, and pay particular attention to accounts that were auto-provisioned on first LDAP login. There is no reported exploitation of this flaw, by CIRCL or by anyone else, and it is not in CISA’s KEV catalog.
Sourcing note
Checked: the NVD record for CVE-2026-90961; the CVE Services record behind it, which is where the assigner (CIRCL), the reservation and publication timestamps, the affected range and the credits come from; the MISP fix commit 0ee058548; the MISP source at tags v2.5.40 through v2.5.46 and at master, read directly from raw.githubusercontent.com; and MISP’s release page for v2.5.46 for the release date and release notes. All quotations of the flaw’s mechanism are from the CVE record’s own description.
Not reached: GitHub’s API is gated from this environment, so the commit’s own date could not be read; the release date for 2.5.46 comes from the release page rather than from commit metadata. CISA’s site returns 403 to automated fetching, so the KEV statement here rests on the catalog mirror at raw.githubusercontent.com, read this morning at catalog version 2026.09.11, in which this CVE does not appear.
Unresolved: the fix adds guards in the two authenticators, but the record also describes empty-string password hashes already written to the database by earlier auto-provisioning. Whether those stored hashes are cleaned up on upgrade, or simply become unreachable because the guard now rejects the empty password before it is checked, is not stated in any source read here. Operators with auto-provisioned LDAP accounts may want to ask CIRCL directly.