Skip to content

Authentication-method precedence, HTTP-verb allowlist bypass, and ACL tie-break drift

A September 21 15:31–15:47Z GitHub advisory wave produced two clusters with durable operator axes — an Apache Airflow credential-precedence trio and a MISP authorization-drift octet — plus a lookup-ordering authz bug (Dogtag pki-core), an OAuth-state sentinel bug (Hatchet), and one fix-branch integrity lesson (Apache MINA). The reusable invariant across all of them: the security control is a comparison or a lookup, and both comparisons and lookups have ordering, coverage, sentinel-value, and tie-breaking edges that ship untested.

0. Two one-liners worth their own sweep

  • Dogtag pki-core ACL tie-break (CVE-2026-80110 / GHSA-c24q-2g88-pp57, 8.1): the v2 REST ACL filter resolves a colliding literal-vs-wildcard ACL key by lexicographic string comparison instead of specificity, so a wildcard-mapped permission overrides a more specific literal one — POST /v2/profiles/raw (meant to need Administrator profiles.create) is authorized under the default Certificate Manager Agents group's profiles.approve. Sweep rule: on any rule-matching engine (ACLs, WAF rules, route matching, CAPAs), construct a deliberately colliding specific-general pair and see which wins the tie. Specificity should never be decided by alphabetical order.
  • Hatchet OAuth state sentinel collision (CVE-2026-61687 / GHSA-phg3-3g28-wq9v, 7.1): the OAuth callback clears session oauth_state_<integration> to "" instead of deleting the key, and the later equality check accepts an empty ?state= — an unauthenticated attacker binds an already-authenticated victim session to an attacker-chosen OAuth identity (login-CSRF → account fixation). Sweep rule: treat empty string, null, and sentinel defaults as equality-check operands — after completing one legit OAuth flow, replay the callback with ?state= empty on any OAuth-enabled deployment.
Advisory Behavior
CVE-2026-82355 / GHSA-9x6g-jfj6-r3jg A request carrying both a session cookie and an explicit Authorization: Bearer token is resolved — and audit-logged — as the cookie's principal. Bearer-over-cookie precedence is inverted by a cookie-derived-user cache path.
CVE-2026-86473 / GHSA-9cmm-29p3-73gr The logout endpoint revokes only the token presented as the _token cookie. A bearer-header client gets a normal 200 logout response while its token stays valid for the full lifetime (default 24 h).
CVE-2026-75158 / GHSA-pfr9-65p3-w9r9 /assets/events returns asset events for every Dag, no per-Dag authorization filter; the filter was also missing from the count query, so total_entries + pagination disclose hidden Dag existence even without reading rows.

Operator axes:

  1. Credential-precedence table. On any API with ≥2 auth channels (cookie, bearer, API key, mTLS), send a request carrying two different principals simultaneously and ask: which identity executes the action, and which identity lands in the audit log? A mismatch is principal confusion + misattributed audit records. Airflow's precondition chain is reusable: plant your own valid cookie in the victim's client via sibling-subdomain cookie tossing, shared-parent-domain XSS, or a shared workstation, then watch their bearer-authenticated calls execute as you.
  2. Logout is a state transition — verify it happened. After logout, replay the captured bearer token against one harmless endpoint. A 200 after "logout" means the control only handled one presentation form of the credential. Sweep every credential-presenting transport (cookie, Authorization, query param, custom header) against logout/revoke/revoke-all-sessions endpoints; each is a separate revocation leg.
  3. Count-query oracle. When list endpoints hide rows but the aggregate query skips the same filter, total_entries becomes an existence oracle for tenant-blind objects. Compare row count vs reported total with a two-team lab deployment — the delta is the finding even when you can't read a single row.
  4. Airflow deployments already have pages on this wiki (June 30 boundary page, Sept 17/18 verb-permission folds): the recurring Airflow class is per-Dag/per-team scoping applied to the row path but not the count, cache, or lifecycle path.

2. MISP <2.5.47: octet of authorization drift (8 GHSAs)

Advisory Boundary
CVE-2026-94379 / GHSA-3q8c-466v-vgx3 login() enforced bruteforce blocking, email-OTP 2FA, and failure logging only for POST/PUT. Any other HTTP method skips all three simultaneously: unlimited credential guessing, 2FA bypass, no audit trail.
CVE-2026-94381 / GHSA-mw89-3hh7-vfpg A read-only API key session: one request to a specific function restores the underlying account's full permissions (write/delete/admin). API-key restriction ≠ session permission.
CVE-2026-94374 / GHSA-qg5m-5m7w-mf5x Module-results processing loops over client-supplied EventReport entries and calls save() without unsetting id (the adjacent attribute/object loops do unset it) — an existing row is updated, letting you reparent another event's report into yours: read, overwrite, re-own.
CVE-2026-94393 / GHSA-rq8q-gvp5-cmqp UUID-keyed report identification inside event edit has no same-event ownership check → cross-event report reparenting via known/guessed UUID.
CVE-2026-94394 / GHSA-cj24-5f9w-vhx6 Adding a reference checks access to the event but not to the individual attribute/object → sharing-group-restricted data readable through container-level permission.
CVE-2026-94401 / GHSA-2jvm-986q-589g XML import accepts non-XML content containing a local path or URL → local file read and SSRF to internal services (user content is parsed as XML without validating it is XML).
CVE-2026-94404 / GHSA-g7f8-8jj3-3vcp Attribute edit path missing CSRF enforcement → drive-by modification of attribute value/type/category/distribution through a logged-in analyst's browser.
CVE-2026-94277 / GHSA-8mcv-mp35-55rc Galaxy matrix statistics view renders galaxy name via sprintf() with no encoding → perm_galaxy_editor gets stored XSS against every user opening the stats page.

Reusable sweep order for any CakePHP/ORM-backed platform:

  1. Verb matrix on security-critical endpoints. For login, MFA enrollment, password change, and logout: replay the identical request under GET, PUT, PATCH, DELETE, OPTIONS, HEAD, TRACE, and casing/whitespace variants. The MISP pattern is an allowlist check (in_array($method, ['POST','PUT'])) gating the security controls — every method outside the list silently skips them. The gate-control itself is the target: verify the control runs (rate limit trips, OTP challenge appears) rather than only verifying the action result.
  2. Key-scope gap on identifier writes. Sweep every create/update flow that accepts id or UUID fields: does the code unset/reject caller-supplied primary keys before save()/update()? The MISP tell is the adjacent loop that gets it right — differential-read the sibling code paths around the vulnerable sink; the correct loop is the attack map for which entry point forgot.
  3. Delegation-axis drift. Enumerate the axes along which the platform restricts access — API key vs session, event vs attribute, read-only vs read-write — and test requests that straddle two axes: read-only key + permission-restoring call; event access + restricted-attribute reference.
  4. Media-adjacent importers. Validate-input-type-before-parse is routinely skipped on XML import endpoints: upload a file whose name says .xml but whose content is a path or URL payload.
  5. Low-role editors as XSS carriers. Statistics/admin aggregate pages that render names of user-editable catalog objects (galaxies here) are stored-XSS collection points even though the editor role feels data-scoped.

MISP context note: this platform is threat-intelligence infrastructure — on authorized engagements, prove with synthetic events/reports/galaxies in a disposable instance; never touch production intel data, and treat any real session/credential material as out of scope.

3. Apache MINA: "fixed" version never got the fix (CVE-2026-94301 / GHSA-h355-27x9-rr7r, 9.8)

The resolveProxyClass() override closing the CVE-2026-47065 java.lang.reflect.Proxy acceptMatchers deserialization bypass was committed to the 2.2.X branch only. The 2.0.29 / 2.1.13 artifacts listed as fixed — and everything after them (2.0.30, 2.1.14) — remain vulnerable, with the advisory publicly saying so.

Operator rules:

  • Version-fingerprint from the artifact, not the advisory table. Confirm the fix's actual code (resolveProxyClass override in the class the fix touched — readable from the shipped jar) before trusting a "fixed in" row. Maintenance-branch lines are where fix-commit skew lives.
  • The public advisory is the exploit map. A 9.8 advisory that describes the exact bypass the fix missed, with affected artifact coordinates published, is a ready-made target list — enumerate apps embedding mina-core 2.0.x/2.1.x deserialization endpoints.

September 22 follow-up: Sync-in Server — three auth-endpoint drifts in one product

