WordPress alternate-surface authorization drift wave (Sept 19 09:32Z)¶
Source: hourly offensive-security scan of GitHub Security Advisories, 2026-09-19 (wave published 2026-09-19T09:32Z, WPScan/WPScan-rp style descriptions).
The recurring shape: the primary UI enforces authorization; the alternate surface for the same object (REST route, AJAX action, front-end form handler, import/export job, social-login resolver) enforces nothing — or enforces a different thing. Multiple records this wave also show capability-vs-intent drift: a delegated plugin-specific permission silently carries user-management or object-ownership power the grantor never intended.
Advisory table¶
| Plugin | GHSA / CVE | Boundary |
|---|---|---|
Botiga Pro < 1.6.5 | GHSA-vc27-9g27-3cvj | REST route with no authorization check at all → unauthenticated write of arbitrary WordPress options with arbitrary values (site-takeover path), stored web scripts rendered on every front-end page, and trashing of arbitrary posts — three sinks behind one missing permission_callback. |
Master Blocks < 1.5.0 | GHSA-4qrp-9fm8-gvg2 | Unauthenticated REST settings update; one saved value is output unescaped in wp-admin → stored XSS executing in an administrator's session (front-door input, back-office sink). |
Unbounce Landing Pages < 1.1.5 | GHSA-jqrj-3hxj-9rmf | Subscriber can update the front-end proxy's target host → arbitrary content served from the site's own origin (trusted-origin content relay / phishing pivot). |
Secure Custom Fields < 6.9.4 | GHSA-6w7q-74cq-4m3f | Front-end form submission never bound to the form rendered for that visitor → unauthenticated submit against any registered form → modify title/content of the post the form is bound to. Form identity is an unverified assumption. |
VikRentItems < 1.2.4 | GHSA-5j33-g9fw-4r4f | Unauthenticated SQL injection via unsanitised parameters. |
MgoSync < 2.1.7 | GHSA-3j79-gfcw-gr26 | Unauthenticated REST endpoint returns stored WooCommerce API credentials — read/write consumer key + secret. Integration tokens are the highest-value unauth disclosure in this wave. |
Mailchimp for WooCommerce < 6.1.1 | GHSA-qc48-4x35-wh3c | Several REST routes' permission callbacks missing → administrator-oriented endpoints reachable unauthenticated with persistent state change. |
Online Scheduling & Appointment Booking < 28.2 | GHSA-mj5r-8jcf-67xr | Unauthenticated AI booking-assistant conversation actions don't verify requester owns the conversation ID → read another visitor's assistant messages and inject messages into their in-progress conversation. |
UsersWP < 1.5.10 | GHSA-cg9c-x648-j6c7 | Social login resolves an existing account by asserted email without verifying the provider confirmed ownership → log in as any user (incl. admin) whose email you can register on a supported provider. Email-assertion ATO, same family as the Sept 18 reset-link-origin items. |
WP Import Export Lite < 3.9.35 | GHSA-839q-664c-x9c5 | Delegated plugin import permission never checked against create_users/promote_users → import can create administrators and overwrite existing credentials/roles. Delegated-capability ≠ implied-capability. |
Nimble Page Builder ≤ 3.3.8 | GHSA-4c39-75m8-74f3 | Authenticated (Subscriber+) AJAX action returns page-builder content for any post ID → disclosure of draft/pending/private/scheduled content. |
Ultra Addons for CF7 < 3.5.51 | GHSA-cq73-82v9-gx86 | Unauthenticated form-field upload with no type/extension validation, stored at a predictable public path with chosen extension. On Debian/Ubuntu Apache default handler config (.phar → PHP) this is RCE; elsewhere it's still a same-origin file host. |
JetFormBuilder < 3.6.5.3 | GHSA-mqwg-84ff-gqhv | Custom field-validation callback restricted by a function blocklist that omits a file-deletion function; form managers set arbitrary PHP callbacks, and deletion fires at form submit — unauthenticated. Blocklist omission + deferred execution by a different principal. |
Paid Memberships Pro (ProfilePress) ≤ 4.17.2 | GHSA-jvc3-5jgr-chw6 | Subscriber-level arbitrary shortcode execution (unvalidated value into do_shortcode) — adds to the Sept 19 03:32Z page's shortcode-primitive family. |
Custom Field Template ≤ 2.7.8 | GHSA-fv9m-mw9j-43cg | SQL injection via post_ID: current_user_can('edit_post', $id) integer-casts $id for the authz check while the unsanitised string reaches the SQL sink — a type-normalization differential between the authorization check and the sink. |
The Create ≤ 2.5.3 | GHSA-c79v-6v55-pvvh + GHSA-jhj7-pcmr-9q9p | Author-reachable REST route (publish_posts permission callback) with order_by/order SQL injection; the vulnerable branch only executes when filter params are present — trigger-precondition gating worth enumerating before concluding a query is unreachable. |
The Welcomizer ≤ 2.8.1 | GHSA-fmwg-rf74-rjqj | Nonce-only savesection AJAX handler with no current_user_can(), and the nonce is printed to any authenticated user by a directly reachable twiz-ajax.js.php; user-supplied "custom logic" hits eval() → Subscriber → RCE. Nonce ≠ authorization; a JS asset that boots WordPress to emit a nonce is an unauthenticated nonce mint for logged-in users. |
Wow Elements Addons ≤ 1.11.2 | GHSA-mw9j-687w-wh97 | Contributor-level SSRF: 'Changelog File' setting URL passed straight to wp_remote_get. |
UsersWP-style entity: Estatik < 4.3.5 | GHSA-vf6m-p9w2-rqf5 | Unauthenticated AJAX reflects values decoded from a request parameter → reflected XSS (decode-then-reflect, no encode at output). |
Ultimate Member < 2.13.1 | GHSA-w2mq-x7qr-mgcw | Profile-name value in page title: sanitizer runs before HTML-entity decoding, so entity-encoded payloads execute against every visitor incl. admins. Sanitizer-then-decode ordering is the same protect-after-use inversion as the Sept 19 WP Recipe Maker page. |
Why this is worth an operator page¶
- Enumerate every surface, then diff authorization. For one object type in a plugin (settings, posts, conversations, imports, forms), list transports: admin UI, AJAX actions (
wp_ajax_*, both priv and nopriv), REST routes (register_rest_route— check eachpermission_callback), front-end form handlers, cron/import jobs. Run the same state-changing operation via each transport as the lowest-privilege principal. Botiga Pro, Master Blocks, and Mailchimp-for-WooCommerce are all "primary UI gates, REST route doesn't." - Delegated capability ≠ implied capability (the import/export deputy). WP Import Export Lite shows the audit rule: when a plugin defines its own permission ("can run imports"), enumerate what objects the import format can create or modify (users, roles, passwords) and check the plugin re-validates each against core caps (
create_users,promote_users,edit_user). Any export→import pair (Ai1wm on the July 28 page is the same family) gets tested for the deputy jump: delegate-permission holder → admin creation. - Conversation/session IDs in unauthenticated actions are IDOR candidates by default. The AI booking-assistant item is the WordPress instance of the API-IDOR sweep already applied to plugins — the novelty is bidirectional: read AND inject into a live in-progress session, i.e., prompt-injection-into-another-user's-AI-agent as a bug class on scheduling/chat widgets. Sweep any
action=handler taking an ID with no nonce/ownership tie. - Email assertion in social login = ATO oracle. UsersWP's rule generalizes to every WordPress social-login plugin in scope: after provider auth, does the app trust the provider-reported email to resolve an existing account without an ownership challenge? Test in a lab with your own accounts on two providers pointed at a staging install — register the victim-lab-user's email on your provider account and attempt login. Never test against a third-party account.
- Type-normalization differentials between authz check and sink. Custom Field Template: the gate sees
(int)$id, the query sees the string. On any parameter feeding both acurrent_user_can(...)call and a query, probe with values whose integer cast and string form differ (1 OR ..., padded/whitespace forms) — same class as the WordPress-core casting items on the Sept 18 core page. - Handler-config as exploit amplifier. The CF7-addon upload becomes RCE only because Debian/Ubuntu Apache maps
.pharto PHP. Operator rule: predictable-path arbitrary-extension uploads are always a same-origin hosting primitive; the RCE claim requires proving the target's handler mapping (request the stored.phar/.phtmlback and observe parse vs serve behavior with an inert file). - Deferred-execution callbacks. JetFormBuilder: a privileged config action (setting a validation callback) is executed later by an unauthenticated form submit. When auditing form platforms, treat any "admin configures a callable" feature as a two-stage primitive: chainable with any social-engineering or stored-config vector, and its blast radius fires at attacker-chosen time.
Validation workflow (authorized scope only)¶
Lab installs, disposable objects
Disposable WordPress installs with the vulnerable plugin versions, synthetic users/roles, lab provider accounts for social-login tests, inert .phar/.php marker files (no shell code), and synthetic posts/conversations. Never overwrite real user credentials, never run credential-bearing REST dumps against production.
- Transport diff: for each plugin surface discovered, replay a state-changing operation as (a) anonymous, (b) Subscriber, (c) owner-user. Evidence: verdict table transport × role × result. Positive controls on own objects.
- Import deputy test: with a delegated-import-permission lab role, import a synthetic XML/CSV containing an admin user creation + a password change on a second lab admin account you own. Record success + fixed-version behavior. Delete synthetic users after.
- Conversation IDOR: two lab browsers, two synthetic conversations; browser B replays A's conversation-ID actions for read + inject. Evidence: message visible/injected in A's session with synthetic text only.
- Social-login ownership: staging install + two provider accounts you control; assert lab-user-B's email from provider account A; record login verdict.
- Cast-differential probe: submit
<numeric> <sql-canary>variants of any ID parameter paired with an authz check; compare authz verdict vs query behavior via benign time/error-free boolean canaries on your own rows. - Upload handler test: upload inert marker with
.phar/.phtml/.php.jpgvariants to predictable-path uploaders; record stored path, served-vs-parsed behavior (marker prints nothing; parse is detectable by response-type/no-exec), and delete immediately.
Tracked without publication¶
- Totolink A3002MU
formWlWdsbuffer overflow CVSS 10.0 (GHSA-gc8g-58xw-mmmf / CVE-2026-93741) — the same router product already tracked from the 03:32Z/06:32Z waves; no new operator axis beyond the known/boafrm/form-handlers class. - Datalogics Ecommerce Delivery subscriber order-create/cancel via stored logistics token + plugin-API-token overwrite (CVE-2026-9613, GHSA-2m92-vwcx-jmjg) — same missing-capability + stored-token-deputy axes covered here and on the July 28 page.
- YS LeadGen subscriber popup-stored-XSS + option exposure pair (CVE-2026-1256, GHSA-h43h-55xh-wjgc + GHSA-48p5-g4jp-2qq5), Ibtana arbitrary post-meta write via
iepa_builderkey, VikRentItems disclosure, LiteSpeed Cache/Redux/MC4WP/WP Customer Reviews/Real3D/WPComplete-class XSS singles, GoPay/WP Optimizer SQLi singles, Hydra Booking trio, Empik/TikTok/Flex Import/Metasync/BlockSpare/Search Atlas missing-auth singles, Rede Itaú, AppMySite, Business Name Generator multisite XSS (admin-only,unfiltered_html-disallowed framing), OTP Login multisite admin XSS, PDF Builder — sparse product-specific items, no new axis. - WordLift/SSL Zen/TikTok information-exposure/open-redirect singles; Microsoft Edge UAF local privilege escalation (GHSA-h9w4-646p-4c73) — local precondition, tracked per browser-item precedent.
Reporting checklist¶
- transport (AJAX action name / REST namespace+route / front-end form), auth context,
permission_callback/current_user_canpresence (absent vs wrong-cap vs wrong-target); - for deputy findings: the delegated cap name, the implied core caps crossed, and the created/modified synthetic object IDs;
- for IDOR: both directions tested (read/inject or read/write), ID source (visible in UI, enumerable), and nonce presence;
- for uploads: stored path pattern, extension accepted, target handler mapping evidence (served vs parsed), removal confirmation;
- for social-login ATO: which provider, whether email-verified flag was set by provider, and the resolve-by-email code path if readable;
- bounded impact language: arbitrary-option write ≠ RCE; same-origin file host ≠ code execution until handler parse is demonstrated; shortcode execution ≠ code execution without a dangerous registered handler.
Related pages: the Sept 19 03:32Z protective-transform-inverted page (sanitize-after-execute, hidden-field validate-skip, do_shortcode primitive), the July 28 WordPress payment/device page (export-secret deputy, nopriv upload axis), and the Sept 18 WordPress core page (transport-parity authorization drift at core level).
September 21 follow-up: sandbox-legitimacy confusion, nesting-defeated stripping, ungated export/import legs, and unscoped upload tokens (5 GHSAs + 1 sibling)¶
Five advisories published 2026-09-21 replay axes already on this page but each adds a distinct validation probe worth keeping:
| GHSA / CVE | Boundary | Drift |
|---|---|---|
| GHSA-p33j-7gxx-7v4f / CVE-2026-92400 | PayPal for WooCommerce IPN/payment notification | Handler verifies the notification is a genuine transaction but not that it was confirmed in the store's configured environment or paid to the store's own merchant account → an unauthenticated buyer marks their own order paid using a real transaction from a sandbox they control |
| GHSA-4grg-xfv8-q8cj / CVE-2026-85113 | GiveWP donor-supplied values on public pages | Shortcode delimiters are stripped from donor input before rendering, but the stripping is defeated by nesting ([sh[shortcode]ortcode]-style rebirth after one-pass removal) → unauthenticated execution of any registered shortcode |
| GHSA-p5g8-hwm5-93cc / CVE-2026-86802 | To Do List Member import routine | Import action has no authorization and no nonce, and the routine fetches data from a location the caller chooses → unauthenticated arbitrary published posts + taxonomy terms (plus the fetch-target axis = SSRF probe) |
| GHSA-xf2r-mj9x-vg5q / CVE-2026-85010 | RestroPress cart add/update | Client-supplied item add-on price is never revalidated server-side → order total is attacker-chosen, down to zero |
| GHSA-hcgg-qj5x-56wj / CVE-2026-82187 | Web to Print Online Designer uploads | Uploads are unvalidated for type/extension and the protecting token is handed to any visitor who asks → unauthenticated PHP upload → RCE |
New durable probes¶
- Payment-integrity: verify the legitimacy tuple, not just legitimacy. The IPN item generalizes far beyond PayPal: any webhook/notification flow should be tested for four separable checks — signature/genuine-transaction, environment (sandbox vs live), recipient merchant/account identity, and amount/currency/order binding. Bug-hunt move: complete a real $0/sandbox transaction against your own lab store with the plugin pointed at live mode and observe whether the order completes. When sandbox credentials are trivially obtainable (they are), the "genuine payment" precondition collapses.
- One-pass strip filters are defeated by nesting. Where a sanitizer removes a token pattern once (not to a fixpoint), reassembly recreates the token. Test every "delimiters are removed" claim with embedded/overlapping spellings before concluding the shortcode/XSS surface is closed. Sibling of the sanitize-then-decode differential already on this page — same ordering-class bug, different transform.
- Sibling-leg gating asymmetry again. To Do List Member's import leg has neither authz nor nonce while the rest of the plugin presumably does; this is the ScadaBR pattern from the same wave (its
/export_project.htmcreateExportJSONwas left unauthenticated while the matching import path already calledPermissions.ensureAdmin(), GHSA-w3gv-q645-g39f / CVE-2026-94148): when you find one gated action, immediately test its mirror (import↔export, upload↔download, create↔import). Export endpoints disclosing project/config content are the classic forgotten sibling. - Cart-price trust is order-integrity RCE-adjacent logic. Client-supplied unit/add-on prices accepted without server-side recompute remain a high-signal, low-noise ecom probe: one replayed cart request with
price=0answers it. - "Token protecting uploads" is only a control if issuance is gated. Web to Print hands its upload token to any visitor — treat every "capability token" as public material until you prove the issuance endpoint itself requires authorization. Combined with zero file validation this is straight unauth upload→RCE; keep the handler-parse proof standard from the CF7-addon item above.
Bounded validation¶
Lab installs of the named plugin versions; your own PayPal sandbox account for the environment-confusion test (never production merchant credentials); inert marker files with .php/.phar variants for the upload leg; nested-delimiter canary strings rendered to a page you own; cart replays against a lab store with synthetic products. Evidence = order-status transitions, rendered-marker presence, fetched-term IDs, stored-file paths; never real donor data or live payment material.
September 22 follow-up: render-vs-process authz parity, options-import ladders, and config-posture-gated POI (6 GHSAs)¶
The 2026-09-22 06:30Z WP wave replays this page's axes but contributes three genuinely new probes:
| GHSA / CVE | Boundary | Drift |
|---|---|---|
| GHSA-q84q-9hmp-p3qm / CVE-2026-13355 | Meta Box AIO ≤3.11.0 (Frontend Submission ≤4.5.6, User Profile ≤3.11.0) | Unauthenticated → Administrator chain in three links: populate_via_query_string() in the frontend-submission component unconditionally overrides the form's target object_id from GET rwmb_frontend_field_object_id with no authorization; Form::process() lacks the user_can_edit() check that render() has, so the submit path writes through the render path's absent gate → unauth wp_update_post() overwrites the post_content of any page with an arbitrary shortcode; the mb-user-profile component then directly trusts the role and auto_login attributes of the injected [mb_user_profile_register] shortcode → register-as-admin with auto-login. |
| GHSA-wchw-wpj2-8j67 / CVE-2026-12470 | CMP – Coming Soon & Maintenance ≤4.1.17 | Missing capability check on the cmp_ajax_import_settings AJAX action → editor-level callers update arbitrary options → flip default_role to administrator + enable users_can_register → self-admin. |
| GHSA-wq6q-gx9m-7669 / CVE-2026-19658 | Give Tributes ≤2.3.1 | Unauthenticated PHP object injection whose reachability is decided by plugin configuration: the single-recipient code path applies sanitize_textarea_field() and the eCard custom-message-required path has give_clean() blanking serialized input — the sink is only live when "Allow Multiple Recipients" is ON and the custom-message option is OFF (the default). No known POP chain in the plugin itself. |
| GHSA-gxqc-8737-rfmf / CVE-2026-88788 | Text Styler through 1.1.1 | Contributor+ styling values echoed into a front-end <style> block unsanitized, and no check that the user may edit the target post → stored JS executes for every viewer including admins (low-role stored-XSS + missing-ownership pair already catalogued on this page). |
| GHSA-73p4-x7g3-p8jm / CVE-2026-85653 | Contextual Related Posts ≤4.4.1 | Author+ stored XSS via the other_attributes block parameter — a generic "arbitrary extra HTML attributes" block setting with no attribute allow-list. |
| GHSA-f9mc-qc82-mqr7 / CVE-2026-93655 | Booking Calendar ≤11.8.3 | Unauthenticated reflected XSS via wpbc_auto_fill (single-axis item, listed for completeness). |
New durable probes¶
- Render-vs-process authorization parity. Meta Box checks edit permission when displaying a form but not when processing it. Treat every render/submit handler pair as two independent gates: find the capability check in the render path, then call the process endpoint directly without ever rendering. Sweep
process|submit|handle|saveroutines paired with any checkedrender|displayroutine across all form-capable plugins on the target. - Caller-target override parameters. A form bound to a target object must re-verify the submitter's authority over that object, not just over the form. Any
..._object_id,post_id,target_id,entity_idGET/POST parameter that selects what gets written is a horizontal-to-vertical pivot: swap your own ID for a target page's ID and watch whether the write follows. - Injected content is later parsed as trusted configuration. On WordPress, post content is a config language (shortcodes carry
role=,auto_login=, prices, callbacks). Once you hold an arbitrary content-write primitive — however low-severity it looks standalone — enumerate registered shortcodes for privilege-bearing or fetching attributes, because the write + config-parser pair chains to admin. Report arbitrary-content-write findings with the concrete downstream shortcode-attribute chain, not as "content overwrite." - Options-update endpoints are privilege-escalation routes. Any import/settings-save AJAX that reaches
update_option()with only a nonce present but no capability check is a two-request admin chain (default_role+users_can_register). Grep plugin source for option-name arrays fed to settings-import handlers and test each action at the lowest authenticated role with a valid nonce (public pages often mint nonces for anyone). - Configuration posture is part of the exploitability proof. Give Tributes is the third consecutive same-week exemplar (after Dancer2's
static_handler: 0precondition and NivoCart's chunk-count asymmetry): sibling code paths carry the sanitizers, and a settings toggle decides which path runs your input. When probing deserialization/upload/shortcode sinks, enumerate every plugin option combination that changes the code path before declaring the sink unreachable — and record which toggle state you proved in the report. - POI without a known POP chain is a conditional finding. Keep object-injection reports as chain-conditional: impact depends on third-party plugins/themes present on the specific target. Validate by proving deserialization occurs (timing/PHP-error/state side effect on an inert class), then state the chain precondition explicitly rather than inflating severity.
Bounded validation (September 22 items)¶
Lab installs at the named versions with a second disposable user as the write-target owner; prove the Meta Box chain against a page you created in the lab and stop at admin-session establishment; prove the CMP import leg with synthetic option payloads on a scratch install (snapshot the options table before/after); prove Give Tributes deserialization with an inert class name and no POP payload; contributor-level marker strings rendered to your own post. Evidence = post_content diffs, option-table deltas, new user rows with role, rendered marker presence; never live donation data or production option tables.
September 22 second-wave follow-up: admin-viewer sinks in submission reviewers, integration-credential write endpoints, and validator-shape gaps (Sept 22 09:31Z wave, 17 GHSAs folded)¶
The 2026-09-22 09:31Z WP-centric wave repeats this page's transport-drift axes but contributes three genuinely new primitive families plus a set of one-line probes.
| GHSA / CVE | Boundary | Drift |
|---|---|---|
| GHSA-x4cv-hqr7-5rcm / CVE-2026-92438, GHSA-9248-28p4-jpfp / CVE-2026-94504, GHSA-jvgp-rj42-qjww / CVE-2026-91827 | Ninja Forms 3.15.3 (three GHSAs) | Anonymous public-form submissions are stored raw and re-rendered unescaped in the admin submission editor → stored XSS executing in the WordPress admin origin when an admin opens the submission (attacker knows the direct submission URL); the same unsanitized values are deserialized during admin CSV export → unauthenticated PHP Object Injection whose trigger sink is an export function. One input field, three sinks: front-end render, admin viewer, export parser. |
| GHSA-h8jc-c8c9-624r / CVE-2026-89412 | TranslatePress ≤3.3.5 | Front-end pipeline runs html_entity_decode() on input before persistence, and the original translation-memory column is deliberately exempt from kses → unauthenticated stored payload that no save-time sanitizer sees, rendered via v-html into the administrator's suggestion panel. |
| GHSA-9596-j6mw-33fx / CVE-2026-18345 | WP User Manager ≤2.9.18 | Connect::complete() hooked to admin_init — which fires for every authenticated user including Subscribers — fetches Stripe credentials from the caller's own WPUM Connect OAuth completion and writes them into wpum_settings with no current_user_can()/nonce → Subscriber swaps the site's Stripe keys for their own → all subsequent payments route to the attacker. |
| GHSA-rqq6-mm6q-4qqx / CVE-2025-14487 | Handily ≤1.0.3 | Unauthenticated modification of Stripe config (publishable/secret keys, success/cancel URLs) via payment-settings parameters → payment redirection to attacker's Stripe account. |
| GHSA-m3qm-2932-58gx / CVE-2025-14484, GHSA-jrhq-q5jc-pgv2 / CVE-2025-14486 | Image Buzz ≤1.0.3, PixelPlay ≤1.0.2 | Unauthenticated write/delete of administrator-configured third-party API keys (Pixabay/Unsplash/Pixels/OpenAI) via pixabay_api / clear_api_type parameters — integration-credential fields are just options, and options endpoints forget they hold credentials. |
| GHSA-m39c-xj94-687f / CVE-2026-93556 | /password/guardarClau/recover endpoint | Recovery JWT is verified for signature but not bound to the usuariId parameter → any valid-format token resets any account's password, including admin. |
| GHSA-vq82-hcfx-mf57 / CVE-2026-9231, GHSA-chrw-gf4w-fmg3 / CVE-2026-92969 | WP Travel Engine ≤6.8.0 (wte_get_template), HUSKY ≤1.4.4 (shortcode param) | Template-include LFI (contributor+), and unauthenticated LFI where the only gate is a nonce publicly emitted in inline front-end JS — the public-nonce-is-not-authorization axis again (The Welcomizer precedent above). |
| GHSA-mmq2-8hq5-2c6q / CVE-2026-15095 | CTX Feed ≤6.6.43 | Two-step deferred destructive traversal: REST call #1 persists the traversal payload into wp_options via save_feed_config; REST call #2 delete_feed performs the unlink() — the path validator is skipped on the second leg; deletion constrained to a feed-extension whitelist (csv/xml/tsv/xls/xlsx/json/txt). |
| GHSA-97h8-3g49-xmvg / CVE-2025-1280, GHSA-c2w6-7j98-2vqx / CVE-2025-1281 | BM Content Builder <3.17.1 | Subscriber-reachable AJAX pair: arbitrary file read (save_layout) and arbitrary file delete (remove_layout/export) — deleting wp-config.php re-opens the installer → RCE ladder from file-delete. |
| GHSA-m435-6crg-xq92 / CVE-2026-93836 | WPC Product Bundles ≤8.6.6 | qty validated by float cast — '1<img src=x onerror=…>' passes the numeric check (cast reads the prefix) while the full string is stored verbatim in _woosb_ids order meta → unauthenticated stored XSS. |
| GHSA-v8f2-9v32-fmmf / CVE-2026-6922 | WP Table Builder ≤2.2.1 | Operator-precedence bug makes a post-type authorization guard never fire (&&/|| grouping), plus a permission callback that checks plugin-role membership with no per-object check → subscriber trashes/restores any post on the site. |
| GHSA-hv57-w5f4-r2xr / CVE-2026-16778 | Live Composer ≤2.1.21 | Save-time wp_kses_post() is defeated because the payload is stored as a serialized string with no literal HTML tags for kses to tokenize; the shortcode callback then emits view_all_link/main_heading_title/button_inline_svg unescaped at render → contributor stored XSS. |
| GHSA-9x9f-f428-ff8p / CVE-2026-18439 | Tutor LMS ≤4.0.7 | Quiz-builder save handler validates only the top-level course_id/topic_id; nested question_id/answer_id/deleted_*_ids[] go straight into $wpdb->update/DELETE → instructor overwrites/reparents/deletes other instructors' questions. Parent-checked, children-trusted. |
| GHSA-45jh-6cpx-5764 / CVE-2026-93778 | WP Yelp Review Slider ≤9.2 | Payload arrives from an anonymous reviewer on a public third-party platform, ingested during the admin's routine "Download Reviews" click — effective attacker is unauthenticated with zero site interaction. |
New durable probes¶
- The admin review UI is an XSS sink; the admin export is a deserialization sink. Ninja Forms turns every public form field into a latent admin-session payload (and a latent
unserialize()input on CSV export). Bug-hunt rule: on any form/submission platform, submit inert HTML-structural canaries (<img src=x>,"><svg) to every non-sanitized field, then check the admin viewer and the CSV/JSON export render of your own submission. The admin-origin execution claim needs the viewer URL and the admin-open event proven, not assumed. - Integration-credential parameters are options with extra blast radius. Stripe keys, Pixabay/Unsplash/Pixels/OpenAI keys, success/cancel URLs appear in payment/API settings endpoints that were built as ordinary settings writers. Sweep every AJAX/REST action whose parameter names match
[a-z]+_api|api_key|publishable_key|secret_key|webhookand attempt write/delete as the lowest-privileged principal (unauthenticated here for Handily/Image Buzz/PixelPlay). Payment-redirect (attacker Stripe key) beats DoS (deleted key) as a report. admin_init-hooked handlers are authenticated-user attack surface.admin_initfires for Subscribers hitting any/wp-admin/page. When auditing, grep foradd_action('admin_init'and read every callback for state writes withoutcurrent_user_can()— WPUserManager's Stripe-connect writer is the exemplar.- Token-vs-target binding on recovery endpoints. A recovery token validated only for signature lets the identifier parameter select the victim. On any password-reset/recovery flow, obtain a token for your own account, then replay it with the user-ID parameter swapped — the check you're testing is "is this token for that user," which is distinct from "is this token valid." Fourth sibling of the token-link authority family (Origin-header/Host-header/origin-parameter waves).
- Deferred-validator two-step writes. CTX Feed: persistence and consumption are two REST calls and only the first is validated. Where a settings-save endpoint stores a path/URL/ID and a different action later consumes it, test the pair: store your own marker payload via call 1 (always your own data), trigger via call 2, and observe whether call 2 revalidates. Same pattern as the config-write ladders page's ZLMediaKit item, WordPress-shaped; the extension whitelist on the delete leg is itself recon (it tells you which files the sink will touch).
- Numeric-cast validators keep the string tail.
(float)-validated parameters accept1<img …>because the cast succeeds on the prefix while the original string persists. Any "validated as number/float/int" parameter should be probed with numeric-prefixed markup; compare the stored bytes against the cast value. - Serialized storage defeats tag-scanning sanitizers.
wp_kses-style filters tokenize real HTML; payloads hidden inside serialized/json-encoded values sail through and unescape later. On page builders and settings blobs, submit payloads in encoded/serialized shapes and check the render leg independently of the save leg. - Boolean-precedence guards can be dead code. An authorization
ifwith mixed&&/||may never trigger. Never conclude a guard works from its presence — always run the negative control (role that should be denied, own object, then foreign object). - Check the children, not just the parent. Nested-ID payloads (Tutor LMS) cross object ownership whenever only the outermost ID is authorization-checked. On any tree-shaped save endpoint (course→topic→quiz→question, order→item→addon), submit your parent's ID with a foreign child ID inside the payload.
- Imported third-party content is an unauthenticated attacker channel. Yelp reviews, feeds, oEmbed, media metadata: content ingested from public platforms often bypasses the site's own input controls because the site owner clicked "download." Treat every platform-ingest feature as unauth stored-content injection into the site's renderers.
Bounded validation (September 22 second-wave items)¶
Lab installs at named versions; submit only inert structural canaries (<img src=x>, no script payloads) to your own form entries and open the admin editor on your own session; prove the Stripe axes with your own test-mode Stripe account keys on a lab store, never production merchant credentials; prove recovery-token binding between two lab accounts you own; prove traversal/read/delete legs against files you created in a disposable directory outside WordPress core paths (never wp-config.php on anything you use); nested-ID tests use two lab instructors' own synthetic quizzes. Evidence = rendered-marker presence in admin DOM, key-value diffs in the options table, payment-routing observation in Stripe test mode, verdict tables per validator probe; never live submissions from real users, real API keys, or real payment material.
September 22 third-wave follow-up: template-include sinks, shortcode execution surfaces, and email-knowledge identity (Sept 22 12:30Z WP singles, folded)¶
Five singles from the 12:30Z wave add reusable axes:
- Template-name parameters are file sinks. WP Travel Engine ≤6.8.0 contributor→LFI via
wte_get_template(CVE-2026-9231): a low-role user supplies a template identifier that is included as a PHP path — contributor + any leg that stores a.phpyou can name (upload, log write, other plugin) = code execution. Audit rule: everyget_template/locate_template-shaped helper whose name/path argument reaches the caller's request is an LFI candidate; probe with your own uploaded marker file and denied-include recorders, never core paths. - Shortcodes are a capability surface. WP Ultimate Review ≤2.4.2 subscriber→arbitrary shortcode execution (CVE-2026-92235): an unvalidated value reaches
do_shortcode. Shortcodes registered by any active plugin are executable primitives (queries, links, sometimes writes); on any subscriber-reachable endpoint that renders user data throughdo_shortcode, enumerate installed shortcode tags and test each as the value. Prove with inert output-only shortcodes first. - Email-knowledge is not authorization. wpForo ≤3.1.5 subscriber takes over a guest author's post — title, body, author name, and stored owner email — by knowing the guest's email address (CVE-2026-91092), when guest posting/editing is enabled. Guest-identity flows that key on an email string rather than a issued token are claimable by any authenticated user who can guess or observe the address. Test matrix: authenticated low-role user + your own guest post + a second lab identity's guest post; check the preconditions (guest posting on) as part of exploitability proof.
- Form-field parameters that carry object IDs cross ownership. Custom Field Template ≤2.7.8 contributor deletes arbitrary site-wide media attachments by supplying any numeric attachment ID in a file-field parameter with the delete flag (CVE-2026-12995) — precondition: your post's template has a file field with
mediaRemoveoff (the default). Sibling of the nested-ID axis with a twist: the ID rides inside a field value, so per-field validators never see it as an object reference. Enumerate field-shaped parameters (file,attachment,media) for numeric-ID acceptance with denied-delete recorders against your own synthetic attachments. - Numeric ID enumeration = CRM PII oracle. WP-CRM System ≤3.4.6 contributor extracts full names/emails/phones/addresses of arbitrary contacts by enumerating
contact_id(CVE-2026-9004): the record-response sweep on the WP page applied to CRM post types; report as read-only high-PII, prove with two synthetic contacts you created. - Subscriber-level
wp_ajax_toggle handlers without capability+nonce checks recur (ThumbPress deactivation CVE-2026-7622, RW Elephant cache toggle CVE-2026-4123) — sameadmin_init/AJAX axis already canonical above; each such handler is a small integrity primitive worth chaining, and deactivation handlers are notable because turning a security/sanitizing plugin off is a precondition-setter for other findings.
September 22 fourth-wave follow-up: marketplace-plugin IDOR quads — every AJAX action is a tenant-boundary test (MarketKing ≤2.1.72, four GHSAs folded)¶
MarketKing (WordPress marketplace/WooCommerce multi-vendor plugin) shipped four missing-authorization findings in one release, all the same shape applied to four different verbs — a template for auditing any multi-vendor/multi-tenant WP plugin (CVE-2026-93344 / GHSA-mr7p-65vm-9479, CVE-2026-93343 / GHSA-494w-928f-793q, CVE-2026-93342 / GHSA-xxjc-pqh8-8mgh, CVE-2026-93341 / GHSA-j68h-v872-356w):
marketking_get_page_content: subscriber supplies any vendor user ID → renders arbitrary vendor admin-panel pages (payouts, financial reports, dashboards) — read.marketking_admin_vendors_ajax: no capability check → complete vendor directory dump (internal PII) — read.marketking_duplicate_product: supply any product ID → duplicate any vendor's product, private metadata included, into your own vendor account — copy/exfil that persists.marketking_send_refund: supply any order ID → file refund requests against any customer's order — write with money attached.
Durable axes:
- Marketplace = tenancy on top of WordPress, and WP core capabilities don't know about it. The plugin's vendor-admin surface authenticates via WP (subscriber = logged in) but authorization should be vendor-scoped; when the scoping check is missing, any Subscriber traverses every vendor. On any multi-vendor plugin (MarketKing, Dokan, WCFM, WC Vendors, WC Marketplace), enumerate its
wp_ajax_/REST actions and test each with (a) unauthenticated, (b) Subscriber, (c) Vendor A acting on Vendor B's IDs — the (c) pair is the finding even when (a)/(b) 403. - Read/copy/write ladder within one object family. Same advisory wave covering view-payouts, dump-directory, copy-product, refund-any-order means the missing check is likely a pattern (action dispatch without a shared
verify_vendor_owns()), not four bugs — once one IDOR proves, sweep every action that takes an ID parameter in that plugin class before reporting; expect the write verbs to be as open as the read verbs, and lead the report with the money-moving one (refund forgery > directory dump for severity). - The duplicate/copy primitive is underrated exfil. Duplicating a foreign object into your own account bypasses "read-only" reporting ceilings: the copy is yours to view, export, and keep. When a platform offers duplicate/clone/import actions, test them against foreign IDs — object-copy handlers routinely re-check nothing because "you're just copying."
Bounded validation: two vendor accounts you own in a lab store with synthetic products/orders/payout rows; prove traversal between your own vendors only, cancel any synthetic refund immediately, never touch real store data or customer orders.
September 23 follow-up: SSO assertion misbinding, fix-history regressions, and remote-cache file sinks (Sept 23 06:31Z WP wave, folded)¶
Seven items from the Sept 23 06:31Z WP wave add new axes; payment-gateway integrity items from the same wave are on the dedicated Sept 23 payment-cluster page.
- Issued-identity misbinding — SSO servers leak the last authenticator. WP OAuth Server (Login with WordPress) <6.4.0 does not bind the OpenID Connect identity assertion it issues to the authorization grant being exchanged; it returns the assertion belonging to whichever user authenticated most recently (CVE-2026-82843 / GHSA-rwfr-37x7-2rr8). A Subscriber can obtain a validly signed assertion for an administrator and authenticate as them at every application that uses the site for SSO — one plugin bug delegates admin authority to the whole connected-app fleet. Audit rule: on any self-hosted IdP/OAuth/OIDC plugin, run a two-browser, two-account interleaved grant battery (A authenticates, B completes a grant, check B's id_token claims for A's identity; repeat with timing variants) — "most-recent" global state instead of per-grant state is the bug shape, and the blast radius multiplier ("any application that uses the site for SSO") belongs in the report headline. Prove with two lab accounts and a lab relying party you control; capture claim diffs, not live tokens.
- Fix history is a recon map: re-test old CVEs and the regression window. The Directorist: AI-Powered Business Directory wave (6 GHSAs, <8.9.5) includes CVE-2026-84098 / GHSA-7mc4-vjfr-47h6 — a listing-deletion authz bypass that is an incomplete fix of CVE-2023-1889 / CVE-2023-35052 (a second, unaddressed deletion path re-enables the identical impact) — and CVE-2026-77766 / GHSA-rc7w-56jp-5gfx, where a REST collection was correctly scoped in 8.8.1 and the unscoped behavior was reintroduced in 8.9.1 (versions 8.8.1–8.9 unaffected). Durable probes: (1) for any plugin with historical CVEs, re-run the old PoC on current versions — fixes patch one path, and sibling entry points (AJAX vs REST vs admin-post) are the classic second leg; (2) for any finding, bisect affected-version ranges — a fix that later regressed means both "current is vulnerable" and "specific old range is safe," which changes exploitability claims and vendor triage. The rest of the Directorist wave repeats this page's canonical axes (subscriber SSRF via unvalidated listing URL 84046, unauth REST user-contact dump 84026, favorites route trusts target-user param 84150, order creation with arbitrary amounts attributed to other users 84027) — treat as corroboration sweeps, not new rules.
- Obscurity plugins leak the thing they hide. Easy Hide Login <1.7 does not enforce its hidden-login protection on password-reset request paths: the standard
wp-login.phpflow stays reachable through reset parameters, and the returned page reflects the site's configured secret login slug (CVE-2026-84168 / GHSA-rx89-27rf-3jpj). Recon heuristic: when a target uses login-URL obfuscation (WPS Hide Login, Easy Hide Login, Rename wp-login), fuzz auxiliary flows that must stay public by design —lostpassword,register,resetpass, feed/REST auth endpoints — hidden paths are enforced on the main route, not the satellites. A leaked slug is low-severity but it converts your recon into the precondition map for every other login-surface finding. - Remote-file cache features are unauthenticated file-write sinks. YAHMAN Add-ons <0.9.31 caches remote files it fetches into a publicly accessible directory without validating the file type, giving unauthenticated attackers arbitrary PHP write → RCE when the feature is enabled (CVE-2026-75799 / GHSA-ppcx-fj6x-46xf). Audit rule: every "import from URL / fetch external asset / sync remote file" handler is a write to web root candidate — check the saved filename/extension handling (does the cache keep the remote extension? sanitize?) before checking the fetch itself; the fetch-then-store leg is where the sink lives, and an SSRF report that ignores the write leg under-reports the same bug. Proof: point the handler at your own HTTP server serving a
.php-named inert marker; verify storage path and public reachability, then delete. - Unauthenticated mail-send handlers carry site-trusted templates. Forminator Forms <1.57.2.1's saved-draft notification binds neither recipient nor link to the session: both the address and the link inside the message come from the request, the authorization token is handed to the anonymous caller by the plugin, and it is unlimited-replay (CVE-2026-87074 / GHSA-4hc7-hvm3-f45g) → any visitor can make the site send its own branded template with an attacker-chosen link to any address (phishing with perfect sender reputation). Hunt shape: for every "email me this / share / notify about draft" AJAX handler, grab the issued token from the page and replay the send with foreign recipient + foreign link. The Sept 18 extract-before-verify page covers the sibling where the link authority (Origin/Host/parameter) is the flaw; here the flaw is recipient/link injection into a site-signed message.
- Arbitrary post-meta write = cross-object primitive. WC Fields Factory <4.1.11's field-management AJAX action lets Subscribers create/modify/delete arbitrary post meta on any post regardless of ownership, including WooCommerce product pricing rules → price reduction at checkout (CVE-2026-93508 / GHSA-fmf3-2pcf-wpmg); the same plugin's post-cloning action (contributor+, no nonce) copies any private/draft post of any type into the attacker's readable view (CVE-2026-93507 / GHSA-225c-7qjr-pm2w).
post_metais the WordPress EAV store that permission layers forget: meta keys gate pricing, roles, template choices, and plugin state. Sweep any authenticated meta-writer parameter formeta_key/_-prefixed key acceptance with foreignpost_id; the clone/dupe leg is the copy-exfil axis already canonical on this page (MarketKing) applied to core posts. - Booking Manager <2.1.21 SQLi via imported external iCalendar feed (CVE-2026-91024 / GHSA-8743-f7m5-qxwx): author-level imports a feed the attacker controls and feed field values go into SQL — third exemplar of this page's "imported third-party content is an unauthenticated attacker channel" rule, with the twist that the importer role is the attacker's foothold and the feed source is fully external; pair with the same plugin's settings-target IDOR (91025).
Bounded validation additions: two-account interleaved grant tests on a lab IdP install with a lab relying party; YAHMAN-style fetch proofs use your own HTTP server serving marker files with suspicious extensions inside a disposable cache dir you own; mail-send proofs go to mailboxes you own; meta-write and clone proofs use synthetic posts/products in a lab store, canceling any synthetic checkout immediately.
September 23 late-morning follow-up: core-hook ordering SQLi and eval() in block frontends (Sept 23 09:30Z WP wave, folded)¶
- Login-page plugins inherit WordPress core's preprocessing — and their own SQL doesn't. Rename wp-login.php to anything you want ≤2.0.1 has time-based SQL injection via the
log(username) parameter (CVE-2026-93368 / GHSA-4rj9-f57h-53hg, high, unauthenticated): the plugin'swp_login_failedhandler builds SQL from the username, and WordPress core applieswp_unslash()to$_POST['log']before dispatching the action, stripping magic-quotes escaping so a raw single quote reaches the handler unimpeded. Durable axes: (1) the auth-failure path is an unauthenticated SQL sink candidate — every login-page customization plugin (rename/obscure/CAPTCHA/2FA) hookswp_login_failed/authenticateand writes attempt data; fuzz the username field with quote canaries on the failed-login path even when the login query itself is prepared; (2) core's normalization is part of your input contract — a handler that expects slashed input (the legacy WP convention) receives unslashed input from core hooks, an ordering differential that flips sanitizer verdicts; check what core already transformed before trusting the plugin's escaping story. Prove time-based blind with benignSLEEP()timing against your own lab install only; never run sleep chains against production. - Block-editor frontends get eval() where admin-side validation never runs. Getwid – Gutenberg Blocks ≤2.1.3 has stored XSS via the Google Maps block's
customStyleattribute — the front-endmapStyles()JavaScript callseval()on user-controlled block content (CVE-2026-5924 / GHSA-pcjw-3vmf-w7jr, contributor+). Contributor-to-XSS-on-public-pages is this page's front-door-input/back-office-sink shape reversed; the sharp general rule: gutenberg block render callbacks that ship front-end JS sometimeseval()/new Function()attribute-derived style/JSON strings that the server-side sanitizer treated as inert data. For any block-based site, enumerate block attributes reaching front-end JS (style,json,config-shaped attrs) and test whether any parser iseval()— proof = a benign object-literal marker changing rendered map/visual state, not script theft. - Shortcode read-path drift again. Advanced Contact form 7 DB ≤2.0.9:
acf7dbshortcode renders all Contact Form 7 submission data to any Contributor (CVE-2026-6831 / GHSA-8v4v-v89h-r3q8) — collected form submissions (PII) sit in a table the plugin exposes through a shortcode with no capability check; sweep every plugin that stores submissions for shortcode/view renderers gated only by "user can create a post." Same wave's remaining WP singles (subscription/XSS/enum items) tracked without publication.
September 23 late-wave follow-up: GraphQL mutation authz reimplemented worse than REST — collection-level checks and sibling-resolver drift (WPGraphQL, folded)¶
- WPGraphQL 2.19.0
updatePostauthorization bypass: Contributor publishes withoutpublish_postsand edits own published posts withoutedit_published_posts(CVE-2026-88974 / GHSA-5mmc-8pc9-wggg, medium but high-signal): the mutation checks only the post type's collection-level capability (current_user_can( $post_type_object->cap->edit_posts )) and never performs WordPress's object-level check (edit_post,post_id), nor apublish_postscheck when the requested status ispublish— status passes straight through towp_update_post(), which trusts that its caller already authorized. The plugin's owncreatePostresolver does the publish-capability check and downgrades unauthorized status topending— the guard exists in the sibling code path and was not reimplemented in the second one. The WordPress REST API correctly rejects the identical operations for the same user, which the advisory itself cites. - Durable axes: (1) collection-level vs object-level capability checks —
edit_posts("may edit posts of this type") is notedit_post("may edit this post"); any authorization code that only references thecap->edit_posts-shaped properties is the shape of this bug. (2) Sibling-resolver parity diff: whenever a GraphQL API exposes create/update/delete on the same object, diff their authorization code — WPGraphQL's create was correct and update was not; audit both directions (a check present in one resolver proves the semantic requirement exists, so its absence in the sibling is a bug, not a design choice). (3) REST-vs-GraphQL parity battery: on any WordPress site with GraphQL enabled, run every capability-sensitive operation twice — RESTwp/v2and GraphQL mutation — with a Contributor/Subscriber token; a REST 403 + GraphQL 200 differential is the finding, and the differential harness doubles as your negative control (same input, two routers, two verdicts). - Bounded validation: lab install with two contributor accounts you own and synthetic draft/publish posts; prove the status transition and foreign-object edit on your own objects first, then against a second lab account's post. Report the resolver-level code citation (collection vs object check) plus the REST/GraphQL verdict table.
October 1 follow-up: signed click-tracking URL mints the subscriber's permanent raw auth cookie (The Newsletter ≤9.3.9, folded)¶
- The Newsletter – Send awesome emails ≤9.3.9 unauthenticated permanent-cookie theft via public click-tracking route (GHSA-r57r-xx5c-pcg3 / CVE-2026-92537): the public REST route
/tnp/l/is registeredpermission_callback => '__return_true'; on any validly signed click-tracking URL it callsset_user_cookie(), which emitsSet-Cookie: newsletter=<id>-<raw_token>to whoever hit the link. The masking that should protect the credential is state-dependent: the subscriber object loaded viaget_user()lacks the_trustedproperty on this path, soget_user_key()returns the raw token column instead of its MD5-masked variant. Any holder of one signed tracking URL (embedded in every external link of every newsletter, forwarded/screenshot/scraped by link-preview and URL-scanner services) receives the subscriber's permanent raw authentication cookie, then exports full PII (?na=px), rewrites the stored profile (?na=ps), and silently unsubscribes via the RFC-8058 one-click endpoint (?na=ocu) — none require a nonce, password, or email challenge. - Durable axes: (1) a signed URL is not just a redirect grant — sweep what the redirect endpoint emits back. Every
permission_callback => '__return_true'route that "just validates a signature and forwards" must be checked forSet-Cookie/session-mint side effects; the signature proves the URL, not that the response is safe to hand to whoever holds it. Newsletter/email click-tracking endpoints are high-value because their URLs are mass-distributed by design and pre-fetched by link scanners. (2) Credential masking is a per-code-path property, not a per-field one. The sameget_user_key()returns masked material on the normal login path and raw material on a loader path that skips the_trustedflag — when auditing any token/cookie emission, trace the object loader used by that path and compare what masking conditionals gate on; hunt the sibling loaders that don't set the flag. (3) Cookie-steering probe for WP newsletter/marketing plugins (MailPoet, Newsletter, Email Octopuses shapes): submit a signed or unsigned tracking hit withcurl -vand inspect response headers for identity-bearingSet-Cookiebefore concluding the route is a benign redirector. - Bounded validation: lab install with your own subscriber record; generate your own newsletter, hit your own tracking URL with a header-logging client, confirm the cookie equals the raw token column in the DB you dumped from your own lab, replay
?na=pxagainst your own subscriber only, then delete the synthetic subscriber. Never collect real subscribers' tokens or open real user profiles.
October 1 late-morning follow-up: registration mass-assignment, self-target capability collapse, and no-role-check login-as (Oct 1 10:30Z WP wave, folded)¶
- Super Forms ≤6.3.316 unauthenticated Administrator registration via
role=mass assignment (CVE-2026-15989 / GHSA-mjx4-cmxj-rrrw, critical): the Register & Login add-on'sbefore_email_success_msg()whitelists the client-submittedrolekey and copies it into the user-data array passed towp_insert_user()— no validation against the admin-configuredregister_user_role, no allow-list, nocurrent_user_can(). Injectrole=administratorinto the data submitted to any published registration form → Administrator account, no credentials needed. Same plugin, second bug: thesuper_save_formAJAX handler has no capability check (Subscriber can create/modify forms and flip settings), andsuper_submit_formpasses attacker-controlledfiles[].subdirintoSUPER_Common::delete_dir(); theABSPATHcontainment guard dies to a trailing-slash mismatch —subdir=wp-config.phpmakesdirname(realpath(ABSPATH.$subdir))equal the WordPress root while the naiveABSPATH !== $dirstring compare passes → Subscriber-driven recursive arbitrary file/directory deletion, site takedown, and reinstall-path RCE. - WPC Shop as a Customer for WooCommerce ≤2.0.0 login-as-admin with no password (CVE-2026-95687 / GHSA-wcrr-q656-8gwf, high): the
wpcsa_loginendpoint issues a fresh authentication session for any user ID supplied directly in the request without validating the caller's authority over the target role — supply an Administrator's user ID, receive a full Administrator session cookie. - ByteCoreStack MCP Connector ≤1.2.3 Subscriber→Administrator through its own MCP JSON-RPC tool (CVE-2026-19807 / GHSA-qwvr-5fhh-47xh, high):
wp_update_user_metagates writes withcurrent_user_can('edit_user', $uid)— which WordPress core'smap_meta_capdowngrades to thereadprimitive when$uidequals the caller's own ID — while the meta-key blocklist covers onlyuser_pass,user_activation_key,session_tokens. A Subscriber calls the MCP tool withkey=wp_capabilities,value={'administrator':true}, own user ID → loads as Administrator next request. - Autoptimize ≤3.1.15.1 stored XSS via REQUEST_URI path (CVE-2026-14995 / GHSA-v9f4-5xpx-364q, high, unauthenticated): the Critical CSS feature queues the request URI into cache entries rendered on the front end; precondition is Critical CSS active with a valid API key — a feature-flag precondition gates the vulnerable path, enumerate optional features before concluding an input never reaches output.
- Durable axes: (1) Registration-form payloads are mass-assignment surfaces — sweep every field name a registration/checkout handler echoes into
wp_insert_user()/wp_update_user();role=,role[]=administratorandwp_user_level=10belong in the canary set, and the fix is always an allow-list of accepted keys, not a denylist. (2) Self-target capability collapse:current_user_can('edit_user', get_current_user_id())is nearly always true because core maps self-edit toread— any tool that lets a user edit themselves must additionally denylist (better: allowlist) the meta keys it accepts, andwp_capabilities/wp_user_level/wp_user_settingsare the privilege keys to probe; blocklist-shaped defenses get swept for the omitted key (this wave's blocklist omitted exactly the two that matter). (3) "Login as" / impersonation endpoints (customer-service, support, agency plugins) are role-confusion gold: test whether the endpoint validates authority over the target, not just caller authentication, by requesting a session for a second lab account's ID. (4) Trailing-slash guard differential (realpath+dirnamevs the constant's trailing/) is the prefix-containment family again — on every path guard, test the bare child of the root (subdir=wp-config.phpshape) wheredirname()collapses onto the root itself. (5) AI-connector plugins are WP privilege surfaces: a WordPress site exposing an MCP/LLM tool bridge doubles the plugin's attack surface with a machine-driven caller that never sees UI prompts — enumerate/mcp-style JSON-RPC endpoints and run the same role/meta canaries there. - Bounded validation: lab install, your own two accounts (Subscriber + disposable Administrator), synthetic forms; prove the
role=injection on your own registration form, the self-meta write on your own user row, and login-as against your own second account; deletion proofs only against marker files inside a scratch directory, neverwp-config.phpeven in a lab that matters.
October 1 12:31Z follow-up: publicly minted nonnes defeat the "nonce = auth" assumption (Forminator, folded)¶
- Forminator Forms ≤1.57.2 unauthenticated stored XSS via freely-mintable AJAX nonce (CVE-2026-92144 / GHSA-pg8f-q234-6933, high): stored XSS through the
postdata-1[post-custom]parameter; the form-submission nonce the handler requires is handed out by the publicly accessiblewp_ajax_nopriv_forminator_get_nonceendpoint — so the whole chain runs with zero authentication and zero account. Durable axis: a nonce fetched from anoprivaction is not an authorization signal — when auditing any WP form plugin, grep its handler registration forwp_ajax_nopriv_endpoints that issue nonces/tokens the submit handler then demands; that pairing collapses the CSRF control into a two-request formality for unauthenticated attackers. Sweep canary:action=<plugin>_get_nonceposted to/wp-admin/admin-ajax.phpas anonymous — any 200 with a token string in the body means every "protected" submission endpoint behind it is unauthenticated attack surface. Bounded validation: lab install, your own form, inert marker payload in your own post, confirm execution only in your own browser profile.
October 2 06:30Z follow-up: cookie-forged privileged UI, lifecycle path reuse, legacy-key validation gaps, and default-disabled auth settings (Oct 2 06:30Z WP wave, folded)¶
- DevKit Pro ≤2.3.0 unauthenticated Administrator ATO via forgeable
original_user_idcookie (GHSA-xwfv-9j83-wfj7, 9.8): therevert_switchhandler (a user-switching/impersonation feature) derives the "privileged identity" from an attacker-controlled cookie:verify_nonce_and_capability()checksmanage_optionson the user named by the cookie instead of callingcurrent_user_can()on the actual requester — and the switch-back form plus a valid session-bound nonce is emitted intowp_footerto any visitor, unauthenticated included, whenever that cookie is present. Recipe: setoriginal_user_id=<admin id>→ page renders with the admin's revert form + nonce → POST it back →wp_set_auth_cookie()for the admin. Durable axes: (1) capability checks must bind to the authenticated principal, not a request-supplied identity token — audit anycurrent_user_can($cap, $id)where$idcomes from cookie/parameter instead of the session; checking a capability on the victim proves nothing about the caller; (2) state-conditional privileged UI fragments leak to anonymous callers — if markup (with embedded nonces) is gated by a cookie rather than by the session, an attacker can trigger the gate for themselves; sweep "switch back / return as / undo impersonation" affordances by setting the steering cookie on a logged-out client and grepping the response for nonces. Same family as the Oct 1 WPC Shop login-as finding: impersonation features carry their own second-order auth surface. - Ninja Forms – File Uploads ≤3.3.34 stored path read/written/deleted by later lifecycle stages (External S3 flow) (GHSA-3v9p-5xxf-6hxg, 8.1): the form submission carries an attacker-supplied file path that is stored verbatim as
file_path, then dereferenced without validation by three later consumers — the notification-email attachment (arbitrary file read), the external-store fetch (arbitrary file write, RCE when S3 action configured), and scheduled cleanup (arbitrary file delete). Durable axis: a path stored at input time is a deferred primitive — validation at form-submit time (or none, as here) is irrelevant if the consumers re-dereference the stored string; when auditing form/upload plugins, grep wherefile_path-shaped columns are read back (mailer, sync, cron/GC) and test the read/write/delete legs from the stored row, not just the submission. Precondition reporting matters: the write leg requires the External (S3) upload action configured, the read leg also needs an email action attaching the file. - Super Forms ≤6.3.316, two more legs (third and fourth bugs on this plugin's audit ladder, after the Oct 1
role=+delete_diritems above): (a) Subscriber→any-account credential takeover (GHSA-h8h9-7p7r-7rpq, 8.8) — the Register & Login add-on'sregister_login_action='update'flow trusts an attacker-supplieduser_idintowp_update_user()with no ownership check, and becausesuper_save_formitself has no capability check (Oct 1 finding), the Subscriber creates the malicious form then submits it with an admin'suser_id+ new password → full account takeover; the two bugs compose — form-builder freedom + update-flow identity trust = privilege chain neither half alone. (b) Unauthenticated arbitrary file read viaparse_requesttraversal (GHSA-r39q-9952-j46j, 9.1) — thefile_upload_authsetting defaults to empty (no auth required); the traversal needs a real 13-digit timestamp upload directory, and the plugin's own file-upload response returns the created directory name — the feature hands you the oracle needed to exploit its own traversal. Durable axes: default-empty auth toggles mean the default deployment is unauthenticated (enumerate plugin settings whose empty value disables a gate —*_auth,*_key,whitelist); and check whether a sibling feature discloses the identifier a path check assumes unguessable. Fix-precondition note for reports: enabling the setting mitigates unauth access but does not fix the traversal itself — test authenticated too. - SiteOrigin Widgets Bundle ≤1.73.2 LFI via a legacy undeclared field (GHSA-vc58-2rcj-cgf8, 7.5): POSTing
widgetDatawith a legacy top-levelthemekey alongside a non-emptycolumnsarray to/wp-json/sowb/v1/widgets/previewsbypasses validation entirely becauseupdate_fields()only iterates declared form fields — the undeclared legacy key still reaches the widget renderer, which includes the path as PHP. Contributor-level → code execution. Durable axis: validation loops over the current schema; renderers often still honor legacy/undeclared keys — mass-assignment's WordPress flavor: fuzz documented-in-changelog-but-no-longer-in-the-form parameters, and compare "keys the validator iterates" vs "keys the renderer reads"; the delta is unvalidated reachable input. - RafflePress <1.12.27 reCAPTCHA secret key published in public giveaway settings (GHSA-82gv-pr29-8vm2): the plugin embeds the server-side reCAPTCHA secret in the settings blob on public giveaway pages. Durable heuristic: on pages embedding a client-side config/JSON blob, diff returned fields vs fields the UI actually renders — server-only secrets (recaptcha site/secret pairs, API tokens, provider keys) leak wholesale when a plugin serializes its options object instead of an explicit view model. Same raw-serialization disclosure family as the Oct 2 PictShare info-endpoint item; sweep
wp_localize_script/inline{"settings":payloads for_key|_secret|_tokennames. - Bounded validation: lab installs with disposable accounts; DevKit-style cookie proofs use your own admin account ID and revert immediately; Super Forms traversal reads synthetic marker files you uploaded to your own lab; RafflePress checks are your own lab giveaway page's embedded blob; SiteOrigin inclusion proofs use an inert marker
.phpyou uploaded in the same lab.
October 2 late follow-up: client-driven checkout state machine, entity-encoded sanitizer bypass, and regex-gated header echo (Oct 1 09:30Z GHSA sync wave, folded)¶
- Ultimate Multisite ≤2.15.0 unauthenticated ATO of any user by email address (CVE-2026-75957 / GHSA-xr7j-wm8w-c5p4, 9.8): the public
wp_ajax_nopriv_wu_validate_formhandler accepts a freely obtainable checkout nonce, and thecheckout_form=wu-finish-checkoutparameter makesget_validation_rules()discard all validation rules whilefinish_checkout_form_fields()returns an empty step list —is_last_step()then returns true and the request routes straight into full order processing.maybe_create_customer()resolves the attacker-suppliedemail_addressto an existing WordPress user ID with no authentication or ownership check, andlogin_customer_after_checkout()callswp_set_auth_cookie()— full session for the victim. Durable axes: (1) client-supplied state tokens that select the validation branch — a step/flow parameter (checkout_form,step,stage) that makes the validator return an empty rule set is an auth bypass wearing a workflow costume; sweep every multi-step form handler for parameters that index into a step→rules map and test the terminal/unknown step where the map returns nothing (empty rules + "is last step?" = skip-to-finish); (2) identifier resolution as credential: any flow that maps an email/username to a user ID and then acts as that user (auto-login, order attribution, invite acceptance) treats "knows the email" as authentication — test with your own second account's email on a lab checkout; (3) public-nonce +noprivhandler composition is the Forminator axis (Oct 1) reaching authentication rather than stored XSS — the same two-request formality now mints cookies. - PDF Invoices & Packing Slips for WooCommerce ≤5.16.1 unauth stored XSS through entity-encoded billing names (CVE-2026-92244 / GHSA-v7w7-gmmm-67f2, 7.2):
sanitize_text_field()/wc_clean()do not strip entity-encoded strings containing no literal<character, so a guest-checkout order plants payload in Billing First/Last Name/Company that executes whenever an admin opens the order. Sanitizer-class axis, new encoding leg: a store-time filter that blacklists literal characters (<,") misses<script>-style or double-encoded shapes when the render-time context decodes them. Sweep canary for every free-text field that reaches an admin screen: submit<img src=x onerror=...>and&lt;svg onload=...&gt;shapes via guest checkout/registration, then open the admin view — the payload surviving storage and executing on render proves the store/render decode mismatch. This is the sanitize-then-decode family (Ultimate Member, Sept 19) reached by the input instead of the storage layer. - Ad Inserter ≤2.8.18 reflected XSS via
Refererheader, gated by a search-engine regex (CVE-2026-19902 / GHSA-298r-885r-pj27, medium): when an ad block's code contains the{search-query}dynamic tag,replace_ai_tags()reads$_SERVER['HTTP_REFERER'], tests it against/[\.\/](google|yahoo|bing|ask)\.[a-z\.]{2,5}[\/]/i, and echoes the match — header content that satisfies a look-valid regex gets reflected unsanitized. Durable axis: conditional-echo gates on attacker-controlled headers are satisfiable by design — aif (preg_match('/google|bing/', $referer)) echo $referershape has an attacker-controlled gate; enumerate every header the app reflects (Referer,X-Forwarded-*,User-Agentin debug panels) and supply payloads that include the expected look (https://google.com/prefix + payload). Adjacent same-plugin item CVE-2026-89427 / GHSA-cjx2-r3r5-6cq4 (reflected XSS viassearch parameter) is the un-gated sibling of the same tag renderer. - LearnPress ≤4.4.8 IDOR through an explicitly no-nonce AJAX allowlist (CVE-2026-93882 / GHSA-hgjr-485v-48f2, high):
CourseMaterialTemplate::render_material_items()is exposed on the publiclp-ajax-handle(load_content_via_ajax) endpoint, listed in the framework's AbstractAjax no-nonce allowlist, with no capability check on the course/id arguments → unauthenticated read of course material by ID. Sweep rule this adds: in any plugin with an AJAX framework that dispatches byactionto per-callback classes, find the framework's own "skip nonce" list (a constant/array in the abstract class) — every callback on it is unauthenticated surface regardless of what the individual handler author assumed; enumerate allowlisted action names from the class source on open-source plugins, or black-box probe them against the single shared handle endpoint. - Bounded validation: lab multisite with your own two accounts (prove the email-resolution login against your own second account and revert); guest-checkout XSS payloads rendered only in your own admin profile; Referer/XSS canaries only on lab installs; LearnPress IDOR proven with a synthetic course you created.
Tracked without publication (same Oct 1 09:30Z sync wave): Form Maker 10Web map-lat/long stored XSS (GHSA-6hr3-5gmx-jj2q), Forminator RTE-textarea stored XSS (GHSA-p5j2-g3x4-wqgf, RTE axis canonical), Appointment Hour Booking DOM-XSS via schedule renderer (GHSA-8899-xhvc-pc43), Calculated Fields Form reflected DOM-XSS via url.<name> predefined value (GHSA-fqpx-ff2f-3pfw), Redux user-meta-merge XSS (GHSA-8g8c-pxm3-p2hh), Duplicate Post noti_token XSS (GHSA-mjxg-mhjq-2vx5), Awesome Support gdpr-data XSS (GHSA-rgpg-j229-6w44), Business Essentials CF7 gateway-field XSS (GHSA-h887-w8fc-499x), bbp style pack display-name+reply XSS (GHSA-3qmp-qqq9-w2h8), Social Media Share Buttons mobile-gated reflected XSS (GHSA-85vv-xjp4-x52m), Ad Inserter s-parameter reflected XSS sibling (GHSA-cjx2-r3r5-6cq4) — XSS-class already canonical; MISP analyst-notes + legacy-taxonomy reflected XSS (GHSA-66pp-jmhc-r9mv, GHSA-p9m5-xwgx-8j69) — reflected-XSS singles on a page whose axes are already published; OpenConstructionERP AI-provider config wrong-session disclosure (GHSA-8rwg-xf8h-29m3, detail-thin); Fuji Apeos printer web-UI traversal (GHSA-vmmg-gmvf-x548, generic printer-traversal class); Android SettingsLib/AccessibilityManager/CredentialManager/Biometric logic singles (June-dated re-surfacing, processed class).
October 2 06:30Z wave tracked without publication¶
- Ninja Forms ≤3.15.4 RTE-field stored XSS (GHSA-7cq6-x444-93vj) — unauth stored XSS when a Paragraph Text field has RTE enabled; sanitize-skip-on-optional-field axis already canonical here (Sept 22 Give Tributes config-posture-gated pattern).
- Autoptimize ≤3.1.15.1 comment-author-name stored XSS (GHSA-fcq7-9xcx-6xxw) — sibling of the Oct 1 Autoptimize REQUEST_URI item already on this page; lazy-load precondition gating is the same feature-flag axis.
- RafflePress <1.12.27 unvalidated parent-page URL → site-origin redirect chain (GHSA-9949-jq7q-8g48); OMGF <6.3.11 unauth no-nonce slow loopback request DoS (GHSA-87mc-m7h4-wxp9) — DoS with no extraction leg, tracked per precedent; Popup Maker ≤1.4.5 nonce-only subscriber site-option overwrite (GHSA-f5pm-cq7v-xmjw) + Request a Quote ≤2.5.6 unauth AJAX contact-record read (GHSA-24j4-6qf8-p4rw) — nonce≠auth and unauth-read axes canonical; Motors listing-metadata IDOR (GHSA-whfp-wchg-v9xw, non-default config) + badge-setting XSS (GHSA-h25v-5897-j3vf); BuildKit <1.0.29 contributor stored-SQLi executed on public view (GHSA-564m-jf3j-p556) — deferred two-stage shape noted but SQLi-class already canonical; CTX Feed Pro ≤7.6.12 admin eval() (GHSA-m8mq-x2gc-8xcm, admin-only); Visitors Traffic Real Time Statistics Pro ≤11.22 nopriv
page_titleunauth stored XSS → admin dashboard innerHTML (GHSA-x36m-2pwv-jrpp) — front-door-input/back-office-sink exemplar, axis canonical; AvadalangR-XSS, Unlimited Elements icon-attribute XSS, Tabs Responsive shop-manager product-tab XSS — generic sanitizer singles.
October 2 09:31Z follow-up: response-cache keyed on URI alone serves minted admin cookies to anonymous GETs (JSON API Auth, folded)¶
- JSON API Auth ≤3.1.2 unauthenticated Administrator session theft via cached cookie-minting response (CVE-2026-97637 / GHSA-pmrp-gw7x-5x4r, critical): the PI-Media/json-api parent plugin caches controller dispatch results in transients keyed solely by URI + query string — ignoring HTTP method and POST body. Its
generate_auth_cookie()endpoint embeds a livelogged_incookie (wp_generate_auth_cookie()) directly in the JSON response body; after an administrator POSTs to/api/auth/generate_auth_cookie/, any subsequent unauthenticated GET to the same URI replays that cached authenticated response for the 24-hour TTL → full admin session. The plugin's nominal HTTPS enforcement gate is bypassed by supplyinginsecure=coolas a request parameter (the literal vendor backdoor string). Durable axes: (1) response caches keyed on URI alone collapse method and body into identity — any POST-mints-a-secret endpoint behind a URI-keyed cache layer becomes a public GET-replayable secret; enumerate cache/CDN/transient layers per endpoint and test GET-replay of POST responses; (2) response-body credential embedding:wp_generate_auth_cookie()output in a JSON body is a cookie regardless of transport — sweep API responses forlogged_in/auth_cookie/token fields and treat them as session material; (3) hardcoded bypass params survive as permanent backdoors — grep plugin source for magic parameter values (insecure=,debug=,dev=) on every API plugin; the cache-replay precondition (an admin POST inside the TTL) makes this a wait-and-replay attack on active admin panels, worth reporting with the TTL window stated. Bounded validation: lab install, your own admin account POSTs the endpoint, anonymous second browser replays the GET; prove cookie validity on your ownget_currentuserinfo; delete transients after.
October 2 15:31Z follow-up: bcrypt-comparison role resolution turns a locally-computed hash into an admin registration (Divi Membership, folded)¶
- Divi Membership ≤2.2.0 unauthenticated administrator registration via attacker-supplied bcrypt hash (CVE-2026-19652 / GHSA-f7mc-wpjj-g5hj, 9.8):
dmem_form_submit_handler()decides the new user's role by iterating all WordPress roles and callingpassword_verify()against a bcrypt hash the attacker supplies in theform_idPOST parameter — no role whitelist. Submit a locally computed bcrypt hash of the stringadministratorasform_id, and you register as an admin; withauto_login=onthe same request returns an authenticated admin session. The required nonce is publicly emitted on any page rendering the registration form, so the nonce gate is decoration. Durable axes: (1) value-comparison-as-authorization across an unbounded candidate set — when a handler loops over privileged candidate values and tests attacker input for a match (hash-compare, token-compare, secret-compare), the attacker who can pre-compute the match chooses the outcome; enumerate every parameter whose value is compared rather than looked up, and ask what happens if the attacker guesses the compare target (password_hash('administrator', PASSWORD_BCRYPT)is trivially computable because the "secret" is a public role name); (2) publicly-minted nonces again (Oct 1 Forminator sibling on this page) — confirm the nonce's issuance page is reachable unauthenticated before crediting it as authz; (3) combined state transitions in one request (auto_login=on) mean privilege escalation is immediately used, not staged — test whether escalation endpoints also emit the session. Bounded validation: lab install, register your own admin account via the form-parameter shape, document role table before/after, delete the account.
October 3 03:31Z follow-up: AJAX directory endpoint skips the field-level visibility model the profile page enforces (Ultimate Member, folded)¶
- Ultimate Member ≤2.13.1 unauthenticated read of privacy-restricted profile fields (CVE-2026-93428 / GHSA-mx72-jqqr-rmpp, 7.5): fields configured owner-only, members-only, or role-restricted on the rendered profile page are returned in clear by the public
wp_ajax_nopriv_um_get_membersendpoint — the directory AJAX handler applies none of the visibility model, and its only gate is theum-frontend-nonce, whichwp_localize_scriptemits to anonymous visitors, so the endpoint is unauthenticated end to end. Durable axes: (1) visibility/privacy models live in the render path; AJAX/REST siblings return the raw object — for every field-level privacy toggle a plugin offers, enumerate which endpoints can return that field and re-read the same object through each (profile page vs directory AJAX vs REST vs export); a privacy setting tested only on its own page is unverified. (2)wp_localize_script-emitted nonces are public constants — before crediting any nonce as an authz gate, fetch the issuing page with no session and grep for the nonce value (this page's The Welcomizer / HUSKY / Welcomizer-family rule, now with a localize-script minting vector). Sweep shape: on any UM (or membership-plugin) target, requestaction=um_get_memberswith the page-extracted nonce and diff returned field keys against what the same profile renders anonymously. Bounded validation: lab install with a disposable member account and synthetic field values; enumerate field keys and presence/absence only; never harvest real member PII beyond confirming your own canary values.
October 3 06:31Z follow-up: recipient-parameter override, upsert-leg guard gap, attacker-chosen deferred SQL trigger, and fallback-extractor hardening gap (Oct 3 06:31Z wave, folded)¶
- Simple Membership ≤4.8.3 unauthenticated activation-email recipient override → plaintext-credential theft (CVE-2026-97337 / GHSA-7wr9-4367-v9vm): the
resend-activation/email-activationendpoints dispatch fromSwpmInitTimeTasks::check_and_do_email_activation()on frontend init with no auth, nonce, capability, or ownership check, and the recipient used bysend_reg_email()is taken from attacker-controlled$_POST['email'], overriding the pending member's registered address. The redirected "registration complete" email contains the member's username and plaintext password, and the attacker can then activate the victim's account. Durable axes: (1) notification handlers that take the recipient from the request are recipient-swap oracles — for every resend/notify/forward action, diff the recipient actually used against the object owner; the parameter names to sweep areemail,recipient,user_emailalongside the ID that identifies whose notification it is; (2) plaintext passwords in transactional email are a standing ATO primitive — when any plugin email contains credentials, the recipient-override bug converts a mail-flow flaw into account takeover without touching the password store; fingerprint plugins that build "your login details" emails. - Groundhogg ≤4.9 contact identity rebinding via upsert-vs-update guard parity (CVE-2026-97644 / GHSA-9c9v-5p57-4qfm, 8.8):
POST /gh/v3/contactsis gated only byadd_contactsand forwards the full payload including the security-bearinguser_idcolumn intoContacts_DB::add()'s upsert path, bypassing the ownership guard thatContacts_DB::update()enforces — a Sales-Rep can rebind any existing contact row to an Administrator's user ID, then invoke the v4POST /gh/v4/emails/testendpoint which acts under the bound identity. Durable axes: (1) add/upsert legs routinely lack the guard the update leg has — the create-side mirror of this page's sibling-leg rule; whenever you findupdate()/save()with an ownership check, test the create/upsert/import leg with the same guarded column present in the payload; (2) CRM contact→user-ID bindings are identity links — rebinding an entity you legitimately hold to a privileged user ID makes every later feature that "acts on behalf of the contact's user" (email sends, automations, API tokens) run as that user; enumerate every consumer of a rebinding-able FK before reporting the write alone. - GeoDirectory ≤2.8.186 stored-coordinate SQLi with attacker-chosen public trigger (CVE-2026-103913 / GHSA-285m-c668-wj9h): lat/long values are saved with no numeric validation, then
geodir_gps_query_part()string-interpolates the stored values into a distance sub-expression executed by the publicwp_ajax_nopriv_geodir_widget_listingshandler when a caller suppliesset_post=<pending-listing-id>&sort_by=distance_asc. A Subscriber plants the payload; the SQL runs in an unauthenticated context the attacker picks the moment and endpoint for. Durable axis: deferred-SQL with trigger selection — the save leg never executes the payload (prepared or escaped at write time is irrelevant), and the consuming leg trusts stored bytes; sweep every sort/distance/nearby query parameter that references a stored object ID, and treat "stored value + expression-building consumer" as the SQLi shape even when the writer looks prepared. Sibling of the Ninja Formsfile_pathdeferred-consumer rule (Oct 2) with SQL as the sink. - Unlimited Elements for Elementor <2.0.21 archive extraction RCE only on the fallback extractor (CVE-2026-85015 / GHSA-q6fg-7vp2-c3xp): uploaded archives are extracted without path sanitisation on servers where the PHP zip extension is unavailable — the fallback extraction code path preserves traversal separators and writes executable PHP outside the upload dir. Durable axis: the fallback/secondary parser is the unhardened one — primary paths (PECL zip, cURL with options, image libs) accumulate sanitizer fixes; fallbacks (PclZip, manual TAR readers, pure-PHP parsers) ship as "compat shims" without the same guards. When a feature behaves safely on your fully-patched lab box, enumerate the feature-detection branches (
extension_loaded,class_exists,function_exists) and reproduce in the degraded environment (disable the extension) before concluding the sink is closed. Same family as the Composer symlinked-bin fix-bypass and provider-conditional XML hardening lessons on the July 31 library page. - WP 2FA <4.1.0 TOTP replay: validate-without-invalidate (CVE-2026-103514 / GHSA-fh83-crgf-9w82): a time-based one-time passcode is not marked used after acceptance, so anyone who knows the account password and observes one valid code inside its window can replay it — including against admin accounts. Durable axis: OTP controls are only "one-time" if the server consumes them — for any 2FA plugin/flow, prove single-use by completing a lab login, then replaying the same code inside the window; the probe costs one extra request and separates genuine MFA from replayable MFA. Reports should state the observed window and whether replay survived a concurrent login.
- Visitor Traffic Real Time Statistics ≤8.16 unauthenticated stored DOM-XSS via
X-Real-IPheader (CVE-2026-97341 / GHSA-4mc8-9rx9-vcjv): thewp_ajax_nopriv_ahcfree_track_visitorendpoint accepts a forgedX-Real-IPfrom any anonymous request and stores the entity-encoded payload verbatim, rendered later as DOM. Axis: tracking/analytics endpoints store request headers as user data — visit-trackers persist header-derived "visitor" fields (IP, UA, referer,X-Real-IP,X-Forwarded-*) that dashboard views later render; add every header to the stored-input canary set on nopriv tracking actions, not just body parameters. - Kubio AI Page Builder <2.9.3 editor-scoped HTML allowance applied to unauthenticated submissions (CVE-2026-88783 / GHSA-w96f-q24m-hc95): the plugin widens its allowed-HTML element set for the editor context but doesn't scope the widening to it — unapproved unauthenticated submissions keep markup that the plugin's own front-end script later executes. Axis: sanitizer allow-list widenings inherit to every role/path unless explicitly scoped; test the lowest-trust submission path against the widest policy. Tracked adjacent: same-plugin contributor URI-scheme link XSS (GHSA-gf46-mg37-6fp9).
Same wave, tracked without publication on this page's canonical axes: TillKit <1.0.5 hardcoded publicly-known POS PIN authenticates a privileged session (identifier-as-credential, July 30 EV page rule); MPG <4.2.3 dataset-source URL-vs-path type confusion copying arbitrary files into public uploads (file-copy relay, tracked); Loco Translate <2.8.9 translator-cap file read outside webroot + bundle XSS (GHSA-m23q-3295-mh2g, GHSA-xmgg-47vp-73xr); MetForm <4.3.1 webroot debug-file leak + REST submission-data read (GHSA-cr6m-q4jq-r64m, GHSA-5m3g-q793-wg8x); WP Ultimate CSV Importer <9.2 unsalted guessable import-log path (GHSA-w4w7-6vj3-3c3v) + archive-stored XSS via multisite unfiltered_html gap (GHSA-4qqx-23c4-hcqv); Mailchimp for WooCommerce <6.3 unauth abandoned-cart IDOR (missing-ownership, canonical); Photo Reviews for WooCommerce ≤1.2.30 arbitrary-post deletion via stored unowned attachment IDs + deferred wp_delete_post on admin/cron trigger (GHSA-24v8-rv6x-cqwg, deferred-consumer sibling noted above); Helpdesk for WooCommerce ≤2.1.6 ticket-response IDOR deletion via admin-footer nonce (GHSA-vv97-3hr9-5prm); Pie Register <3.8.4.14 invitation-code report username/email dump (GHSA-hj5m-8g2v-x4f9); Unlimited Elements search-value rewrite of prepared SQL (GHSA-f222-q2vh-g6q6, prepared-statement-post-edit, config-precondition-gated); AIOSEO ≤5.0.2 unauth arbitrary shortcode execution (GHSA-5q32-9w97-g3v9, do_shortcode canonical); ProfilePress ≤4.17.4 [pp-custom-html] shortcode-fragment profile-field disclosure via registration nickname/bio (GHSA-fc4p-8rjj-4hfw); EmbedPress unquoted-attribute block XSS (GHSA-2gcx-8mfj-5hvc); GD Rating System/Strong Testimonials/SEOPress/Welcart/Transliterator/Magic Tooltips/SaveTo/WP Ultimate Review/WPC Smart Quick View/EWWW/Real Cookie Banner/WPZOOM/LearnPress/Calculated Fields/XSS-class singles — generic sanitizer singles per precedent.
October 4 09:30Z follow-up: unscoped REST auth filter disables core nonce protection site-wide, rewrite-rule protection dead on multisite topology, and option-name resolver as disclosure oracle (Oct 4 09:30Z WP wave, folded)¶
- CoCart ≤4.9.7 unscoped REST authentication filter → site-wide CSRF including admin creation (CVE-2026-93549 / GHSA-9p8w-fmwq-692f): the plugin registers a
rest_authentication_errors-style filter that never checks whether the current route belongs to CoCart — when its own cookie-auth logic runs for every route, it causes WordPress core's REST nonce check to be skipped for all routes on the site, including core and WooCommerce admin routes. Result: a cross-site request from any visitor holding a logged-in administrator session can hit corePOST /wp/v2/usersand create a new administrator — the nonce wall that normally blocks state-changing cookie-auth REST requests is gone site-wide because of one plugin's laziness. Durable axes: (1) WP REST security is filter-stack security; an unscoped plugin filter modifies the security posture of routes it does not own — a single badrest_authentication_errors/rest_pre_dispatch/rest_post_dispatchcallback is a site-wide control change, so the audit question flips from "is this plugin's authz correct" to "which global filters are installed and what do they do to routes outside their namespace"; (2) for a bug hunter this is a multiplier discovery, not the finding itself: when any installed plugin registers an unscoped REST auth filter, every normally nonce-gated route on the site (core user creation, plugin settings routes, WooCommerce mutations) becomes plain CSRF — enumerate active plugins' REST filter registrations (add_filter('rest_authentication_errors'greps on open-source installs) and treat unscoped ones as a site-level authz finding independent of the plugin's own routes; (3) prove boundary: lab install with CoCart active, drive the admin-creation CSRF against your own lab from a second browser with an admin session, then delete the created account. - User Private Files <2.2.0 private-file disclosure because the protecting rewrite rule is never reached on multisite (CVE-2026-97332 / GHSA-rgr9-g879-r5wm): the plugin's access check lives in a rewrite rule that routes stored-private-file URLs through its PHP gate; on multisite installations the rule is never registered/reached, so direct file URLs return other users' private files with no authentication. Durable axis: a protection implemented as a routing-layer rule is topology-dependent — the same URL is gated on single-site and wide open on multisite/subdirectory networks, and vulnerability scanners and plugin authors both routinely test only the single-site shape. When a WP target is multisite (or uses alternate permalink/site-structure configurations), re-probe file paths protected by rewrite-based gating (private files, download gates, membership content gates) directly, and diff single-site vs multisite responses for the same canary path.
- Five Star Business Profile and Schema <2.4.0 arbitrary option/meta read through unconstrained default-value resolver callbacks (CVE-2026-86817 / GHSA-qpw5-3rj5-433c): Author-level users can store schema-field default values that reference arbitrary callbacks/keys, and the resolver happily reads whatever name it is given — disclosing other users' password hashes and arbitrary
wp_optionsvalues in public front-end output. Durable axis: name-to-value resolver callbacks are datastore-read oracles when the accepted name set isn't constrained — any feature where a low-privilege stored value later becomes an argument toget_option/get_user_meta/a callable name should be swept with well-known sensitive key names (*_keys, user meta containing hashes/tokens) on a disposable author account, and the public render checked for the canary value. Sibling of the coarse-permission secret-read class with the resolver-callback as the new input shape. - Same wave, tracked without publication on canonical axes: Razorpay for WooCommerce <4.8.8 unauthenticated checkout REST route modifying shipping info on arbitrary orders (GHSA-g56q-qwxf-x99w, missing-ownership canonical); Unlimited Elements ≤2.0.20 blind SQLi (GHSA-rcjh-9qxp-82pg, SQLi canonical); Simple Shopping Cart stored settings XSS, Horizontal scrolling announcements stored XSS, and generic XSS singles (GHSA-p57v-42f3-wfqr, GHSA-6g7j-p76q-h89g, plus three detail-thin 7.1 XSS entries) — sanitizer singles per precedent.
-
Same-wave non-WP tracked: InternLM MindSearch 0.1.0 planner-agent
ExecutionAction.runcode injection viainputswith public exploit (GHSA-4535-w5r2-pmqp / CVE-2026-105135, 10.0) — lands directly on the Oct 2 AI page's published "tool registry is the attack-surface map / enumerate all execution sinks, not just the obviously-named ones" rule; no new axis beyond it, vendor unresponsive. cognee ≤1.5.4 hardcodedFASTAPI_USERS_JWT_SECRETdefault signing key → forged auth tokens (GHSA-xrgh-2vxr-rhm4 / CVE-2026-105141) — shipped-default-secret class canonical on the Oct 2 AI page (Trigger.dev.env.exampleleg). Drogon ≤1.9.13/10.0-beta WindowsStaticFileRoutertraversal (GHSA-7fvc-r3q2-9v2h / CVE-2026-105144) — Windows backslash traversal class canonical. Laradock ≤20.4 workspace Dockerfile unsigned code download (GHSA-r2hw-g35f-pfw5 / CVE-2026-105137, high-complexity) — build-integrity class, no new sink. -
Same wave, tracked without publication on this page's canonical axes: Ultimate Member ≤2.13.1
form_idstored XSS viasubmittedusermeta → admin Users-modal jQuery.html()(GHSA-6phc-8h2j-74hw / CVE-2026-96270, unauth front-door input / back-office sink, axis canonical); Beaver Builder ≤2.11.0.5 contributor blind SQLi viafields[][value]onget_autosuggest_values— the requiredfl_ajax_updatenonce is emitted into the block editor for anyone who can edit a BB post, another author-capability-gated nonce mint (GHSA-27qq-rr92-q78g / CVE-2026-95865); SupportCandy ≤3.5.3sort_bytime-based SQLi behind compound Subscriber+agent-role precondition (GHSA-xqwv-9f36-ccgr / CVE-2026-94539) +namestored XSS gated on non-default config (GHSA-8cxj-g538-75wh / CVE-2026-94378); Ivory Search ≤5.5.18 highlight-term reflected XSS with admin-precondition (GHSA-fhf6-3c94-w2q4 / CVE-2026-92243); Jeg Kit ≤3.2.19 comment stored XSS via approved-email allowlist widening (GHSA-97rq-wvrr-p823 / CVE-2026-100180); Formbricks <5.4.4/<6.0.1 "Custom Head Scripts" survey feature enforced at readWrite instead of Manage → persisted script executes in every survey visitor's session (GHSA-q9hg-xqvp-x2xv / CVE-2026-105090 — role-gate drift on a feature that persists code executed in others' browsers; the shape duplicates the Trigger.dev/low-code config-as-code family, tracked). ThemeREX Addons ≤2.46.0 SSRF (GHSA-wx4c-58f5-rwc5, generic plugin-fetch class); Airano MCP Bridge ≤2.11.0 missing-authorization (GHSA-f55q-6qxf-8569, no-callback axis canonical); PublishPress user-controlled-key authz bypass (GHSA-jffr-vg9w-qc68); LearnPress missing-authorization sibling (GHSA-h3fr-24jq-9v5c); JetAppointment stored XSS (GHSA-fpfr-m3qm-9475); Aculect AI open redirect (GHSA-w4cx-23fp-xc39); Smart One sensitive-info-in-sent-data (GHSA-c299-27x5-pj33); HospitalManagementSystem/CodeAstro/SourceCodester/AVEZ thin-detail singles.
October 3 09:31Z wave tracked without publication¶
- Beaver Builder ≤2.11.0.5 unauth arbitrary shortcode execution via Sidebar-module widget text (Recent Comments shape) with comment moderation off/approved (GHSA-jx8c-663q-ggc5 / CVE-2026-92084, critical) —
do_shortcodeon untrusted render chain is canonical here (AIOSEO sibling same day); the durable nugget is the render-chain precondition (attacker text must reach a widget inside a BB page) rather than a new sink. - VikAppointments ≤1.2.21 unauth arbitrary file deletion via
extract()path validation gap, wp-config.php deletion → RCE path (GHSA-x82w-x82x-7998 / CVE-2026-87115, critical) — archive-extraction traversal class canonical (Unlimited Elements fallback leg above); wp-config-delete→reinstall is a known WP RCE tail. Precondition: published File-type custom field. - WP Visitor Statistics ≤8.7 second-order SQLi: unauth
wmcTrackpersists rawfullRefintowp_logVisit, admin Traffic Sources view triggers it (GHSA-f46r-g4h3-gxr2 / CVE-2026-96267) — deferred-SQL shape duplicate of the GeoDirectory leg promoted the same day; tracker-endpoint stored-header rule already covers the input side. - Smart Manager ≤8.97.0 subscriber SQLi via
access_privilegeswhere a role deny-list config that fails to block the internalaccess-privilegemodule implicitly permits subscribers (GHSA-c5cw-fc86-6h2j / CVE-2026-18443) — deny-list-default-allow + self-referential module gap noted; config-precondition-gated, sibling of Formbricks role-gate drift and SiYuan zero-check sweep. - WPCafe ≤3.0.18 contributor LFI via template-scope function (GHSA-qq8p-42q8-m7f5); Nelio Content ≤4.5.0 contributor deletes any
nc_reusable_socialpost (GHSA-j2j8-hvh9-q6jf, sibling-action authz canonical); WP Mail Catcher ≤2.1.12 stored XSS via PHPMailerwp_mail_failederror text into mail-log view (GHSA-7wqp-vpcj-332m — error-message-as-user-data leg of the stored-input canary set, noted); Twenty20/WPFront/WPC Options/WPMobile/Responsive Plus/WP Ultimate Review pair/WPC Delivery Date/WPCafe enum singles — sanitizer/missing-cap singles per precedent.
October 7 09:30Z follow-up: capability-gated code-execution feature reachable by default Editor, and stored XSS whose final leg is the web server's MIME-sniffing default (Oct 7 09:30Z WP wave, folded)¶
- WP Coder <4.5.2 Editor-role PHP code execution because the code-exec feature is gated on a content capability (CVE-2026-104677 / GHSA-pfgx-g476-6w3m): the plugin's "PHP code snippet" feature checks a capability the Editor role holds by default instead of an admin-only one, letting Editors save and execute arbitrary PHP on the server → full site compromise from a role handed to content teams and agencies. Durable axis: capability-choice drift, not capability-absence — the check exists and passes legitimately; the bug is picking
edit_*/edit_theme_options-family capabilities for a code-execution sink. Sweep on every plugin that gates a powerful feature: read thecurrent_user_can()argument and resolve it against the default role map (editors holdedit_pages,edit_theme_options,unfiltered_htmlin single-site) before assuming the gate is admin-only. Editor→RCE is the highest-severity WordPress finding class for agency-hosted estates; enumerate installed plugins with code/snippet/template executors (code-snippet managers, theme/ file editors, ad/tag managers) and test each from a disposable Editor account. - OMGF ≤6.3.10 stored XSS through the
ssearch parameter in the comments-atom feed — with the delivery layer as the deciding leg (CVE-2026-89417 / GHSA-jmqx-vrvj-5m9h, high): the plugin's feed pipeline writes a retained.tmpfile containing the unsanitized search term, and when the front-end server serves that.tmpwithout aContent-TypeorX-Content-Type-Optionsheader, MIME-sniffing browsers (Chromium) execute it as HTML — a condition the advisory notes is default on many Apache/nginx+php-fpm deployments. Durable axis: the stored-XSS chain's final sink can be the web server's default header behavior, not the app's escaping — when you find any persisted attacker content in a server-served path (temp/cache/export/feed files), test it fetched directly: noContent-Type, nonosniff, HTML-looking body = executing stored XSS even when the app never renders it. Add "list app-writable, URL-reachable file paths and diff response headers" to the stored-XSS sweep; the same posture catches cached-fragment and export-file XSS families. - Same wave, tracked without publication on canonical axes: Academy LMS <4.0.0 quiz-question and course-enrollment object-authz misses (GHSA-rg55-r6h3-vvf2, GHSA-43m9-7565-v53c, missing-ownership canonical); User Private Files <2.1.9 supplied-user-ID file access (sibling of the Oct 4 rewrite-gate leg, GHSA-m2gh-g9gw-hjhf); Nexi XPay Build ≤7.6.2 unauthenticated payment-result routes marking arbitrary orders paid/failed + order-key disclosure (GHSA-65w5-qcwh-f3g3 unverified-callback + GHSA-8fcj-x85r-h3ff token-validation leg; payment-state-machine family, callback-forgery shape canonical), Yaad Sarig gateway sibling (GHSA-6pp4-6q7m-6492); Optimole ≤4.2.15 missing capability check on an exposed function (GHSA-q3p9-mmwf-qgrc); If-So Dynamic Content ≤1.10.2 URL-scheme validation gap (GHSA-qp9g-v8qg-cx5q); Koinonia Link ≤1.1.5 unvalidated target-user change (GHSA-hx93-m334-hcgh); PowerPress <11.17.11, Envira pair, MetForm, Magee pair, String locator class-restriction, Animated Number Counters, WPCafe REST restriction miss, MPG import structure, Geliver cargo marketplace, Epos Now, Frontend Dashboard, CP Media Player — sanitizer/capability singles per precedent.
October 8 00:30Z follow-up: LatePoint subscriber IDOR where the contact-merge key decides whose password-reset email you redirect (CVE-2026-17538 / GHSA-gw8m-rqgv-6j3j, ≤5.6.9)¶
process_step_customer() gates on is_user_logged_in() alone before merging POSTed customer data (first/last name, email, phone, notes) into an existing LatePoint customer record — no ownership check. Any Subscriber can rewrite any customer's personal data, and when the site's contact_merge setting is phone, overwriting the email field on the victim's record makes the next password-reset send to your address → full account takeover.
- Durable axis: merge-by-natural-key turns a plain field-write IDOR into identity binding. When a profile/customer store keys or merges records on a contact channel (phone, email, membership number) rather than an immutable ID, the field-write primitive crosses into authentication: the reset/OTP/2FA-SMS channel follows the edited field. Audit posture for every WP/custom booking, CRM, LMS, or member-portal plugin: find the record-merge/dedup logic, identify which field is the merge key under default settings, and test whether a low-privilege principal can write that field on a foreign record. The decision table is merge-key × write-permission × reset-channel — "subscriber writes victim's email under default merge setting" is the ATO finding, even though the advisory's base severity only says IDOR.
is_user_logged_in()is an authentication check masquerading as authorization — the WP canonical ("logged-in" is not "owns"), now with the merge-channel twist as the escalation leg. Disposable two-account harness (subscriber + victim customer with an owned-mailbox canary address); never touch real customer records.- Same wave, tracked without publication on canonical axes: Vault ACME/plugin-catalog/ACL four-pack was promoted on the Sept 20 credential-vault-platform page (Oct 8 follow-up) rather than duplicated here; Aurora Heatmap
urlstored XSS (GHSA-qg97-rqrx-gj82 / CVE-2026-94154) — sanitizer single per precedent.
October 8 06:31Z follow-up: backup-plugin trio where the trigger origin, the web-server topology, and the fallback archive library are each the boundary (BackWPup <5.7.7) + ABSPATH guard nullified by a trailing slash (Super Forms ≤6.3.316)¶
- BackWPup <5.7.7 cron-handler trigger-origin spoofing (CVE-2026-86827 / GHSA-pcfw-vf4c-h2h5): the cron-triggered backup execution handler never verifies the request originates from WordPress's internal scheduled-event dispatch — unauthenticated attackers force any existing backup job to run immediately, off-schedule, repeatedly. Durable axis: scheduled-action endpoints must authenticate their dispatcher. Any WP route whose only guard is "this is normally hit by wp-cron" is unauthenticated if it's URL-reachable; enumerate cron-callback routes (
add_action('wp_.../ action hooks registered alongsidewp_schedule_event) and test direct HTTP invocation. Job-forcing looks low-sev until you pair it with the next leg — repeated full backups of a large site are also an availability lever, and forcing a restore-window job changes the state machine under the admin's feet. - BackWPup <5.7.7 restore working directory web-reachable when the server ignores
.htaccess(CVE-2026-86826 / GHSA-jprm-wgx9-37wq): protection is an.htaccessrule; on NGINX (and any server that doesn't honour.htaccess) the working directory is directly downloadable — the full backup archive (DB dump + site files, credentials and secret keys included) left behind by an interrupted restore. Durable axis, twin of the Oct 4 User Private Files multisite rewrite-rule leg:.htaccess-based protection is a topology-dependent control that silently fails-open on nginx — the plugin author tests Apache, the deployment runs nginx, nothing errors. WP-ecosystem audit rule: for every app-writable directory the plugin claims to protect with rewrite/.htaccessrules (backup dirs, restore staging, temp exports, cache), fetch a canary path directly and check whether the deny actually fires on the target's server software; interrupted-job leftovers (partial restores, half-written exports) are the content to look for. On nginx fronted by php-fpm there is often no deny at all — full-site credential disclosure from an interrupted restore is a common real-world shape. - BackWPup <5.7.7 fallback archive library escapes the restore directory (CVE-2026-86828 / GHSA-5547-r7f3-wccc, admin→RCE): when the plugin's fallback archive extraction path is used, the extracted file destination isn't confined — high-privileged users write files outside the restore dir. Axis: feature-flag/library fallbacks carry their own (weaker) validation — the primary ZipArchive path was checked, the fallback PclZip-style path wasn't. Sweep every "if library X unavailable, use Y" branch: the fallback is where path checks go missing (same one-canonical-form theme as the Vault ACL leg). Precondition is admin-level, so report as defense-in-depth/hardening-adjacent rather than primary RCE on default installs.
- Super Forms ≤6.3.316 unauthenticated arbitrary directory deletion via ABSPATH guard defeated by
dirname()trailing-slash normalization (CVE-2026-17609 / GHSA-pchj-gc42-gqq7, critical): thesubmit_formpath validates attacker-controlled JSON field declarations insufficiently against the real form schema, and the containment check "path starts with ABSPATH" is trivially bypassed because a trailing slash makesdirname()strip it back out — recursivedelete_dirreaches anywhere, including the WordPress root (precondition: admin enabled the non-default "delete files after submission" setting, which the advisory notes is common). Two durable axes: (1) containment checks that compare a path against a root constant break on normalization order —dirname($p)on$p = ABSPATH . '/../foo/'yieldsABSPATH.'/../foo'→ parent: always test the trailing-separator anddirname-interaction variants of any prefix check, not just raw../; (2) attacker-declared field schemas must be checked against the server's stored schema, not just well-formedness — when a submission accepts "field declarations" naming file paths, the schema is the validation boundary. wp-config-adjacent deletion is the WP reinstall-tail RCE path (VikAppointments precedent on this page).
October 9 09:31Z follow-up: Code Snippets <3.10.0 — "site administrator" capability is not network confinement, and snippet scope comes from the request (2 GHSAs, multisite topology)¶
The WordPress Code Snippets plugin (PHP-snippet execution as a feature — every snippet is code running network-wide) published two legs that complete this page's multisite topology theme (twin of the Oct 4 User Private Files rewrite-rule leg, this time in the authorization layer):
- Network-scoped snippet activation controlled by a single-subsite admin (CVE-2026-106095 / GHSA-x27w-vpmw-g569): a snippet-management action skips the capability check and derives the network scope of the targeted snippet from the request instead of the stored record — a subsite Administrator (not a Super Admin) can activate, deactivate, and reprioritise snippets whose scope is network, i.e. code that runs on every site in the network. Axis: scope-selecting fields must be read from stored state, never from the caller, and on multisite an "activate a code snippet" verb whose scope is caller-declared converts subsite-admin into near-Super-Admin code execution (snippet content itself is limited by the plugin's own editor, but activation state + run-order is cross-site control; chain with any snippet-creation capability the subsite admin already holds for full network PHP execution).
- UNION-based SQLi against shared network tables from a subsite admin (CVE-2026-106097 / GHSA-6xh4-5cg9-76mq): snippet-migration import endpoints are gated by "site-administration capabilities" — which multisite grants to subsite Administrators — and interpolate a user-supplied parameter into SQL, reaching shared network tables (
wp_users,wp_blogs,wp_sitemeta): network-wide password-hash disclosure from a role the plugin author mentally modeled as single-site admin. Axis:manage_options-class caps mean "this subsite" to the author and "any subsite" to the attacker; import/migration endpoints are the least-audited verb family (this page's core invariant again — the import path enforces a different thing than the UI).
Durable multisite sweep rules for WP engagements: (1) determine topology first (wp-config WP_MULTISITE, wp_blogs shape via any SQLi, /wp-signup.php, path vs subdomain structure); (2) re-run every admin-gated plugin action from a subsite-admin account, not just subscriber/author — the subsite-admin↔Super-Admin boundary is systematically unmodeled in plugin code; (3) for every object with a scope field (network/site/blog ids), swap the scope value and diff — caller-selected scope is the bug; (4) import/migration/restore endpoints get their own probe pass. Validation boundary: lab multisite with your own subsite-admin + Super-Admin accounts, your own snippet rows and canary users; UNION proofs against your own tables only.
Same-wave tracked no-publish: Kirki <6.3.2 editor-gated SSRF host-check miss + comment-shortcode execution (known SSRF/shortcode classes, Sept 16 rmcp / Sept 19 shortcode pages), WP Rocket executable-regex + missing-auth singles, Redux Framework privilege escalation (class-covered), SendPress nonce-gated-no-capability pair, FooSales/UnitechPay/Social Web Suite/Ocean/AWP Classifieds/SKU Error Fixer singles (known broken-authz/XSS/archive-extraction classes), Linux kernel bpf/veth/netfilter wave (memory-safety, tracked per kernel precedent).
October 10 03:30–09:30Z follow-up: cross-plugin guard-disable filters, never-verifiable nonce formats, session-namespace injection, and admin-request-time email cookie capture (Oct 10 WP wave, folded)¶
Four legs from this wave add genuinely new axes; the rest fold onto canonical classes listed at the end.
- Blocksy Companion ≤2.1.58 unauthenticated seller/vendor self-registration + auto-login because the plugin actively disables another plugin's nonce guard and trusts
$_POST['role'](CVE-2026-107645 / GHSA-w5h9-m5j6-5394, critical 9.1): theimplement_user_registration()AJAX handler runsadd_filter('dokan_register_nonce_check', '__return_false')— killing Dokan's vendor-registration nonce from Blocksy's own code — then passes the attacker-suppliedrolestraight intowc_create_new_customer()andwc_set_customer_auth_cookie(). Unauthenticated attackers mint a Dokan seller account (publishing capabilities beyond a customer) and are auto-authenticated into it, even on sites where Dokan vendor signup is explicitly turned off. Three durable axes: (1) cross-plugin filter injection as guard removal — when auditing a registration/checkout AJAX handler, grep foradd_filter(calls that flip another plugin's*_nonce_check/*_enabled-style guard to false; the guard lives in the other plugin's code path, so the vendor who wrote the check never tested the site where a companion plugin disabled it; (2) client-supplied role at registration — anywc_create_new_customer()/wp_insert_user()call fed from$_POST['role']is the bug regardless of how benign the created role sounds; enumerate the effective capability delta of each accepted role value (seller/vendor/author all clear "customer"); (3) feature-flag bypass via handler ordering — "signup disabled" is enforced in Dokan's form/dispatch, not insidewc_create_new_customer(); alternate entrypoints that call the shared creator function skip the posture check entirely (this page's alternate-surface invariant again). Sweep: on any Woo stack with a companion plugin that claims to "handle registration for X", probe the companion's endpoint withrole=variants and diff created users + returned cookies; disposable sites only, enumerate your created accounts afterward. - WPO365 | Login ≤44.1 OIDC nonce replay through a validation primitive that can never succeed — and never aborts (CVE-2026-104759 / GHSA-p38j-g4g4-fm3g, high 8.1): the deprecated token parser validates a 64-char hex value produced by the plugin's own
Nonce_Service::create_nonce()using WordPress corewp_verify_nonce()— which only accepts 10/24-char WP nonces, so verification always fails — and the failed result does not terminate execution: auth continues intoauthenticate_oidc_user()with the attacker-suppliedid_token. Anyone holding a previously-issued validid_tokenfor a target account replays it to log in as that user, up to admin. Durable axes: (1) issuer/verifier format mismatch = fail-open validation — for every check that looks real in code, confirm the format the producer emits matches what the checker can accept; a check wired to a validator that structurally cannot return success is an absent check with a log line; (2) non-terminating validation failure — grep auth flows forif (wp_verify_nonce(...) || ...)and negated-continue shapes where a false result skips a branch instead ofwp_die()/return; build the decision table {valid, wrong, wrong-format, absent} × {accepts, rejects, falls-through}; (3) deprecated parser behind an option — the leg is gated onuse_id_token_parser_v2; when a plugin ships v1/v2 handlers, enumerate which option value routes to which, and fingerprint deployments by testing the broken-format leg (sibling of alternate-versioned-route families on the Keycloak page). Same plugin, same wave: stored XSS via thewpo365_errorstransient (CVE-2026-96765 / GHSA-87pp-p526-6wm7, 7.2) — a forgedid_tokenwhose base64url-decodedunique_name/issclaim contains HTML is stored unauthenticated in a 3-day transient and rendered on the admin wizard page. Axis: JWT claims are attacker-controlled strings that plugins happily persist as "error context" — run the mXSS/DOM marker battery through every claim value, then hunt where error transients/options survive to admin-rendered pages; unauthenticated → admin-sink stored XSS needs zero user interaction beyond an admin opening the settings page. - WPCOM Member ≤1.7.27 full auth bypass by writing attacker-named entries into another visitor's session namespace (CVE-2026-104803 / GHSA-gj32-ffvm-frj6, critical 9.8): the social-login callback on
inithas no nonce, no OAuth state verification, and no per-visitor isolation — a crafted GET writes an attacker-named/attacker-valued entry into the global session store by prepending an underscore to bypass the per-visitor prefix, a second GET firesweapp_new_user()which reads the forged entry and resolves the attacker-suppliedopenidto the bound WordPress account, andwp_set_auth_cookie()logs the attacker in as that user (admin included) if the victim's openid/unionid is known or enumerable. Durable axes: (1) session-store key namespace is an authorization boundary — any handler that writes session/option/transient entries with caller-influenced keys must confine keys to the caller's own prefix; test the underscore/global-escape, empty-prefix, and case variants; (2) two-request handshake attacks — social-login flows decompose into "store claim" + "consume claim" legs; each leg is individually reachable via GET (this page's GET-state-change invariant), and the consumer leg trusting the store is the whole bug; (3) provider identifier as credential — openid/unionid values minted by a shared provider (WeChat unionid is stable across every app of the same Open Platform account) can leak across ecosystems, so "requires knowing the victim's openid" is weaker than it reads; enumerate provider docs for identifier scope before accepting the advisory's precondition. Sweep: every social-login plugin's callback handler — check state/nonce presence, then probe the store/consume split with your own two disposable bound accounts. - Download Manager ≤3.3.71 suspension email compiled inside the admin's own request leaks the full
Cookieheader (session hijack) (CVE-2026-104735 / GHSA-qj64-pjpc-4f52): an admin clicking Suspend on the attacker's account causes the plugin to synchronously build and send the suspension email inside the admin's authenticated HTTP request, and the email template has access to the request's rawCookieheader includingwordpress_logged_in_*— delivered to the (attacker-controlled) suspended user's mailbox. Durable axis: admin-action-triggered emails are a request-context side channel — transactional emails generated synchronously during privileged requests can inherit request data (cookies, headers, signed URLs, TOTP material) as template variables; map every admin action that emails the subject user, and diff what the email body/headers actually contain against what the admin typed; the attacker picks the mailbox. The CVSS 4.3 undersells the outcome (full session hijack). Same plugin, same wave: stored XSS viaregurlthat fires only for logged-out visitors (CVE-2026-102401 / GHSA-mw8v-g8v9-8mxj) because the vulnerable template branch is gated!is_user_logged_in()— remember to verify stored-XSS payloads from both auth states; template-branch parity means the same stored payload has different victim populations than the tester assumes (your logged-in browser will show nothing). - Extensions For CF7 ≤3.4.5 unauthenticated arbitrary file upload:
sanitize_file_name()reconstitutes the blocked extension (CVE-2026-94589 / GHSA-fjhx-3cm7-whvf, critical 9.8): the signature field'svalidation_filter()does no extension/MIME/size validation, the upload directory has no PHP-execution guard, andshell.php-survives validation becausesanitize_file_name()strips the trailing-back toshell.php. Durable axis: sanitizers that delete characters can create the blocked string — for every deny-list extension check, test inputs where validation sees string A and the filesystem seessanitize(A): trailing/interior banned chars, double extensions,%-entities, and separator swaps; always re-read whatwp_handle_upload()actually stored, not what the filter rejected. Pair with the Oct 7 OMGF leg (server-layer decides executability). - Divi Plus ≤2.4.0 unauth arbitrary file read + SSRF via a REST endpoint whose
permission_callbackreturnstrueunconditionally (CVE-2026-91136 / GHSA-9676-xmxf-xp84, high 7.5):/wp-json/elicus/v1/dipl-modules/svg-animatorpipessvg_imageintofile_get_contents()(sofile://stream wrappers work) with awp_remote_get()fallback (so the same parameter is also an SSRF dial), and the raw body is reflected in the JSONhtmlfield. Axes:__return_truepermission callbacks remain the top REST sweep target (enumerateregister_rest_routesites and read the callback, don't fuzz the gate);sanitize_text_field()/esc_html()are not path controls — the escaping target and the sink target are different layers (Sept 19's validation-target-vs-write-target rule); one parameter crossing bothfile_get_contentsand the HTTP client is a two-bug finding — report both legs. LatePoint ≤5.7.3 latent admin-mint via settings write (CVE-2026-104766 / GHSA-h977-fppf-xgg7): asettings__edit-capable agent role can persistdefault_wp_role_for_customer = administratorbecause the role allowlist exists only in the UI dropdown — every subsequently self-registered customer is created as a WordPress admin. Axis: settings writes whose only allowlist is a<select>, and config-bombs: a setting that governs future account creation converts a low-priv settings write into standing privilege escalation; for every plugin with "default role for new X" settings, attempt the admin value via raw POST even when the dropdown forbids it (LatePoint is the second promoted leg on this page — same-product re-sweep rule). WebToffee PDF Invoices ≤5.0.2 guest document IDOR authorized by base64(billing email) (CVE-2026-93746 / GHSA-wjmp-g49r-xp3p): the guest print handler accepts an order's documents when the attacker-supplied base64urlemailequals the order's billing email — a non-secret identifier used as the authorization check where WooCommerce'sorder_keyexists for exactly this purpose. Axis: encoding is not a capability — grep handlers for authorization by equality against user-attacker-known attributes (email, phone, username) instead of issued secrets; base64/URL-encoding of the compare value is cosmetic. W3 Total Cache <2.10.6 deferred-import confused deputy (CVE-2026-107321 / GHSA-445r-vc94-h34g): an Author plants a media-import entry pointing at an arbitrary server-readable file; the file copy runs with the admin's privileges when the admin later runs the import, landing the file in a publicly served directory. Axis: deferred-action queues inherit the consumer's authority, not the enqueuer's — every "admin clicks import/process/export on data a lower role wrote" flow is a read primitive across the role boundary (same deferred-detonation shape as the Brocade config-relay and GeoDirectory trigger legs); enumerate import/queue tables for author-writable rows and watch what the admin run actually copies. - Same waves, tracked without publication on canonical axes: ~20-leg ThemeREX/Axiomthemes PHP object-injection 9.8 dump (Booklovers/Gutentype/Tantra/Convex/Greeny/Travesia/Partiso/Smash/IPharm/Dwell/Rosalinda/Hygia/Camelia/Edema/Hogwords/Smart Casa/Let's Play/Veto/Balance — one shared theme-framework sink advertised under dozens of slugs; vendor-family fingerprint rule: one ThemeREX deserialization sink = sweep all sibling slugs from a single harness; detail-free vuldb bodies, no new axis); WPCOM/WPO365 sibling XSS singles; WP File Download ≤6.3.9 subscriber delete/move/publish across managed files (GHSA-cqx4-48wp-q2rc missing-action-check canonical); Fluent Forms ≤6.2.5 custom-role submission read/delete/settings (GHSA-pmcp-c5j9-q942 same UI-role-vs-server-role shape as LatePoint, class-covered); Booking Package <1.7.30 unauth reservation disclosure + cancellation tokens (GHSA-hhf8-8xcf-35xv IDOR + capability-carrier canonical); Grocery Shopping Store ≤1.3.3 password-recovery alternate-path bypass (GHSA-ghj7-cmq3-785j detail-free); LatePoint ≤5.7.2
booking[service_id]SQLi (GHSA-mwwp-rchw-pr78 generic SQLi canonical); Motors ≤1.4.108 missing-authorization single; W3TC/ContGallery/W3-pair rate-limit singles; ZTE Z80 Ultra pack (local Android info-disclosure singles); ExifTool-for-Android shell-string build from unescaped single-quote in media path (GHSA-fwcv-c88q-m2pm shell-quote page class); Groundhogg pair, Feedzy, GeoDirectory, Filter Everything, Hello Plus, Post Export Import traversal, Academy LMS pair, CleanTalk, FireBox, FV Flowplayer, Bookly trio, Table Field ACF, Avada, HivePress, Retainful, VikBooking, Photo Reviews, LazyLoad, PostX, User Frontend, FunnelKit, QSM, Molongui, SiteOrigin traversal, 10Web Booster, WP Meteor, Premium Packages, Kirki re-listing, Divi Plus singles (stored/reflected XSS, enum, IDOR singles per precedent).
October 14 03:30Z follow-up: standalone-file bootstrap bypass (Snazzy Social Media Feed), per-branch allowlist divergence with anonymously-minted nonce (Printcart Store), and log-re-execution inside the admin's session (Bit integrations) (Oct 14 WP wave)¶
- Snazzy Social Media Feed ≤2.5 unauthenticated arbitrary file upload → RCE (CVE-2026-110002 / GHSA-cqr7-6967-pf9v, critical 9.8):
save-to-cache.phpis a standalone script in the plugin directory that never loads WordPress — no bootstrap, no auth, no capability, no nonce — and writes thepluginURLparameter intocached-content/cached-feed.phpwith onlyFILTER_SANITIZE_URLapplied, a filter that preserves every character needed for PHP source. Subscriber-equivalent (really: anonymous, the advisory's auth bar is fiction since the file is directly reachable) writes executable PHP to a web-served path, one GET to execute. Durable axes: (1) the standalone-file sweep is this page's core invariant restated as a grep —find wp-content/plugins/<x> -name '*.php'and test each one that doesn'trequire wp-load/ABSPATH-guard: any directly-reachable non-bootstrapped PHP file skips all of WP's auth model at once (siblings: the wiki'suninstall.phpand*-ajax.phphandler hunts); fuzz the reachable-file set, not justadmin-ajax.php/wp-json; (2) sanitizers chosen for a different purpose are not security controls — a URL sanitizer applied to content destined for a.phpfile is a category error; check the destination's execution semantics against the surviving character set. - Printcart Store (PCDesigner/NB Designer) ≤2.9.1 unauthenticated upload RCE via allowlist branch divergence + free nonce (CVE-2026-93388 / GHSA-p5c5-938c-g6r5, critical 9.8):
nbdesigner_copy_image_from_urlis registeredwp_ajax_nopriv_; the normal branch enforces a jpg/png/gif allowlist, but the Google Drive branch rebuilds the destination filename from rawgapi[name]— extension fully attacker-controlled — and writes fetched bytes towp-content/uploads/nbdesigner/temp/; the requiredsave-designnonce is handed to anonymous callers by a different endpoint (nbd_check_use_logged_in). Axes: (1) per-branch validation parity — when one handler has two content-source branches (direct upload vs remote fetch vs provider import), diff the validation applied by each; the imported/remote branch is almost always the lax one (same rule as the Oct 8 SAN data-collector and Crawlee legs); (2) nonce-mint sibling endpoint — Bricksforge'sregenerate_noncepattern generalized: before accepting a nonce gate, enumerate every response on the site (including logged-out pages and cheap AJAX actions) that embeds the target nonce action name; (3) filename-from-provider-metadata is filename-from-attacker — provider metadata fields are client-side data. - Bit integrations (btcpay-form suite) ≤2.10.6 privilege escalation via log re-execution (CVE-2026-104749 / GHSA-8mmw-9hhm-8rr4, high 7.5):
executeWithCapture()stores raw anonymous form fields — includinguser_pass— verbatim intowp_btcbi_log, and the admin-facingLogHandler::reexecute()replays the stored payload throughFlow::execute()inside the replaying administrator's session, soupdateUser()resolves the target to the admin and writes the attacker's password → unauth admin ATO, gated only on an admin clicking "Re-execute" on the failure row the attacker provoked. Axis: deferred-execution queues where the replayer's identity authorizes the enqueuer's payload — the third deferred-detonation leg this week (W3TC import, ONLYOFFICE runtime.json, this) and the WP-admin-panel flavor: every "retry/re-execute/replay" button over a log table an admin reviews is a payload-delivery channel from unauthenticated input; audit whether replay re-resolves the acting principal or inherits the viewer's. Report precondition honestly: requires a configured flow mapping a form field touser_pass. - Automatic Role Changer ≤20260828 revoked-admin resurrection (CVE-2026-104767 / GHSA-m963-j4qw-ph48, high 7.5):
remember_foreign_roles()merges externally-granted roles into a user-meta cache but never evicts revoked ones, andmaybe_add_role_to_user()unconditionally re-assigns every cached role on any role event → a subscriber who once legitimately held Administrator re-grants it to themselves by buying a role-mapped product. Axis: derived/permission-caching layers that only ever ADD — the MISP stale-correlation-snapshot and SmarterMail stale-role-refresh revocation-gap class applied to an identity mirror cache; revocation tests must cover: revoke upstream → trigger every downstream sync event → check re-grant. Preconditions (Replace Roles mode + prior legitimate admin) stated honestly; still a durable audit pattern for any SSO/HR-sync role bridge. - Tracked this wave without publication: Directorist ≤8.10.2.1 subscriber cross-owner listing delete via
wp_localize_script-exposed nonces (public-nonce class, Booking Package ≤1.7.29 PII dump same-class — both canonical nonce≠authorization); BP Profile Search ≤5.9 LFI viabps_directorycookie →bp_locate_template()with no basename check and aHTTP_REFERER-derived gate (referer-as-authorization canonical; cookie→template-path = known cookie-input-sink class); CFDB7 ≤1.4.1 unauth arbitraryunlink()via any stored field name containingcfdb7_file(substring-key-match deletion — substring-matching as authorization scoping worth watching, single leg); AnyComment ≤0.3.6 stored XSS via subscribe REST endpoint rendered whenuser_IDfails to resolve (conditional-render XSS canonical). Non-WP tracked: TeamPlus Messenger JS-injection→code-exec pair (Zellic, client-app warning-click chain, known class), Kasa local-protocol unauth management (IoT adjacent-network canonical), Hyland Desktop Host%PROGRAMDATA%weak-service-path LPE (canonical unquoted/weak-permission service dir), Hitachi disk-array driver trio (vendor boilerplate), Samsung QA-Board cmdi (detail-free), Unreal Engine LaunchURL empty-scheme-filter promoted on the Oct 14 document-server page.
October 14 06:31Z follow-up: decorative capability arguments on a non-bootstrapped standalone admin.php (AGE-GATE) and empty-string credential accepted as authenticated (JTL-Connector) (Oct 14 WP wave)¶
- AGE-GATE ≤1.0 unauthenticated RCE (CVE-2026-104800 / GHSA-rh8j-h273-hvj3, critical 9.8): the plugin ships its own standalone
admin.phpthat never loads WordPress and carries no ABSPATH guard — the "decorative capability arguments registered via the plugin's simulated menu API" are fiction; POSTwidth/heightland unsanitized in generated PHP source written to an included file. This is the second same-day instance of the Snazzysave-to-cache.phpaxis (03:30Z wave above): the standalone-file sweep is now a two-for-one on a four-hour window — enumerate every directly-reachable plugin PHP file that doesn't bootstrap WP, and treat any capability/menu registration claimed by such a file as unverified UI theater. Extension of that entry: AGE-GATE's file is literally namedadmin.phpinside the plugin directory, so path guessing via plugin README/zip listing (unauthenticated source is public) enumerates these endpoints pre-auth. - JTL-Connector for WooCommerce ≤2.4.3 arbitrary file upload → RCE (CVE-2026-87855 / GHSA-7mjr-phqx-8hgq, critical 9.8) + same-plugin time-based SQLi (CVE-2026-78193 / GHSA-38p9-rj5h-7qh4, high):
startSession()accepts an emptyjtlauthparameter as a valid credential, unlockingsaveImage()/handleImagePush()with insufficient file-type validation. Axis: empty-string-as-credential — the Oct 4 phpMyFAQ empty-default-API-token and hMailServer classes generalized from "default secret" to "absence of secret parses as success"; fuzz every auth-parameter gate with'', missing, and null before theorizing about the crypto. Same product carrying two critical/high legs = second JTL sweep precedent for the same-product re-sweep rule. - Tracked this wave without publication: the remaining Oct 14 06:31Z WP batch is sparse vuldb-style singles already class-covered on this page (Form Maker/Gmedia/Hyyan SQLi, NewStatPress/FV/Enfold/Countdown/PO-MO/Verified Reviews stored XSS, Avada info-exposure, Tutor LMS, Directorist re-listing).
October 14 09:31Z follow-up: unauth admin self-registration through a meta exclusion list that omits wp_capabilities (SimpleForm) and the page-gate token printed on the page it gates (WP Data Access pair) (Oct 14 WP wave)¶
- SimpleForm – Form Maker ≤3.10.0 unauthenticated Administrator creation (CVE-2026-104748 / GHSA-5hqh-jvjf-mpvw, critical 9.8):
wp_ajax_nopriv_simpleform_wp_registerhas no capability check on role assignment,save_custom_fields_to_user_meta()protects user meta with an exclusion list that omitswp_capabilities,get_form_settings()returns hard-coded defaults that unconditionally enable meta writing regardless of the suppliedform_id, and the required nonce is emitted into thefront_end_dataJS object on every front-end page. Payload = standard registration fields +wp_capabilities[administrator]=1. Durable axes: (1) deny-lists overwp_usermetakeys are a losing game —wp_capabilitiesmust be explicitly protected, and every registration/profile-save form that writes arbitrary submitted keys to user meta inherits this hole; probe rule: for any form-to-user-meta writer, submitwp_capabilities[administrator]=1(URL-encoded array syntax) and check the resulting user object; the add-only-role-cache page leg and the Oct 13 role self-grant legs are the cache flavor, this is the raw-meta flavor; (2) hard-coded defaults that fail open on unknown identifiers — passing a bogusform_idshould reject, but here it selects the "meta writing ON" default: the absent/unknown-input-selects-permissive-branch battery (SPIP absent-param, Enswitch absent-parameter) applied to a settings lookup; (3) the public-nonce class fires again — a nonce embedded viawp_localize_scriptis free, so pair-nonce-gate everywp_ajax_nopriv_*registration handler as if ungated. - WP Data Access two-leg authorization-drift pair: export-token-is-public — the database-table export action is gated only by a token the plugin itself prints into the public page displaying the table, letting unauthenticated callers export complete table contents including columns and rows withheld from the rendered display (CVE-2026-106099 / GHSA-mjx7-qq55-q3w6); and read-gate-on-write-verb — one application REST endpoint verifies only read permission before performing a write, so anonymous read-exposed applications accept updates even when the admin disabled update actions (CVE-2026-106098 / GHSA-v8fc-v39p-fp8c). Axes: (1) a gate credential displayed by the gated UI is not a gate — before treating any page-embedded token (export tokens, edit tokens, preview keys) as an access control, check whether your current unauthenticated response already contains it (same root family as the public-nonce and
wp_localize_scriptexposures — the renderer leaks the capability it's about to check); the row/column-masking angle means the export verb must be tested for field-level parity with the render, not just row parity; (2) verb-gate drift on public data-application endpoints — this page's Payload/ESO read-vs-write rule again: enumerate each HTTP verb on anonymous-readable data-app routes and diff which permission each checks; the read path being open makes the write path look intended. - Tracked this wave without publication: the rest of the 09:31Z WP batch is sparse vuldb-style singles class-covered here (LifterLMS/GeoDirectory/Mang Board/Complianz XSS-LFI singles tracked on their class pages, LatePoint legs folded on the pretix placeholder page this run); Sunshine Photo Cart ≤3.7.4 comment write into restricted galleries while the display path checks access (CVE-2026-108069 — display-gate/write-gate asymmetry noted, thin single); CodeAtoZ B2B guest-quote enumerable-ID reads (guest-ID sweep canonical); File Upload to Order unauth arbitrary
unlink()via session-derived path (unauth file-delete class); Restricted Site Access missing-authorization sparse single; Koha legs folded on the query-grammar page and shell-quote page this run. Non-WP tracked: curl HTTP/2RST_STREAMupload client-side UAF CVE-2026-92392 (client library, watch for client-target angle), unnamed critical T-SQL body-parameter SQLi CVE-2026-89310 (9.9) and "Vulnerability in tbc" CVE-2026-89309 (10.0 boilerplate, no product identified — revisit on enrichment), unnamed invoicing-PDF missing-authz + search-engine-indexed paths CVE-2026-14851 (product unnamed; the indexed-invoice-path recon axis is already page canon), Trellix Agent 5.8.4 hardcoded key inmfecryptc.dll→ local DB decryption (local detail-free), EC-CUBE AmazonPay order-flow open redirect (known redirect class), HPE NonStop iTP WebServer memory corruption (detail-free), Tronclass grading-anonymity IDOR pair (privacy-restore IDOR class).
October 14 11:1xZ follow-up: universal route_call AJAX dispatcher exposes internal handlers as anonymous IDOR verbs (LatePoint) (Oct 14 WP wave)¶
- LatePoint ≤5.7.3 unauthenticated booking/cart IDOR (CVE-2026-103366 / GHSA-3pf7-5297-2ffv, high): the public
wp_ajax_nopriv_latepoint_route_calldispatcher routes to internal helpers by name, andsteps__reload_booking_form_summary_panel/steps__startfeed rawbooking[id]/cart[id]intoset_booking_object()/set_cart_object()→OsModel::set_data()loads an arbitrary row by primary key with no ownership check — the ownership guard the vendor added after the earlier disclosure was placed at one entry point, not at the model loader the dispatcher shares. Axes: (1) generic route-dispatcher AJAX endpoints multiply the audit surface — onenoprivaction that names internal routes means every internal step-helper is a candidate public verb; enumerate dispatcher route names from plugin JS/source and probe each for object-ID parameters; (2) per-entry-point ownership guards fail on the shared loader — when a vendor patches "the disclosed endpoint," test sibling dispatcher routes that reach the sameset_data()-style loader; the patched-twin differential applies to route families, not just CVE twins. LatePoint auth-bypass leg from the same wave folded on the auth-precedence page; placeholder-expansion legs remain canonical on the pretix page.