Severity Daily

IT and AI security incidents, checked against the primary source

Tag: Grav

  • Grav CVE-2026-86193 names one affected plugin; the vendor advisory names three and fixes one

    Grav CVE-2026-86193 names one affected plugin; the vendor advisory names three and fixes one

    Five Grav CVEs landed at NVD on Saturday; the highest-scored one is recorded against a single plugin, while the vendor advisory it came from names three and gives a fixed version for one.

    What happened

    Five CVE records for the Grav flat-file CMS and its plugins were published to the National Vulnerability Database on Saturday, September 5, 2026, all timestamped 1:18 p.m. UTC and all assigned by [email protected]. All five trace to GitHub Security Advisories filed in the getgrav/grav repository on August 21, 2026 — fifteen days earlier — and all five credit the same reporter, @1K0CT.

    Three of the five are scored 8.7 and all three are in the Grav API plugin, fixed in 1.0.20:

    • CVE-2026-86193 (CWE-863, CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H). NVD’s description: “Fails to validate group-inherited super permissions in user-management guards, allowing non-super user managers to modify super-admin accounts.” The advisory’s title is blunter: “Target-super guards ignore group-inherited super.”
    • CVE-2026-86195 (CWE-269, same vector). NVD: “Privilege escalation in InvitationsController where stripSuperFlags() only removes nested super flags but fails to strip dot-keyed equivalents like api.super.”
    • CVE-2026-86196 (CWE-290, PR:N/UI:P). NVD: “Builds password reset links from untrusted Host header in forgot-password endpoint, allowing unauthenticated attackers to redirect reset tokens to attacker-controlled domains.”

    The other two: CVE-2026-86194 (6.9, CWE-862) in the Grav Form Plugin before 9.1.22 — “allowing anonymous visitors to execute form actions defined on login-restricted or unpublished pages” — and CVE-2026-86197 (5.1, CWE-79) in Grav before 2.0.20, a Twig sandbox policy that “allowlists addJs and addCss methods on Grav\Common\Assets without proper output escaping.”

    Here is the gap. Both NVD and VulnCheck’s own advisory page record CVE-2026-86193 against one package: grav-plugin-api, versions before 1.0.20. VulnCheck’s affected-product string is exactly “grav-plugin-api >= 0, < 1.0.20,” and it lists no other package. The GitHub Security Advisory the record derives from lists three: grav-plugin-api at 1.0.19 and below, grav-plugin-login at 3.9.2 and below, and grav-plugin-admin at 1.10.55 and below, plus 1.11.0-beta.5. A patched version appears for the API plugin only. The advisory also states that “the getgrav/grav core package is not affected” — the vulnerable code, it says, lives entirely in plugins.

    The mechanism, per the advisory: every guard in Grav’s user-management surface that asks whether the target account is a super-admin answers by reading that account’s own access map. Grav also grants super through group membership. An account that holds super by inheritance therefore reads as an ordinary user to the guard. A delegated user manager can then reset its password, delete it, mint API keys in its name, or disable two-factor enrollment — and from a super-admin account, install a plugin, which is code execution.

    CVE-2026-86195 is a different route to the same destination. stripSuperFlags() is meant to prevent a user manager from writing a super flag into an invitation payload. It removes the nested form, ['api' => ['super' => true]]. It does not remove the dot-keyed form, ['api.super' => true], which Grav’s permission resolver treats as equivalent. A user manager holding only api.access and api.users.write creates an invitation carrying {"api.super": true}, accepts it through the public endpoint, and holds a super-admin with immediate JWT access. No real invitee is involved. The advisory notes the result is invisible in an invitation list, because those lists do not display access blocks.

    CVE-2026-86196 needs no account at all. POST /api/v1/auth/forgot-password builds the reset URL from the request’s Host header, so an attacker submits a reset for a known address with Host: attacker.com. The target receives a genuine email from the real site whose link points elsewhere, and clicking it hands the token to the attacker’s logs.

    None of the five records carries a cisaExploitAdd or cisaActionDue field. No KEV listing, no federal deadline, and no exploitation reported by anyone.

    Why it matters

    The record is not wrong. It is narrower. That distinction is the whole problem, because nothing in a CVE record announces that its affected list is a subset of the advisory’s. A team that remediates from the feed — most teams, because that is what scanners and ticket generators consume — reads “grav-plugin-api < 1.0.20,” upgrades that plugin, and closes the ticket while running two others the vendor names as affected by the same CVE. There was no contradiction to catch and no flag to trip. The only way to find the other two is to open the advisory and read it, which is precisely the step automating vulnerability management is supposed to remove.

    The second half of the problem is worse for the reader than for the record. Two of the three affected plugins have no patched version anywhere in the advisory. Someone on grav-plugin-login 3.9.2 now knows they are affected and has no number to upgrade to. That is not a gap a CVE record could have fixed even if it had carried the full list — the information does not exist upstream to carry.

    The two privilege-escalation flaws are worth reading together, because they are the same mistake in two costumes. In both, a security check re-implements, incompletely, a resolution that already exists elsewhere in the same codebase. The permission resolver knows that group membership confers super; the guard asks the account directly and does not. The permission resolver knows that api.super and nested api: {super: true} are the same statement; the sanitizer strips one spelling. Neither bug is a missing check. Both are checks that were written, reviewed, and look right in isolation — which is why this class survives code review. Reading the guard tells you nothing; you have to read the guard against the resolver and ask whether they agree on what a permission is.

    The dot-keyed variant deserves its own note, because it recurs wherever a configuration format has more than one spelling for the same value. Dotted keys and nested maps are interchangeable across much of PHP, YAML, and JSON configuration handling, by design, so humans can write the short form. Any sanitizer written against one shape is a filter with a documented bypass sitting in the format’s own manual. The durable fix is not adding the second pattern to the sanitizer — it is normalizing before filtering, or filtering through the same resolver that will later interpret the value.

    On the fifteen-day lag: this is not misconduct, and it is not unusual. GitHub advisory publication and CVE publication run on separate pipelines with separate queues, and a third-party CNA picking up a GHSA weeks later is ordinary. But the practical effect is that from August 21 to September 5, the fix and the complete affected list were public in one place while the identifier that scanners key on did not exist. Anyone whose vulnerability program is CVE-shaped had a fifteen-day blind spot on a flaw whose end state is plugin installation.

    One smaller conflict, shown rather than resolved: VulnCheck’s advisory page for CVE-2026-86193 gives a disclosure date of September 5, 2026. The GitHub advisory it summarizes was published August 21, 2026. Both dates are real — one is CVE publication, the other advisory publication — but a reader taking the VulnCheck page at face value will date the disclosure two weeks late.

    What to do

    Upgrade grav-plugin-api to 1.0.20. That fixes CVE-2026-86193, CVE-2026-86195, and CVE-2026-86196.

    Then do the part the CVE record does not tell you to do: check grav-plugin-login and grav-plugin-admin. The advisory names login at 3.9.2 and below and admin at 1.10.55 and below, plus 1.11.0-beta.5, as affected by CVE-2026-86193, without naming a fixed release for either. Move both to current releases and, for certainty, ask the project which version carries the fix.

    Separately, take Grav Form Plugin to 9.1.22 and Grav core to 2.0.20.

    For detection, the useful checks are specific. For CVE-2026-86195, do not audit the invitation list — the advisory says access blocks do not appear there. Enumerate current accounts holding super and reconcile against who should have it. For CVE-2026-86193, enumerate accounts that hold super through group membership rather than a direct flag; those are exactly the accounts the guards failed to protect, so they are where password resets, new API keys, or dropped two-factor enrollment would show. For CVE-2026-86196, check outbound mail logs for reset links whose host is not yours, and pin the application’s canonical host rather than trusting a proxied Host header.

    There is no clock on this one. No KEV entry and no cisaActionDue on any of the five records.

    Sourcing note

    Checked: NVD records for CVE-2026-86193, CVE-2026-86194, CVE-2026-86195, CVE-2026-86196, and CVE-2026-86197, all published September 5, 2026 and sourced to [email protected]; the GitHub Security Advisories GHSA-vv8m-jqpm-38×4, GHSA-m363-3hww-gcwc, and GHSA-262p-56vv-7v5r in the getgrav/grav repository, all published August 21, 2026; and VulnCheck’s own advisory pages for the same CVEs. The affected-package discrepancy was checked twice against the GitHub advisory to rule out a retrieval artifact.

    Descriptions in quotation marks are NVD’s record text or GitHub advisory titles, verbatim. Explanations of mechanism and impact are paraphrased from the advisory bodies rather than quoted. No code was independently reviewed and no proof-of-concept tested; the technical account rests on the vendor’s own advisories.

    Unresolved: the advisory indicates the login and admin plugins are patched but does not name the versions, so this story cannot tell a reader running grav-plugin-login 3.9.2 what to upgrade to. Also unresolved is whether the CVE record’s narrower affected list will be corrected; as of publication, NVD and VulnCheck both list only grav-plugin-api. All five advisories are filed under the getgrav/grav repository even though the advisory states core is not affected, which is reasonable placement for discoverability but makes the core repository’s advisory list read as a list of core defects.