A single product (Sync-in Server v2.3.0) shipped all three recurring auth-audit shapes in one wave; use it as a checklist template for any NestJS-style login stack:

  • Complete 2FA bypass via the token endpoint (CVE-2026-58269, GHSA-92cr-jxw4-5wjg, 8.1): POST /api/auth/token authenticates with username+password (AuthLocalGuard) then calls getTokens() directly — which never reads user.twoFaEnabled — while the parallel POST /api/auth/login path routes through setCookies(user, res, true) where the 2FA gate actually lives. Valid credentials + zero TOTP = unrestricted access/refresh JWTs. This is the alternate-transport authz-parity rule applied to MFA: enumerate every endpoint that mints a session (login, token, device-flow, sync-register, CLI, API-key issue) and diff which ones consult the second-factor flag. The cookie path enforcing 2FA proves nothing about the bearer path.
  • TOTP brute-force via lockout-counter no-op (CVE-2026-58271, GHSA-274f-6w77-8qm9, 6.8): on the desktop-sync registration route, a failed TOTP attempt called the failure recorder on a code path where the branch wrote passwordAttempts back unchanged — counter never reached the threshold, lockout never fired. A guessed TOTP registers a sync client and returns a {clientId, clientToken} pair exchangeable for a session; with the known password the attacker can then call the 2FA-disable endpoint. Audit rule: the rate-limit/lockout control must be tested on every consumer of the credential, not just the login form — and the correct test is whether the counter state advances after N failures, not whether the endpoint eventually blocks (a fire-and-forget write-back can silently no-op).
  • Username enumeration timing oracle via short-circuit before the dummy hash (CVE-2026-58272, GHSA-29hq-23m2-2j47, 5.3, incomplete fix of a prior timing advisory): the codebase already contains the standard defense — comparePassword() burns a bcrypt compare against a dummy hash when no hash exists — but validateUser() returns early when the user lookup comes back empty, so the nonexistent-user path never reaches the constant-time function. ~100ms bcrypt-vs-nothing differential. Reusable lesson: timing equalization lives or dies on the call graph, not the helper; when auditing any "fixed" enumeration advisory, trace every return path from the router to the sink and check each one actually passes through the equalized function. This is the fix-placement sibling of the MINA "fixed version never got the fix" pattern above.

Validation boundaries: disposable Sync-in instance, synthetic users with TOTP enrolled, and your own credentials only; for the timing check use lab-network latency controls and report median/response-time distributions, not a single sample. Never test lockout exhaustion against a shared instance (it will lock real accounts).

September 22 follow-up: MISP second wave — dead-operator-precedence guards, unlockedActions CSRF, path-concat oracles, and parent-vs-child distribution drift

A second MISP wave (published 2026-09-22, ~14 advisories, < 2.5.47 train) adds five new axes to the octet above:

  • Unauthenticated SSRF via an unlockedActions endpoint + PHP operator-precedence guard bug (CVE-2026-95679 / GHSA-95gm-v6rp-cf52). RequestHandlerComponent auto-decodes XML bodies on all write requests; Xml::build()'s readFile guard is written readFile && http || https — && binds tighter than ||, so the https branch bypasses the readFile check entirely, and a bare HTTPS URL in the body is fetched server-side. The cspReport endpoint accepts XML content-type and is unauthenticated by design → unauth outbound-HTTPS oracle (internal probing, timing recon, side effects on internal endpoints). Two sweep rules: (1) the &&/|| dead-guard pattern already canonized on the Sept 22 Dancer2 page and the WP wave — grep mixed-precedence boolean guards in validation code, they die silently; (2) "unauthenticated by design" endpoints (CSP-report, webhooks, health) still execute the platform's full body-decoding pipeline — an endpoint that needs no auth still inherits every parser feature (XML entity/locator fetching) the authenticated endpoints have. Probe CSP-report/webhook endpoints with each content-type the framework auto-parses.
  • CSRF-stripped action executors via framework allowlists (CVE-2026-95658 / GHSA-5fvj-qq2x-32vr): moduleStatelessExecution sits in CakePHP's unlockedActions, which disables CSRF and field-hash validation — on an action that runs a workflow module's exec() with caller input. An attacker's cross-origin form POST executed by a logged-in admin invokes module execution. Audit rule: dump every unlockedActions/CSRF-exempt list in CakePHP/Spring/other frameworks and ask whether any exempted action is state-changing or executor-shaped (webhooks legitimately belong there; module runners do not).
  • User-controlled strings concatenated into file_exists()/path checks = existence + type oracles (CVE-2026-95698 / GHSA-24qr-3pqh-xghm: org name from event import → findOrgImage() traversal, ../../etc/passwd resolves outside the image dir, distinct error messages yield file-existence oracle; CVE-2026-95703 / GHSA-f4j8-7jmm-g7jj: admin __uploadLogo runs file_exists/MIME/EXIF probes on a caller-supplied tmp_name before is_uploaded_file(); CVE-2026-95701 / GHSA-c7hm-qgxh-6vgg: dead-but-instructive sibling). Sweep rule: any file_exists/is_readable/mime-probe whose path components include a DB field the caller can write (org names, display names, slugs) is a filesystem oracle even when no content is returned; differential error messages are the channel. The tmp_name shape is its own classic: PHP upload handlers that probe before is_uploaded_file() accept arbitrary server paths — test upload endpoints with non-upload tmp_name values pointing at lab marker files.
  • Parent-readable ≠ child-readable: enrichment queries skip the child's own ACL (CVE-2026-95683 / GHSA-83cf-9w8x-h48q). The Overmind event view auto-fetches the most recent attached report using only the event ID, ignoring the report's own (more restrictive) distribution setting — and doesn't filter soft-deleted rows. This is the nested-ID axis with the check inverted: instead of "child trusted without parent check," it's "parent checked, child's tighter ACL never applied." Sweep every auto-enrichment/preview/aggregate fetch: does the query join filter on the child's own visibility columns, and does it exclude soft-deleted rows? Soft-deleted-object disclosure is its own recurring class — always request deleted IDs.
  • Verb-gated authorization check with a verb-permissive persistence layer (CVE-2026-95671 / GHSA-ph85-5rm4-24r5): CollectionsController::add() ran the sharing-group authorization check only on POST, while CRUDComponent::add() persists on POST and PUT → PUT /collections/add creates a collection shared with an unauthorized sharing group. First verb-allowlist bypass inside this same product family's second wave — the September 21 rule ("verify the control runs, per HTTP method") now has a same-product second data point; add PUT to the verb-matrix test on every add()-shaped CakePHP endpoint.

