
Advisory and proof-of-concept for CVE-2026-92424, a Contributor-level stored XSS in the Content Egg WordPress plugin that impersonates an admin before inserting posts.
Researcher: [email protected] (aymen benlamari)
Plugin: Content Egg – Affiliate Product Importer & Price Comparison (keywordrush.com)
Affected versions: < 11.9.0 (vulnerable through 11.8.x; tested on 11.4.0)
Fixed in: 11.9.0
Type: Stored Cross-Site Scripting (XSS)
CVE: CVE-2026-92424
WPVDB entry: WPVDB ID 917b8042-b38c-4b6a-9237-1dd08ada157d
Published (WPScan): 2026-09-28
Status: Public — Verified by WPScan — Fixed upstream in 11.9.0
Content Egg lets a site administrator build Import Presets — reusable templates that turn scraped/affiliate product data into WordPress posts, with a configurable author and publish/draft status. Presets are used by a bulk Import Queue that any search/import screen can enqueue jobs into.
The AJAX action that enqueues a job — cegg_import_enqueue — requires only
edit_posts, the lowest capability WordPress grants to any role that can author
content (Contributor and above). It accepts a client-supplied JSON payload
describing the "product" to import, and that payload is later substituted
verbatim into the post title/body via template placeholders
(%PRODUCT.title%, %PRODUCT.description%, …) with no output escaping
anywhere in the substitution path.
Immediately before inserting the post, the plugin calls
wp_set_current_user($preset['author_id']), switching the active WordPress
user to whoever the admin configured as the preset author (usually an
Administrator), and only then calls wp_insert_post(). WordPress's HTML
sanitizer (wp_kses) on post_title/post_content is bypassed based on the
current user's unfiltered_html capability — which Administrators have by
default on non-multisite installs. The plugin therefore borrows a privileged
user's sanitization exemption to store the attacker's raw <script> payload
unfiltered.
Net effect: a Contributor — a role routinely handed out on multi-author blogs, guest-post sites, and WooCommerce vendor setups — can plant a live Stored XSS payload, misattributed to the site's Administrator, using nothing more than their own normal "Add New Post" screen and one AJAX call.
The plugin does not verify that a user running its bulk content-import feature is authorized for the import preset they select, and switches to the preset author's identity before creating the resulting post, allowing users with contributor-level access and above to store arbitrary web scripts unfiltered under a privileged user's account, executing in the context of anyone who later views that content.
— WPScan, WPVDB ID
917b8042-b38c-4b6a-9237-1dd08ada157d
| Field | Value |
|---|---|
| CVE | CVE-2026-92424 |
| WPVDB ID | 917b8042-b38c-4b6a-9237-1dd08ada157d |
| Type | XSS (Stored) |
| CWE | CWE-79 |
| OWASP Top 10 (2017) | A7: Cross-Site Scripting (XSS) |
| CVSS 3.1 | AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H — 6.8 Medium (WPScan scoring) |
| Affects | content-egg |
| Fixed in | 11.9.0 |
| Verified | Yes |
| Original Researcher | aymen benlamari |
| Published / Added / Updated | 2026-09-28 |
Contributor (edit_posts)
│ 1. harvests cegg_import nonce from own post-new.php UI
▼
POST /wp-admin/admin-ajax.php action=cegg_import_enqueue
│ 2. chooses ANY admin-created preset_id + free-form JSON payload []
▼
ImportQueueApi::handle_enqueue() app/admin/import/ImportQueueApi.php:65 (edit_posts only)
│ 3. worker loads preset + application/admin/import/ProductImportService.php:809-815
│ payload substituted verbatim: ProductHelper::replaceImportPatterns() (strtr, no escaping)
▼
wp_set_current_user($preset['author_id']); ProductImportService.php:854 ← privilege borrowing
wp_insert_post($postArr, true); ProductImportService.php:855 ← wp_kses runs WITH admin's
unfiltered_html → payload stored raw
▼
Stored XSS, authored as Administrator (draft → fires in wp-admin when admin previews;
publish → fires for anonymous visitors)
wp_set_current_user() impersonation step. See
weakness.md.| File | Contents |
|---|---|
README.md | This overview: advisory metadata, chain summary, remediation, references |
weakness.md | Weakness classification — CWE/OWASP, CVSS 3.1 breakdown, scoring comparison with the original submission, CVSS v4 consideration |
prerequisites.md | Exact preconditions for the published PoC, configuration notes, and capability facts |
poc_commands.md | Step-by-step PoC commands (curl) matching the published advisory, plus lab runner commands |
Related material elsewhere in this workspace:
reports/stored_xss/REPORT.mdlabs/xss-privesc/findings/README.mdPreconditions: Content Egg active; at least one Import Preset authored by an administrator (the default when an admin creates a preset) with Post Status set to Draft; a contributor-level account.
As an administrator, create an Import Preset
(Content Egg → Import Tools → Presets → Add New): Post Status = Draft,
Post Title Template = %PRODUCT.title%,
Post Body Template = %PRODUCT.description%. Note the preset id.
Log in as the contributor. Open Posts → Add New. The Content Egg
metabox is present, and the page exposes the plugin's import nonce as
contentegg_params.importNonce in its inline JavaScript.
As the contributor, send the enqueue request reusing that nonce, with a
product payload whose title and description carry a <script> tag:
POST /wp-admin/admin-ajax.php
action=cegg_import_enqueue
nonce=<importNonce from step 2>
preset_id=<admin preset id from step 1>
module_id=<any active module id>
payload=[{"unique_id":"poc-1","title":"<script>alert(document.domain)</script>","description":"<script>alert(document.domain)</script>"}]