Docker validation lab and Python PoC for CVE-2026-89274, proving arbitrary shortcode execution in WP Recipe Maker <= 10.8.1 via rating-comment reviewBody.
Security-research material for reproducing and validating CVE-2026-89274: WP Recipe Maker <= 10.8.1 executes arbitrary registered WordPress shortcodes inside recipe JSON-LD metadata, planted via an approved rating comment.
do_shortcode_safe)AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) - CWE-94Use only on systems you own or are explicitly authorized to test. The PoC posts one rating comment containing a lab-registered marker shortcode. No destructive shortcode is used and nothing is written to disk.
Status: proven in the supplied lab.
WP Recipe Maker 10.8.1 (WordPress 7.1.2, default recipe-card model):
comment posted : "[cve89274]" + wprm-comment-rating=5 on the recipe's parent post
result : JSON-LD on the rendered page contains
"reviewBody":"CVE89274-SHORTCODE-FIRED"
(marker shortcode executed server-side at render time)
10.8.2 : shortcode stripped, marker absent (patched negative control)
This demonstrates server-side execution of an arbitrary registered shortcode with output embedded in the page metadata, disclosed to every visitor. It does not demonstrate RCE; impact depends on which shortcodes are registered on the target (post/meta exposure, plugin-provided shortcodes with side effects).
WPRM_Metadata::sanitize_metadata() recursively sanitizes every scalar
field of the recipe's structured metadata array:
// includes/public/class-wprm-metadata.php:553, WP Recipe Maker 10.8.1
$sanitized = strip_shortcodes( wp_strip_all_tags( do_shortcode( $metadata ) ) );
// 10.8.2
$sanitized = strip_shortcodes( wp_strip_all_tags( WPRM_Instacart::do_shortcode_safe( $metadata ) ) );
do_shortcode() executes the shortcode first; the strip_shortcodes()
wrapper only removes tags from the already-executed output. One of the
fields, reviewBody, is populated verbatim from comment_content of
approved comments carrying a wprm-comment-rating meta value
(class-wprm-metadata.php:1016), queried against the recipe's parent
post (post_id => $recipe->parent_post_id(), line 973) - the post or page
where the recipe card is embedded.
The 10.8.2 replacement, WPRM_Instacart::do_shortcode_safe()
(class-wprm-instacart.php:501), keeps only the inner content and executes
nothing.
[wprm-recipe])./wp-comments-post.php with the shortcode in the body
and wprm-comment-rating=5.review array, and
sanitize_metadata() executes the shortcode. Its output replaces the
comment text inside reviewBody, visible to every visitor.Requirements: Docker with Compose v2, Python 3.10+ on the host.
./lab verify
Builds the 10.8.1 lab, requires the marker to fire, then builds the 10.8.2
lab and requires its absence. Remove everything with ./lab reset.
Vulnerable control:
[!!!] SHORTCODE EXECUTION CONFIRMED
evidence: 'CVE89274-SHORTCODE-FIRED' present in the rendered recipe page
context : ..."reviewBody":"CVE89274-SHORTCODE-FIRED","author":...
RESULT: VULNERABLE CONTROL CONFIRMED
Patched control:
[=] shortcode text present but NOT executed: output was stripped
RESULT: PATCHED CONTROL CONFIRMED
A marker in the page only inside the displayed comment (raw text) is not accepted as proof; the marker must appear as executed output in the metadata.
./lab start vulnerable # 10.8.1 lab on http://127.0.0.1:9482
./lab test # assert marker fires
./lab start patched # 10.8.2 lab
./lab test # assert absence
./lab status
./lab reset
Run the PoC by hand (loopback default; authorized external targets need the explicit override):
python3 poc/poc_cve_2026_89274.py --url http://127.0.0.1:9482 --post-id 5
python3 poc/poc_cve_2026_89274.py --url https://target.example \
--allow-authorized-non-loopback [--insecure]
The nuclei template (nuclei/cve-2026-89274.yaml) is a version screen only
(readme.txt stable tag <= 10.8.1). The behavioral chain requires comment
submission plus an approval transition, which is not practical to express in
nuclei; the Python PoC performs the full flow.
| Version | Assessment |
|---|---|
| WP Recipe Maker 10.8.1 | Shortcode execution reproduced end to end |
| WP Recipe Maker 10.8.2 | Patched negative control reproduced |
| WP Recipe Maker <= 10.8.0 | Affected per advisory; not individually tested |
.
├── .github/workflows/validate.yml
├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── docker-compose.yml
├── lab
├── nuclei/
│ └── cve-2026-89274.yaml
└── poc/
└── poc_cve_2026_89274.py
Not committed: assets/ (plugin zips), .lab-state, __pycache__/.
| Claim | Status |
|---|---|
| Shortcode execution via rating comment reviewBody | Proven (10.8.1) |
| Execution output disclosed in rendered JSON-LD | Proven |
| Patched negative control (10.8.2) | Proven |
| Default configuration (private recipe CPT + parent post) | Proven (this is the lab topology) |
| Public recipe post type configuration | Not tested |
| WPRM's own rating REST endpoint as alternative entry | Not tested |
| Impact beyond registered shortcode output (RCE) | Not claimed |
Run this repository only on systems you own or are explicitly authorized to
test. The lab driver refuses variants other than vulnerable/patched, the
PoC refuses non-loopback targets unless --allow-authorized-non-loopback is
supplied, and the Compose port is bound to 127.0.0.1.
See SECURITY.md for disclosure handling. This repository does not include production targets, customer data, access tokens, or evidence from real systems.
MIT - see LICENSE.