Smaller items from the wave folded without separate rules: wildcard-ACL overmapping (replaceSuggestionInReport mapped to * permission while siblings require perm_add — the ACL-tie-break family above; diff the permission map of an action against its siblings' [CVE-2026-95685 / GHSA-385h-fw9f-cf6x]); $force-style "system paths" that skip ownership checks when triggered by ordinary user flows (org-metadata overwrite by SG editors via captureOrg [CVE-2026-95697 / GHSA-prf9-8cgh-75pc]); JS-string-context encoding class — json_encode() without JSON_HEX_QUOT|JSON_HEX_TAG inside a single-quoted JS string literal breaks out on ' (CVE-2026-95665 / GHSA-24x5-fh5v-x5g9), and raw echo inside a double-quoted JS string is the same bug with no encoder at all (CVE-2026-95682 / GHSA-99m7-rm9g-j79h); when probing injected-XSS candidates in PHP apps, test quote characters (' and ") separately per render context — a server-side "properly JSON-encoded" value is still injectable if the delimiter is a quote the encoder doesn't escape.

Tracked from this wave without publication: TOTP-branch disabled-user guard no-op via missing SELECT column (advisory itself says re-validation downstream makes it harmless), enrichment module-name silent fallback (availability-of-unintended-feature only), installer world-writable log, assorted reflected XSS singles without new render-context classes.

September 22 evening follow-up: unregistered action verbs, ACL typos, and validate-then-cache invitation tokens

Three more authorization-drift exemplars from the Sept 22 18:33–19:57Z waves, all siblings of this page's core rule (authorization is a lookup/coverage problem, and coverage bugs fail open):

  • Deepstream PATCH_MULTI permission bypass (CVE-2026-63116, GHSA-89vx-jh4q-vg3w, 8.8): the record permission engine maps allowed actions in a RULES_MAP; PATCH_MULTI was never registered, and the engine's "no rules found for this action" branch calls back with allow. Any authenticated minimal user writes arbitrary data to any record via WebSocket {topic: RECORD, action: PATCH_MULTI}. Operator rule: for any action-map/verb-registry authorization engine, enumerate sibling verbs the registry omits — read the map's keys from protocol/docs, then try every action verb not in the map. The fail-open default on "unknown action" is the bug; PATCH covered but PATCH_MULTI absent is the classic late-added-endpoint drift.
  • MISP ACL typo converts a permission string into nothing (CVE-2026-95805, GHSA-c2r5-m9w2-228q): the ACL entry for previewEventAttributes references theming_enabled* (stray asterisk) instead of theming_enabled; the malformed key matches no real permission, so the action's access control malfunctions (bypass or deny depending on evaluation logic). Adjacent entry previewEventObjects uses the correct string — diff adjacent ACL rows for spelling drift. Sweep rule on any file-based ACL config (MISP, framework acl.ini, route-permission tables): grep for permission values that don't exist in the role/permission master list; an unmatched key is not "closed", it is undefined behavior.
  • Databasement validate-once invitation tokens (CVE-2026-95654, GHSA-9cq7-qg25-29v5, 7.4): the invitation flow validates the token only when the acceptance page loads, caches the authorization decision, and never re-checks token validity at the accept step. A leaked/forwarded invite link stays usable after the legitimate recipient has already accepted — the second accept overwrites the account password. Operator rule: for every token-gated flow (invite/reset/verify), run the stale-token replay battery — load the page, let the real use happen, then replay the cached-authorization accept step. Validation at render ≠ validation at effect; this is the page-load-vs-acceptance twin of the render-vs-process authz parity rule.

October 2 late-wave follow-up: an auth plugin that fails open upward — leftover session attribute grants agent privilege (Dogtag EST)

  • Dogtag pki-core CMCAuthForEST fail-open to agent privilege (CVE-2026-104988 / GHSA-qgmm-v3v3-wff2, 8.1): an EST fullcmc enrollment submitted via BasicAuth without an end-user client certificate leaves the SSL_CLIENT_CERT session attribute holding the EST subsystem's own agent certificate, and downstream authorization judges the request as agent-privileged → an authenticated EST user obtains CA-signed certificates with arbitrary subject names. This page's fail-open family with a new carrier: the missing credential doesn't default to anonymous, it defaults to the highest-privileged identity that previously passed through this servlet/session. Two sweep rules: (1) on any auth plugin wrapping a shared subsystem (EST/ACME/CMP gateways behind an RA, proxy-authenticated admin consoles), submit the request through the plugin path without the expected credential and observe which identity the audit log names — a log line crediting the subsystem's agent cert is the finding; (2) session attributes set by an upstream component are ambient authority: test the downstream route directly and through the upstream with and without per-request credentials, and diff the granting identity. CA-side proof stays lab-only: disposable CA, synthetic subject names, never production PKI issuance.

Evidence and reporting checklist

  • Airflow proofs use two lab principals (cookie-attacker + bearer-victim), synthetic tokens, and record which identity the audit log shows as the primary evidence.
  • Post-logout replay proofs state token TTL and use only synthetic tokens; never retain real session material.
  • Verb-matrix tests run against a disposable instance with security controls observable (rate-limit counters, challenge presence), never against shared/production MISP.
  • IDOR/reparenting proofs use two lab events + synthetic report rows; report the exact field (id vs uuid) reaching save().
  • SSRF/file-read proofs use owned callbacks and lab marker files.
  • MINA claims cite the artifact coordinate and the decompiled presence/absence of the override.

Tracked from the same wave without publication

  • fetchmail NTLM stack overflow (CVE-2026-94184), libX11/libXrender malicious-X-server overflows (CVE-2026-88806/88807), Corosync totempg heap overflow (CVE-2026-94381-adjacent vj89), Netty compression pair — client/library memory-safety class per parser precedent.
  • UVdesk privilege/XSS trio, Ditty stored XSS, Aureus ERP chat XSS, Leantime, iDirect WebServer, ST/iDirect singles — sparse product-specific items without new axes.
  • Thinkst Canary Redis-service DoS (CVE-2026-85220, 3.7) — DoS-only; noted because it targets a honeypot service, no operator workflow.
  • Linux kernel wave (l2tp, nfsd family, ksmbd, zram pair) + Oracle Hyperion/VirtualBox sync items — kernel/desktop class, tracked.
  • openshift/oc-mirror PGP signature-verification bypass (CVE-2026-75939, 7.4): signature-error check runs before the signed body is fully processed → valid Red Hat key ID + forged signature accepted; requires MITM of the signature endpoint to poison a disconnected registry mirror. Adjacent to the extraction-before-verification family (Grafana plugins) — revisit if a replayable mirror-poisoning workflow with a disposable registry lands.
  • Apache NiFi Registry NAR-coordinate path manipulation (CVE-2026-87976, unnormalized path-containment check), NiFi Process-Group migration authorized on Connector alone (CVE-2026-86089), and Connector config missing Asset/Secret authorization (CVE-2026-81866) re-surfaced from the Sept 16 wave with detail enrichment; sibling of the delegated-authorization and unnormalized-path families already covered — no new page, tracked here as processed.
  • Tuleap Enterprise OS command injection (CVE-2026-84285, 8.8) — sparse description, no reachable-sink detail; revisit when technical detail lands. Update (Oct 12 12:30Z wave): Tuleap Enterprise 17.0–17.5 six-GHSA coordinated dump landed — OS cmdi (CVE-2026-105407/GHSA-27gp, 8.8), tracker-DB SQLi (CVE-2026-105412/GHSA-h33c), tracker-admin-on-one-tracker → update/delete fields of ALL trackers (CVE-2026-105411/GHSA-7mq4, 8.1 — supplied-scope-vs-owned-scope class, already on the Sept 18 ignored-scope page), three stored-XSS legs (105408/105409/105410). All descriptions remain sink-free vendor boilerplate — tracked no-publish; the cross-tracker field-write leg adds no new axis over the DolphinScheduler scope fold. Revisit the cmdi/SQLi legs if vendor advisory detail lands.

September 22 evening follow-up: async-authorization bypasses and subject-kind coverage gaps (Sept 22 20:34–20:40Z sync wave)

  • Unleash missing await on a permission check (CVE-2026-77426, GHSA-72h8-wp98-7hch, high, fixed 8.0.3): POST /api/admin/segments/strategies is route-level permission: NONE and self-checks via this.accessService.hasPermission() — without await. hasPermission() returns Promise<boolean>; a Promise object is always truthy, so if (!hasFeatureStrategyPermission) never fires → any authenticated user reaches the admin sink. Same wave: cross-project IDOR on other admin routes + a project member reordering any other project's activation strategies (CVE-2026-77425, GHSA-5ffh-6f9q-5hhr) + clone-feature from unreadable projects (CVE-2026-76910, GHSA-8xcj-9hfr-fh9j). Operator rules: (1) for any JS/TS app, handler-local permission checks are a grep target — search route handlers for if (!await vs bare if (! on async check names; a missing await is an auth bypass with no error and no log; (2) test every admin route as a low-privilege principal — route-level "NONE + handler checks itself" is the fragile pattern, route-level declared permission is the enforced one; (3) ordering/reordering/clone endpoints are authorization sinks just like read/write — reordering another tenant's strategies also bypasses the audit log.
  • Gardener manage-members gate covers only User-kind subjects (CVE-2026-79767, GHSA-gfjv-gqf2-c888, fixed 1.143.3/1.144.2/1.142.6): the customverbauthorizer diff-checks old/new member lists through findHumanUsersWithRoles(), which tracks only isHumanUser() subjects — a project admin without manage-members adds a Group subject (system:authenticated = every authenticated user gets project access) or ServiceAccount subject and the check never sees it. Sweep rule generalizes every kind-coverage drift on this page: when an authorization check filters the subject/object set by type (human vs group, User vs ServiceAccount, first-party vs federated identity), attempt the change with each subject kind and diff which legs the check observes. Documentation saying "controls users and groups" while code gates only users is the audit headline — read the predicate, not the docs.
  • KubeEdge control-plane fields concatenated into shell commands on edge nodes (CVE-2026-62371 NodeUpgradeJob spec.version/spec.image into keadm upgrade edge GHSA-5jpj-293f-rhvj; CVE-2026-62182 ConfigUpdateJob updateFields values through a system shell GHSA-m3c6-2p7h-cfr3; both fixed 1.21.2/1.22.2/1.23.1): permission to create/update two CRD types = arbitrary command execution on every enrolled edge node (Kubernetes RBAC → edge-host RCE privilege ladder; edge nodes typically sit in unmonitored OT/branch offices). KubeEdge fingerprinting for authorized engagements: keadm-managed nodes, cloudcore on 10000/10002/mqtt 1883. Validation stays inert: create a job with an option-shaped canary in spec.version against a lab edge node and capture the constructed command, never the executed effect. This is the cluster-resource-field → shell-string leg of the command-wrapper family (join the Octavia generated-config and Temporal subprocess-provider exemplars).

September 22 late-evening follow-up: membership lifecycle state omitted from access queries (Vaultwarden)

  • Vaultwarden ≤1.37.3 skips membership-status filters in three cipher-access queries (CVE-2026-95814, GHSA-hm7r-36jx-j25q, high): get_user_collections_access_flags, get_group_collections_access_flags, and is_in_full_access_group join organization membership without checking member status, so revoked and not-yet-confirmed members retain read/write/delete/attachment access to org ciphers server-side. This is the lifecycle-state axis of this page applied to the membership row instead of the credential: the query asks "does a membership exist?" where the policy means "does an active membership exist?" — exactly the Keycloak disabled-client-recheck and Airflow replay-after-logout shape (Sept 19/21). Sweep rule: for every org/tenant/team authorization query, diff the predicate against the membership-state enum (invited/accepted/confirmed/revoked/suspended/deleted) and test each non-active state with a disposable two-org lab vault: confirm access as pending member, revoke, re-attempt cipher read + attachment download; the revoked-principal-still-serves decision table is the finding. Never touch real vaults; synthetic secrets only.

September 23 follow-up: cluster metadata service trusts client-supplied node identity (Apache Doris)

  • Apache Doris Frontend (FE) meta service improper authentication (CVE-2026-31377, GHSA-6cx7-4x8x-cqpx, high, fixed 4.0.8/4.1.4): internal FE metadata-service endpoints relied on client-supplied node information for authentication — no independent proof of the requesting party — so an unauthenticated remote attacker (under certain network configurations) reaches internal FE metadata interfaces and reads sensitive cluster information. 1.2.x and earlier are unaffected (the header-trust surface arrived with 2.0). Operator axes: (1) client-asserted identity inside a cluster is this page's precedence family applied to service-to-service auth — for any distributed system (OLAP engines, Kafka/Zookeeper-adjacent metaservices, DB coordiders), enumerate the "internal" port family and test whether the claimed peer (node id, backend address header, RPC identity field) is asserted vs proven (mTLS, shared secret, network placement); an asserted peer means anyone routable to the meta port is a cluster member. (2) Doris fingerprinting for authorized engagements: FE query/HTTP/meta port family; positive control = a request naming a fake node id, negative control = no identity field — if both are accepted, the auth layer reads nothing. (3) The version-boundary note (1.2.x unaffected) is a recon signal: header-trust auth designs arrive with major rewrites, so scan-version alone predicts the surface. Prove read exposure with synthetic cluster metadata only; never pull production table/schema inventories.

October 1 follow-up: a verifier return value that never says "unverified" (grpc-js)

GHSA-m9gg-hp2v-232j (high, fixed 1.13.6/1.14.5): with server credentials created requireClientCertificate: false, getAuthContext() returns unauthorized client certificates indistinguishably from authorized ones — the return shape carries no verified bit. @grpc/grpc-js-xds in some configurations sets exactly that option and feeds getAuthContext into RBAC decisions → RBAC authenticates peers whose certificate chain failed validation. This is this page's truthiness axis applied to a verdict object: the consumer reads "a peer certificate is present" as "the peer certificate passed," because the API gives it nothing else to read. Audit rule: for any mTLS/identity surface, locate the object that crosses from the TLS layer to the authorization layer and answer what it encodes about verification state vs mere presence — then test with a cert signed by any CA the server presents (or a foreign valid cert) against an RBAC rule that should deny. Service-mesh/xDS deployments (istio-adjacent envoy→grpc-js control/data planes) are the population; fingerprint DownstreamTlsContext require_client_certificate settings read-only and report the intersection with RBAC-on-authcontext configs the way this page handles config-intersection surfaces. Proof stays lab-only: disposable CA, synthetic SPIFFE-style identities, allow/deny decision table per cert-leg; never forge certs against live mesh infrastructure.

October 1 follow-up: flatten-view ACL drop and session-cached OTP counters (MISP <2.5.48, folded)

  • MISP event-flattening authorization bypass (CVE-2026-103659, GHSA-r4v7-fgm4-552c, high, fixed 2.5.48): requesting an event with flatten enabled strips Object containment and re-emits object attributes as top-level event attributes — without re-applying the object-level distribution/sharing-group ACL. A user who can view a community-distributed event reads attributes belonging to organisation-only (distribution 0) or sharing-group-restricted objects. This is the page's container-vs-child axis (Sept 21 octet item GHSA-cj24-5f9w-vhx6) reached through a view transform: every query parameter that reshapes response structure (flatten, expand, merge, include=) is an authorization-recalculation checkpoint the developers forgot. Bonus lesson from the fix history: the first remediation reused the full Object contain conditions (soft-delete state included) as the gate and broke event owners reading deleted attributes — the final fix isolates the distribution ACL as the sole gate; when re-testing vendor fixes, check both the bypass and the over-correction (positive/negative pair per transformed view).
  • MISP TOTP accepts the same code twice inside its validity window (CVE-2026-103655, GHSA-2hxj-rrwr-5g5v, critical): no consumed-period record, so a code observed once (shoulder-surf, compromised client, network interception) replays for ~30 s to mint a second session. MISP HOTP (paper token) replay + counter rewind (CVE-2026-103651, GHSA-28mq-63wc-4252, high): verification compared the submitted token against the counter cached in the user's session at password-step time, not the authoritative DB counter — a burned token replays as long as the session cache is stale, and counter state corrupts for subsequent legit tokens. The invariant: the OTP check must read server-authoritative consumption state, never state the session (client-side) carries or caches. On any MFA-enabled product with a lab instance, test replay-in-window (TOTP) and replay-after-consume (HOTP/OOB) as a standing pair — a "correct" first factor step that caches second-factor state server-side-but-session-scoped is the shape.
  • Sweep rules added: (1) enumerate response-shaping parameters on every read endpoint and diff which ACL predicates survive each transform (row-level, object-level, sharing-group, distribution); the flatten shape generalizes to GraphQL fragments, OData $expand, REST ?embed=. (2) For any two-step auth flow, inspect where step-2 verification state lives; if a client-controlled or client-derived artifact (session copy, cached counter, pending flag) participates in the equality check, attempt consume-then-replay. Prove only in a disposable MISP with synthetic events and your own tokens.

October 1 follow-up: gateway plugin auth drift, non-secret identifiers as credentials, and lifecycle/setup gates (Oct 1 12:31Z wave, folded)

  • Apache APISIX plugin-trio (all fixed 3.19.0): (1) CVE-2026-94269 / GHSA-37wm-6fqc-r75p — non-canonical URL paths decide authorization: when a permissive route overlaps a protected one, a crafted encoded path matches the permissive route's policy yet reaches the protected upstream — route matching and policy evaluation disagree on which path they're judging. Sweep: for every gateway with overlapping route shapes, request the protected upstream through encoded/uncanonicalized spellings (%2e, %2f, case, duplicate separators) of the permissive prefix. (2) CVE-2026-94276 / GHSA-9v5j-4cf2-q593 — openid-connect remote introspection accepts a token that introspects active for issuer A on a route restricted to issuer B: the introspection response's active bit was consumed without binding the returned iss to the route's configured issuer. On any multi-tenant IdP, present a token from tenant A's issuer at tenant B's introspection-protected route — cross-issuer replay is this page's peer-identity axis (Apache Doris) at the OIDC layer. (3) CVE-2026-94212 / GHSA-9cp4-6jfv-7725 — saml-auth under default configuration fails signature verification so any unauthenticated attacker impersonates any user on every SAML-protected route. Default-config auth plugins are the audit target: enumerate plugin default fields (signing required? audience checked?) from the schema, not from the deployment's tuned config.
  • Fleet device API authenticates with hostname and hardware serial (CVE-2026-103264 / GHSA-rmfg-c7hm-wv58, critical, <4.87.0): the device endpoint accepts non-secret identifiers (hostname, serial) as auth tokens in addition to the device UUID — an unauthenticated attacker who knows or guesses a hostname/serial authenticates as an iOS/iPadOS host and triggers device-scoped actions incl. software install and MDM migration. Standing sweep for every device/enrollment API: enumerate all accepted token fields (not just the documented one) and test each against public or enumerable identifier sources (MDM inventory leak, procurement sheets, mDNS, SerialNumber= in support tickets). A credential is anything the endpoint compares, whatever the docs call it. Adjacent same-product fold: CVE-2026-103265 / GHSA-hfhg-8x2p-pvmg (medium, <4.89.0) — MDM command results not filtered by team authz when one command UUID spans teams → cross-team read of host UUIDs/payloads/device responses (shared-artifact-ID cross-tenant oracle, Sept 21 shape).
  • MISP discussion posting checks only org-only distribution (CVE-2026-103858 / GHSA-vwhj-wmrx-p88c, medium, <2.5.48): posting/replying to a thread verified single-org restriction but skipped sharing-group membership and event-level visibility → read thread title + quoted post and inject posts into threads you can't see. Container-write path checked a subset of the read-path ACL — the write leg frequently lags the read leg in predicate coverage; on any threaded/discussion feature, diff the ACL predicate of post vs view endpoints with a two-sharing-group lab.
  • oc-mirror embedded cache registry binds 0.0.0.0 unauthenticated (CVE-2026-96577 / GHSA-gh86-jqm4-pwg7, high): during mirroring the local cache registry is reachable by the adjacent network to push tampered images / delete cached content. Recon rule for air-gapped/disconnected-update workflows: the "local" helper tool spawned by a mirroring/sync job is a network service; ss -ltnp on any build/airgap jump host during a mirror run finds it. Push-proof stays lab-only with a throwaway image tag.
  • ground-station first-run setup mode is unauthenticated SQL + admin plant (CVE-2026-103244 / GHSA-wwmv-hj9m-4mcc, critical, <0.8.0): setup.restore over Socket.IO executes arbitrary SQL while the app is in first-run mode, planting admin users and forged session tokens. The install-gate family (Craft install gate precedent, Sept 17 page): fingerprint setup mode on first scan (/setup, /initialize, unauthenticated restore/import RPC namespaces); a reachable setup endpoint means the instance was never completed or the completion check is client-side — test setup.* methods on Socket.IO namespaces even on instances that present a login page.

October 1 late-wave follow-up: auth filters that report-but-don't-stop, permission-list self-extension, and a path-from-certificate file read (Foreman/Katello + Keycloak)

  • Foreman templating safemode bypass → RCE (CVE-2026-96658, GHSA-g2wg-rx2f-4j5r, 9.9): a low-privileged authenticated user appends unauthorized functions to the sandbox's allowed-execution list through improper handling of delegated methods, then runs arbitrary commands on the Foreman host. This page's allowlist axis applied to a template sandbox: the safemode allowlist is a runtime-mutable data structure, and the mutation path (delegated-method registration) is itself reachable through the sandbox's own API. Sweep rule for any product with a "safe" templating/report engine (Foreman/FreeIPA-style provisioning, Jasper, Jinja-sandbox apps, low-code report builders): enumerate how the allowed-helper list is populated and attempt to add to it from inside a template before trying to call anything forbidden — registration/inheritance legs are usually unchecked. On authorized Foreman/Satellite installs, a low-priv test user + one inert marker helper call is the proof; never render commands on a provisioning-integrated host.
  • Katello registry-proxy fall-through: the deny runs, then execution continues (CVE-2026-56098, GHSA-f6cg-x72x-c392): registry_authorize detects the unauthorized request and calls its unauthorized method — missing return — so the controller proceeds into business logic and DB validation filters, leaking valid Users, Organizations, and Products instance-wide through differential responses. Canonical Rails/Rack-class shape: an auth filter that renders/reports an error without halting the filter chain or method. Operator test: send an unauthorized request and diff status and body/timing against a nonexistent-id request — if you still get id-validity signal under a 403/401, the fall-through is live; it converts any filter-style authz into an enumeration oracle even when the "real" action is blocked.
  • Katello RegistryProxiesController raw-SQL path params (CVE-2026-56097, GHSA-mmq3-mv36-wfm7): check_blob_push_org_label / get_matching_products_from_org interpolate labels taken from the request path into raw SQL fragments — reachable with only create_personal_access_tokens permission, even for a user with no Organization/Location assigned. Two axes: (1) path-segment values are untrusted input like any query param — SQLi sweep must include path placeholders, not just bodies/queries; (2) the entry permission is a token-minting right nobody treats as sensitive — enumerate which minimal permissions open which controllers, since "can create a PAT" ≠ "can read products" but the code doesn't agree.
  • Keycloak X.509 CRL Distribution Point file-path read (CVE-2026-103884, GHSA-xfh5-8fx4-3crm): with CRL Distribution Point checking enabled, the client-certificate authenticator fails to validate file paths inside the certificate — a crafted certificate makes the server read local files (sensitive-file disclosure) or load huge files (memory-exhaustion crash). The certificate itself is an input channel: extension fields (CRLDP, AIA/OCSP URIs, policy qualifiers) are attacker-chosen URLs/paths fed to server-side fetchers/readers. Standing sweep for every mTLS/X.509-enabled surface (IdPs, gateways, mutual-TLS APIs): with CRLDP checking on, present a client cert whose DP is a file:// path or a URL to an owned callback — a path/URL the server dereferences from cert content is SSRF/file-read with the client cert as the payload carrier. Lab proof only: your own CA, synthetic cert, benign local marker file; never read production files.

Class invariant for this wave: the check fired ≠ the check stopped anything — Foreman's sandbox allowlist grew instead of denying, Katello's authorize filter denied-and-continued, and Keycloak's CRL check dereferenced attacker-chosen paths while "validating." For every gate, test the gate's own failure and data paths, not just the happy deny.

October 2 follow-up: MISP correlation-table stale distribution snapshots (3 GHSAs, folded on the standing MISP axis)

Three more MISP records (published 2026-10-02T18:31Z) extend this page's MISP ACL-drift family with a new carrier: the correlation table stores a point-in-time copy of each event's distribution level and sharing group, and authorization on correlation-driven views is judged against that stale snapshot instead of the live ACL — and the snapshot lacks the published flag entirely.

  • GHSA-qxmx-5j2w-4wjc / CVE-2026-104910: related-events listing returns metadata (title, date, correlating value counts) for events the caller may not open — unpublished events, or events whose distribution/sharing group was narrowed after the correlation row was written.
  • GHSA-w4mw-2637-h8xp / CVE-2026-104912: attribute searches that trigger correlation lookups authorize correlated attributes/events from the stale row → read attributes of events you lost access to.
  • GHSA-mw5r-825j-vg45 / CVE-2026-104914: soft-deleted attribute search/paginated listing omit the owning-org restriction the event detail view enforces — cross-org deletion-attribute disclosure through an alternate query path (read-route vs detail-route drift, the same shape as the Sept 21 octet and Oct 1 flatten items).

Durable axes:

  1. Derived-index denormalization is an authorization time bomb. Any side table that copies ACL-relevant columns (correlation tables, search indexes, materialized views, caches) authorizes yesterday's policy. Operator test for any product with correlation/link/index side tables: with two disposable orgs, create a correlation, then narrow the target event's distribution (or unpublished it, or move sharing group), then re-read through the correlation-driven route. Positive = stale-snapshot disclosure; the live-ACL route denying while the correlation route leaks is the decisive differential.
  2. Soft-delete state is an ACL predicate too. Every listing endpoint with a "deleted" filter gets the owning-org/visibility check diffed against the primary detail view — deleted rows frequently lose the row-level predicate entirely.
  3. Cumulative MISP pattern across Sept 21/Oct 1/Oct 2: the main view enforces, the alternate projection (flatten, correlation, search, deleted, discussion) forgets. On any MISP-like instance, enumerate projection/transform query parameters and side-table query paths first; that's where the last three waves of bugs lived.

Tracked without publication from the same MISP batch: remote event preview, TAXII object viewer, and ID Translator XSS (stored-XSS class, page's existing policy), index-table stored XSS, decaying-model import input validation.

October 6 follow-up: SSSD access-rule chain exits on a warning, and a sentinel expiry value reads as "never" (2 GHSAs)

The October 6 SSSD advisory wave (15 GHSAs, mostly local responder DoS — tracked) contains two access-control legs that extend this page's evaluation-order family:

  • Expired-password warning terminates LDAP access-rule evaluation as success (GHSA-3hrm-ww2w-wqqc / CVE-2026-92821, 6.8): when SSSD is configured to evaluate password-expiration warnings before restrictive access rules, the warning branch ends the evaluation loop and the request is admitted. A remote authenticated user with an expired password authenticating by an alternative method (SSH public key) skips every configured access restriction. This is the check-fired-≠-check-stopped invariant with a new exit state: not fall-through-after-deny but informational-branch-as-terminal-success — a policy chain that treats "produce a warning" as "produce a decision." Operator test on any SSSD/LDAP access-rule deployment in authorized scope: disposable user with forced-expired password + key auth, verify whether access_provider = ldap restrictions still deny; the decisive differential is key-auth login succeeding past a rule that denies the same user under password auth. Sweep rule for any policy chain with warn/deny/allow branches: ask what terminal state each branch returns, especially the warn/degrade branch.
  • Shadow expiry attribute value 0 is not treated as expired (GHSA-v9m6-j2qm-86p2 / CVE-2026-104033, 5.4): with LDAP shadow-attribute account expiration enforced, an expiration value of zero — the epoch-zero sentinel most deprovisioning scripts and directory imports write — passes the expiration check, so deactivated accounts keep authenticating. Sentinel-value semantics axis: lifecycle fields (0, -1, empty, 9999-12-31, NULL) mean "never expires" to the writer and often "valid" to the parser. When validating deprovisioning or account-lockout controls on directory-integrated systems, explicitly test an account whose expiry field holds each sentinel spelling, not just a real past date.

Cumulative for this page: the gate's failure path (Katello deny-and-continue), the gate's warning path (SSSD warn-and-admit), and the gate's sentinel input (expiry 0) are three distinct bypass planes on the same access rule — test all three, not just deny.

Tracked from the same SSSD batch without publication: NSS/KCM/autofs/PAM responder DoS dozen (integer-underflow packet parses, unbounded negative cache, UAF on connection reuse, idmap OOB write) — local availability class; and the Entra ID Graph OData quote-injection leg (GHSA-p2pv-jqxp-33qv / CVE-2026-104040) folded on the Sept 18 query-grammar page as a new OData axis.

October 7 follow-up: SSSD HBAC compares short usernames only — trusted-domain identity collision (CVE-2026-104048 / GHSA-474r-m56w-5qgh, medium)

In trust-enabled environments, SSSD evaluates Host-Based Access Control rules by stripping domain qualifiers and comparing only the short username — an authenticated user in a trusted domain whose username matches an authorized local account passes HBAC rules written for the local user. This is the identity-normalization plane of this page: the rule engine canonicalizes the identity before comparing it, and the canonical form is lossy in exactly the direction that widens access.

  • Generalization across the page: gates fail via their deny path (Katello), warn path (SSSD warn-and-admit), sentinel input (expiry 0), and now normalization collision — any principal-uniqueness assumption (case folding, realm/domain stripping, UPN→sAMAccountName reduction, email→username derivation) turns every identity namespace that normalizes into the same string into an alternate-authentication path. Audit rule for any multi-domain/multi-realm access check: enumerate the normalizer's collision set (user@domainA vs user@domainB vs bare user) and test the cross-domain spelling of an authorized username from an identity in a different namespace you control.
  • Engagement relevance on AD-trusted Linux estates: if any forest trust or cross-realm relationship exists, short-name collisions are controllable by whoever can create accounts in the weaker-trusted domain (often self-service). Proof: disposable account in the trusted lab domain named to collide with an HBAC-authorized lab user, record allow-vs-deny differential per realm spelling. Also from this batch, tracked without publication: the Entra ID search-input filter-sanitization leg (GHSA-g6q7-jchg-6v7f / CVE-2026-104047, directory-query filter injection — same OData/LDAP grammar axis already on the Sept 18 query-grammar page via the Oct 6 p2pv leg) and the pre-auth-state memory-exhaustion DoS pair (104046) + kcm length-header stall DoS (m2fx/80048), local availability class.

October 7 03:30Z follow-up: Candlepin multi-parameter verifier aggregates ANY-of instead of ALL-of (CVE-2026-106471 / GHSA-993v-gm7c-q4cq, high)

Candlepin (Satellite/subscription authorization) central authorization filter grants access when ANY ONE of multiple @Verify-annotated parameters is accessible, instead of requiring access to every verified entity. A low-privilege authenticated attacker who can access the first referenced object bypasses the checks on all subsequent objects — cross-organization consumer disclosure and entitlement/subscription modification. New plane for this page's gate-failure taxonomy: the gate isn't missing, inverted, warn-terminal, or normalizer-lossy — it is correctly attached to each entity and then boolean-aggregated wrong.

  • Sweep rule: for any declarative/multi-object authorization layer (annotation-driven filters, policy-as-code allOf/anyOf combinators, middleware that verifies N resources per request), determine the aggregation operator over per-object decisions with a 2-object decision table: attacker-accessible A + attacker-inaccessible B, both referenced in one request. ANY-of shows up immediately as allow-on-partial-access. Target shapes where one object type is self-service-readable (own consumer/own subscription/own entitlement) while the paired object is another org's — that asymmetric pair is the test vector.
  • Same-day sibling shape in role-edit validation: Octopus Server (GHSA-r946-p43h-mw8h / CVE-2026-102478, high): a user with permission to modify roles bypassed the protections preventing access abuse by weakening a built-in role and adding their own account to a privileged team — role-definition writes not gated against self-modification (capability self-grant via a legitimately-held edit permission, the AVideo/LaraDashboard role.edit rename precedent again; proof: disposable tenant, inert permission markers).

October 8 03:31Z follow-up: Octopus Server multi-scoped permission grant aggregation over-grants (CVE-2026-102488 / GHSA-w6j2-5q9c-xv6x, high, SA2026-12)

Second Octopus Server authorization-evaluation defect in two weeks, and the boolean-combinator sibling of the Candlepin ANY-of leg above: multiple scoped permission assignments are evaluated incorrectly, letting a highly privileged user obtain deployment permissions beyond those actually granted. Where the Oct 7 Candlepin leg showed ANY-of where ALL-of was intended, this leg shows scope-set combination widening the union of grants — scoped assignments (environment/tenant/project scopes) composed additively instead of intersecting against the intended grant.

  • Sweep rule that now has two independent product instances: for any RBAC system where a permission can be granted once per scope (per-environment, per-team, per-tenant), build a scope-combination matrix per permission — grant in scope A only, grant in scope B only, grants in A+B — and compare the effective action set against the intersection semantics the UI implies. Over-union shows up as scope-A credentials working in scope-B targets. Vendor advisory SA2026-12. Same-product re-sweep rule holds: the Oct 7 role-self-modification leg and this Oct 8 scoped-aggregation leg were found in different sweeps of the same product weeks apart — when one Octopus authorization bug lands, re-audit the whole permission-evaluation path.

October 8 15:3xZ follow-up: omitted-parameter default-match ATO (Integrics Enswitch) and client-side-only admin auth (mJobTime) — the absent-value battery on telephony and job-scheduling admin panels

Two fresh instances of the empty/absent-value-selects-permissive-branch family (389 DS SELFDN empty DN, DataPower empty-password LDAP same-day):

  • Integrics Enswitch 3.13–4.4 (GHSA-qxpp-mcfv-63xp / CVE-2026-107640, 9.1): /api/json/user/password/update/ requires a reset parameter to be present-and-valid only when a password reset is pending — omit it entirely and an account with no pending reset (whose stored reset_key is the empty default) matches the defaulted-empty comparison, so the password is changed with zero authentication. Two compounding defects worth separating: (1) an optional-in-name control (reset) whose absence silently selects the pass branch instead of the deny branch, and (2) empty-stored-state == empty-supplied-state collision. Sweep rule: on every password-change/reset/verification endpoint, run the absent-vs-empty-vs-garbage-vs-valid decision table for each security parameter (token, code, key, nonce): present-valid and present-garbage should both be denied without a pending state; ABSENT must never be the one shape that succeeds. Pair with username enumeration (this product allowed it) = admin ATO from a single unauthenticated POST. Enswitch is a widely deployed SIP/PBX platform — its /api/json/ family is a standing recon target class on unified-comms engagements (lab PBX only).
  • mJobTime through 15.7.3.32 (GHSA-fw9h-fgmp-7fx3 / CVE-2026-9209, 9.8): the Login.aspx admin panel's runQueryButton postback and exportSqlQuery_Server PageMethod execute caller-supplied SQL as DBA/sysadmin against the backing Sybase SQL Anywhere database with no server-side authentication beyond a client-side sessionStorage flag — xp_cmdshell/xp_read_file reachable ⇒ single-HTTP-request pre-auth RCE as LocalSystem. The durable rule is the trivially-general one: any state the server does not re-check is not a control — sessionStorage/localStorage/disabled-button "auth" is fingerprintable by simply POSTing the handler directly (bypass the page, hit Login.aspx methods with the same parameter envelope). On any legacy .NET WebForms-style appliance: enumerate __VIEWSTATE postbacks and PageMethod endpoints independent of the login flow; a SQL-exec box exposed at 9.8 means the admin panel was never authenticated server-side at all.

Cross-ref: the absent-param-branch rule also appears in the Sept 17 SPIP Crayons leg (omitting secu_ selects an unconditional-TRUE dispatcher branch) — three products, one bug class: controls that only fire when their parameter is present fail open on absence.

October 9 06:31Z follow-up: FalkorDB Bolt endpoint — auth delegated to a backend error string, parser live while the feature is "off," and ASSERT-only bounds checks (7-GHSA wave)

FalkorDB (graph DB shipped as a Redis module; a Neo4j Bolt protocol listener rides a separate BOLT_PORT, disabled by default) published a coordinated seven-advisory wave fixed 4.20.0 / 4.18.x. This is the same product whose Sept 21 memory-safety six-pack was tracked here without publication — the same-product re-sweep rule paid off: the second wave contains a genuine new authorization plane, not just more decoder crashes.

  • Auth gate keyed on ONE backend error string; every other error means "authenticated" (GHSA-f2fq-5xqf-g726 / CVE-2026-107910, critical): is_authenticated() decides whether a password is required by issuing an empty AUTH command to Redis and treats only WRONGPASS as "a password is required." Any other error reply — LOADING while the dataset loads, MASTERDOWN during replication failover, OOM under memory pressure — classifies the Bolt client as already authenticated, giving unauthenticated graph-query execution. New sub-axis for this page's gate-failure taxonomy: the gate has no failure path of its own — it delegates the decision to a backend call and then pattern-matches one error token, making every unrecognized backend state an allow branch. Sweep rule: wherever an auth gate shells a decision into a backing store/cache/replica, enumerate the backend's full error vocabulary (loading, failover, timeout, quota, disconnect, shutdown-in-progress) and ask which spelling returns "allow"; lab triggers for the transient states are real — bulk-load a dataset, kill the master mid-failover, apply memory pressure against a disposable instance. Timing-window corollary: LOADING-window access is scheduled attack surface — a restart-to-load is operator-predictable.
  • Parser surface active even when the feature flag is off (GHSA-g4x7-54hf-p7f3 / CVE-2026-107911, high): the _read_flags handler accepts a --bolt argument on the normal Redis command path, casts the following Redis string object to a Bolt client struct with no origin check, and result-set code dereferences pointers read from that object — and the argument is parsed even when the Bolt endpoint is disabled, so default configurations are affected. Fingerprint rule for any multi-protocol server: config gates routinely disable the listener but not the parser/handler code behind it; probe feature-flag arguments (--<feature>, protocol-named flags, alternate-protocol keywords) on the primary port even when the feature port is closed. Type confusion via "cast attacker string to internal struct" is the memory-corruption tail of the same finding.
  • ASSERT()-only bounds checks, compiled out in release builds (GHSA-jpx9-94mp-7xhq / CVE-2026-107908 critical — Bolt RESET with attacker-chosen 16-bit chunk size moves up to 64 KiB backwards past the read buffer; GHSA-x67x-q5pm-p9h7 / CVE-2026-107909 high — WebSocket frame with 64-bit extended payload length XORs past the receive buffer; GHSA-fmqx-854h-cfff / CVE-2026-7826, GHSA-fwp9-2ghh-pw8g / CVE-2026-7827 critical, GHSA-gxrc-33jm-h5ch / CVE-2026-5759 critical on the RDB replication-stream decoders — reachable against any passwordless Redis-protocol instance): four legs where the only bounds/validation check is ASSERT(), which release builds delete. Recon/audit rule for C projects: grep the target's parse paths for assert-macro validation (grep -n "ASSERT\|assert(" src/), and treat "has a check" findings from source review as build-mode-dependent — differential-test the same malformed input against a debug build and the shipping release build before trusting the vendor's claimed control. The three RDB-decoder legs pair with the June GDB/Redis-replication precedent: replication command families (SYNC/PSYNC/RESTORE-class) are an under-swept attacker-controlled deserialization channel on any exposed, passwordless Redis-shaped service.
  • Surface inventory for engagements where FalkorDB or Redis-module stacks appear: three separately fingerprintable listeners (Redis port always, BOLT_PORT when enabled, replication channel on the Redis port) × two protocol parsers (RESP flags path, Bolt/WebSocket framing) — enumerate all three before concluding "feature disabled = surface absent."

October 9 14:0xZ follow-up: Strawberry GraphQL — permission gate truthiness-tested on an un-awaited coroutine (awaitable-is-truthy fail-open)

Strawberry 0.217.0–0.326.0 (GHSA-pfvf-fwfp-25mp / CVE-2026-107728, high, fixed 0.326.1): PermissionExtension.resolve() decides sync-vs-async by inspect.iscoroutinefunction(has_permission) — i.e. by the declaration spelling, not by the return type. A plain def has_permission() that returns an awaitable (a coroutine object, an asyncio.Future, an aiohttp call handle) takes the synchronous path, where the gate is a bare truthiness test. Coroutine objects are always truthy, so a permission that resolves to False still admits the resolver; the protected field executes under both execute_sync() and execute() whenever the field's resolver is sync. The fix fails closed: the sync path now raises if the return value is awaitable.

New leaf on this page's gate-failure taxonomy: the gate runs, returns a decision-in-waiting, and the engine tests the wrapper instead of the decision. Durable sweep rules:

  • Un-awaited-truthy sweep (Python): any security callback (has_permission, policy hooks, middleware predicates, validator callables) consumed by if result: without await/await_maybe fails open for every implementation that returns an awaitable. The fingerprint is exactly Strawberry's mistake — dispatch on declaration (iscoroutinefunction, async def grep) instead of return-value type. In target audits: grep permission classes whose has_permission is def (not async def) and whose body calls async helpers — those are pre-exploitation on any GraphQL/permission framework.
  • Black-box probe for GraphQL stacks: the shape is invisible from the wire, but the negative control is cheap — a field behind an intentionally-denying permission should return a permission error; a null-data-with-errors vs data-returned mismatch on deny-precondition fields (rate-limited check, IP allowlist implemented as async call) is the observable. Report as authorization bypass with the declaration-shape precondition made explicit.
  • Same fail-open family as this page's absent-param and error-token leaves: the control's return was never the control's meaning — a wrapper object, a missing parameter, an unrecognized error string all select the allow branch because the engine only tested "did we get something".

October 9 14:0xZ Banks prompt-library pair — rendered user text parsed as privileged chat messages, symlinked prompt registry

Banks ≤ 2.4.5 (GHSA-hmq2-7hp6-7crh / CVE-2026-107717, medium, fixed 2.5.0): Prompt.chat_messages() parses each line of the final rendered template as a ChatMessage JSON object, and the model accepts any string role. User-controlled template variables that render as {"role":"system","content":"..."} are emitted as privileged system/assistant/tool messages — chat role injection with no model involvement, before the first provider call. Banks ≤ 2.5.0 (GHSA-556j-vv39-8rqv / CVE-2026-107716, fixed 2.5.1): DirectoryPromptRegistry validates prompt names but not symlinks — a symlinked .jinja file inside the prompt dir is indexed and disclosed via get(), and a symlinked index.json is overwritten by set() (arbitrary file read/write where prompt dirs accept untrusted content: unpacked archives, shared repos, multi-tenant folders).

  • Sweep rule for every prompt templating/rendering layer (banks, guidance-style renderers, agent prompt builders): the structurize-after-render step is the boundary — serialization of privileged message structure must bind to template syntax (a {% chat %} block, an escape-marked region), never to content that user data can spell. Test vector is a one-line JSON canary with role:"system" in any field the template renders verbatim; check whether it survives into provider messages with a privileged role (log/echo the message list client-side in an owned lab).
  • The role: str (unconstrained) schema leg generalizes: any message model that accepts arbitrary role strings lets injected lines pick their own trust level — audit role fields for enumeration constraints across LLM SDKs, gateway proxies, and log/audit serializers.
  • Registry symlink legs are the familiar symlink-to-root-consumer class (index/manifest files routinely skipped by per-item symlink checks — same shape as the July 28 zip-lib and Oct 1 registry manifests); keep as tracked-class evidence that metadata files need the containment check too.

Tracked from the same 06:31Z wave (07:1xZ run) without publication: Ops Manager cross-project monitoring-record IDOR by known record ID (w8qp/87108), cross-member pending-invitation disclosure (c448/87109), unthrottled costly monitoring endpoints (gcr6/87110) — known IDOR/DoR classes; Keycloak login-theme input-filter bypass letting a realm admin store script in display fields rendered on the login page (gv8g/107889) — admin-authored stored XSS, class covered, though the IdP login page is a premium phishing sink worth noting on labs; Backdrop CMS config-export protection gap (rcrw/107914); CUPS quad q6rw/39g6/95mw/746c memory-safety/resource-exhaustion. Updated-feed enrichments tracked: GLib D-Bus DBUS_COOKIE_SHA1 server-supplied cookie_context traversal = client-side arbitrary-file-read-with-hash-comparison oracle (hmpf, June-dated RHEL enrichment — server-to-client trust inversion already on the Sept 15 parser page via the Oct 8 Apache Directory LDAP API leg); openvt -u stat-follows-/proc/pid/fd/0 symlink console-owner confusion (6766, Aug-dated, local LPE); ansible.posix authorized_key chown vs lchown/no-O_NOFOLLOW symlink redirection LPE (49qh — symlink-to-root-consumer family already on the July 28 zip-lib page); Mitsubishi GX Works3 in-memory module patch defeats block-password auth (x4hf, local, detail-free, ICS class); Fanvil x7a CORS pair and Chrome 155 pack re-listings (existing tracking holds).

October 13 15:3xZ follow-up: Wazuh reserved-user guard inside a falsy truthiness check — empty password = superuser takeover (fv2h/CVE-2026-74035, high, 4.6.0–<4.14.8)

Wazuh's security.update_user places the reserved-user guard and the password policy inside a falsy truthiness check, so a delegated user administrator sends an empty password to the user-update endpoint, the guard branch is skipped entirely, and the wazuh superuser account is overwritten — then authenticated with empty credentials. Full SOC-platform admin from a mid-tier "can manage users" role, one request.

This is the falsy-operand leaf of this page's gate-failure taxonomy, joined to the Oct 8 Enswitch absent-parameter leg and the Oct 9 Strawberry un-awaited-truthy leg: the guard and its payload share one if value: wrapper, so the value that tests falsy ("", [], 0, missing) skips the guard and the check together. Sweep rules:

  • Code-audit fingerprint: in any update/patch handler, ask whether the "is this input valid / is this target protected" checks are inside the if new_value: block. A handler shaped if password: guard(); policy(); set() fails open on empty — and empty is often a legal stored state, which makes the post-exploitation login (authenticate with empty credentials) work too. Grep target: update_user|update_.*\( handlers where the first statement is a truthiness test on the mutable field.
  • Black-box battery: for every user/PAT/role update endpoint, submit the full field-value battery per security-relevant field: valid, garbage, null, "" (empty string), omitted, array-wrapped, type-confused ({"$ne":...}, numeric 0). The finding is any shape that mutates a reserved/protected object (admin users, built-in roles, system tokens) or bypasses a strength/confirmation check. Wazuh's specific combo — delegated-admin role + reserved-account guard + empty-accepted login — makes the chain: foothold as a low-code user-admin tenant user → blank the superuser → own the SIEM that is watching everyone else (detection-plane takeover class, cf. the wiki's monitoring-credential-cache rules).
  • Reserved-object guards deserve their own decision table separate from policy checks: protected-target rejection must fire before any value-dependent branch, on every presentation shape.

October 14 11:1xZ follow-up: identity-resolution fallback collides on the wrong field (LatePoint) and RBAC exact-path matcher degrades to prefix on case-insensitive mode (grpc-js)

  • LatePoint ≤5.7.2 authentication bypass (CVE-2026-100163 / GHSA-fwf2-rqrw-2q37, high): when "Use WordPress users as customers" is enabled, the public booking step's create_wp_user_for_customer() falls back from email_exists() to username_exists( $customer->email ) — matching any WordPress account whose user_login equals the victim's email address even when that account's actual user_email is different — and links that WordPress user's ID into the attacker-driven booking flow's customer row (privileged roles excepted). The identity the guard looked up and the identity the link binds are keyed on different fields: the check asks "does an account have this email," the fallback answers "does an account have this login," and login strings are frequently emails harvested from anywhere. Sweep rule: on any auto-link/auto-provision handler, enumerate the fallback lookup key separately from the primary one and test with accounts where login ≠ email (subscribers with email-shaped logins are the standard shape on imported/SSO-synced sites); this is the account-linkage sibling of the Sept 16 and Oct 8 email_verified-never-checked OAuth auto-link family — same ATO outcome, achieved through a lookup-field substitution instead of a missing verification check.
  • @grpc/grpc-js-xds RBAC exact-path matcher = prefix matcher in case-insensitive mode (CVE-2026-101914 / GHSA-88h9-xgvx-hvf2, medium, fixed 1.13.1/11.4.1): when RBAC applies different access rules to two gRPC methods where one method name is a prefix of the other, the exact (method-name) matcher under case-insensitive matching admits the longer method under the shorter method's rule. Operator reading: the matcher mode (case-insensitive) silently changes the matcher semantics (exact→prefix) — a config knob on one axis mutates enforcement on another, same leaf as this page's verb-allowlist and tiebreak drift. Black-box battery for gRPC/xds meshes: enumerate service/method full names from reflection, find prefix pairs with different advertised access (Foo allowed / FooAdmin denied), and call the longer name with the shorter name's case-insensitive matcher in play; on code audit, grep path-matchers where a normalization flag (case fold, percent-decode, trailing-slash trim) is applied before the equality comparison — folded-then-equals silently degrades to folded-then-starts-with whenever one candidate contains another.
  • Same wave tracked without publication: @grpc/grpc-js enrichment of already-tracked items; util-linux heap overread and libarchive RAR5 (client/parser DoS class); Debian run-mailcap 2022 backfill re-listing; WP singles already class-covered on the Sept 19 WP pages this run (LifterLMS extract()-before-resolve LFI = variable-overwrite-before-path-resolve leaf tracked there, Mang Board dynamic-<mode>_type-parameter LFI class, Post Export Import admin-gated PHP object injection with vendor-acknowledged no-POP-chain).

October 14 12:3xZ follow-up: TSplus Remote Access — first-factor login completes when the MFA validation result is never applied, plus internal-resource route miss on /socket.io/JWN/ (4468/CVE-2026-108071, 2xg6/CVE-2026-108070, 23j4/CVE-2026-108072)

TSplus Remote Access (remote-application gateway appliance) published a three-leg wave, two of them on this page's precedence/consumption axes:

  • MFA validation runs but its result is discarded (GHSA-4468-w24c-wmgj / CVE-2026-108071, high): with valid first-factor credentials, the authentication flow does not correctly apply the second-factor verification outcome — login completes without a valid TOTP. This is the consume-the-result leaf distinct from the skip-the-check leaf: the second factor can be present in the code path, executed even, and still be advisory. Operator probe on any appliance MFA you're authorized to assess: valid 1F + garbage/absent 2F, and a valid-2F-but-replayed variant as control; a 200/session-cookie outcome on the garbage-2F branch is the finding regardless of what the UI claims about enforcement. Same drift family as Nginx UI's enabled_2fa=OTP||passkey vs EnabledOTP() flag mismatch (Oct 9) — there the flag lied, here the result is dropped; test both: what the config says, what the branch reads, what the response grants.
  • Internal resources reachable through /socket.io/JWN/ with manipulated paths (GHSA-2xg6-fqm4-hc55 / CVE-2026-108070, high, unauthenticated): the web server's access restrictions miss a socket.io-namespaced route family, so crafted paths under it reach otherwise-protected internal resources. Alternate-route-family sweep rule again (Keycloak versioned-account family, Nginx UI missed api/cluster router, Payload /verify/ substring classifier): enumerate every mount/namespace prefix from the server's own JS bundle and 404-vs-content fingerprint each with traversal and internal-resource canaries — socket.io, ws, and RPC bridge mounts are historically where per-route auth was never wired.
  • Same product, local axis, tracked: autostart1.tscr shared-theme login scripts writable by unprivileged users → command execution in another user's session context at next login (23j4/CVE-2026-108072) — shared-startup-file cross-user LPE class (cf. Oct 14 Blinko startup-file leg on the container page); worth one line in terminal-server engagements: check ACLs on theme/autostart script directories.
  • Perimeter-appliance framing for engagements: TSplus sits in the forgotten-remote-access lane (same discovery posture as the SAN/ICS "second management network" rules) — when it's in scope, this trio gives you a pre-auth route sweep, an MFA-result probe, and a local-script ACL check; proof stays on lab instances and owned accounts, never live customer gateways.

October 14 19:1xZ follow-up: PgBouncer SCRAM identity switch — the auth exchange verifies one identity while the session adopts another (CVE-2026-107350 / GHSA-qcxp-3cx5-3f3g, 8.8, 1.17.0–1.26.0)

PgBouncer's SCRAM path accepts a second StartupMessage sent mid-authentication against a non-existent database: the exchange is not rejected, the user being logged in as switches to the one in the second startup message, and SCRAM proofing still runs against the first user's keys. Knowing the password of any user in auth_file admits you as any other SCRAM user — including PgBouncer admin users and the backend superuser. Two more legs same wave: startup-time infinite loop (h8cj/CVE-2026-107349) and mbuf_make_room integer overflow (982p/CVE-2026-107348).

This is the FalkorDB is_authenticated leaf generalized one layer up: there the delegated check admitted on the wrong error vocabulary; here the authentication state machine has two identity slots — the identity being proofed and the identity being adopted — and nothing re-binds them after a mid-exchange re-startup. Sweep rules:

  • Multi-round auth handshakes (SCRAM, SASL, OIDC code exchange, 802.1X): after every leg completes, ask which server-side state fields were frozen at start and which remain client-writable. Any field that names a principal, database, realm, or tenant and can still change mid-exchange is an identity-switch candidate. Black-box battery: re-send the opening message mid-auth (with a different user/database), send the opening message twice, send it after the final client-final but before server-completes; the finding is any acceptance where the proofed credential and the granted session identity differ — verify with a canary pair (own low-priv account proving, target canary admin identity adopting, then read an admin-only surface like PgBouncer's SHOW STATS/admin console).
  • Connection-poolers are auth-delegating intermediaries — same audit posture as the FalkorDB Bolt-over-Redis and Impala executor JWT legs on this page: the pooler holds backend superuser credentials and decides who becomes them; a single identity-binding defect converts a tenant credential into fleet-wide database access. When a pooler (PgBouncer/PgPool/ProxySQL/HikariCP-side gateway) is in scope, test the handshake itself, not just post-login authorization.