Research · 8 min read

WordPress LFI Bugs: Two Plugins, Same Shortcode Trick

By WP Vanguard Team

WordPress LFI Bugs: Two Plugins, Same Shortcode Trick

Two unrelated WordPress plugins disclosed local file inclusion bugs within a day of each other this week, and both trace back to the same design mistake: a shortcode attribute gets handed straight to a file-loading function with no check on where it's allowed to point.

33

high or critical local file inclusion disclosures in 30 plugins in the last 90 days

WP Vanguard database, July 5 to October 4, 2026. 18 of them need no login.

HUSKY, the WooCommerce Products Filter plugin, shipped the worse of the two. Its version is unauthenticated. WP Travel Engine's needs contributor-level access first. Different severity, same root cause, worth covering together because the pattern is what's actually reusable here, not either individual CVE.

HUSKY's CVE-2026-92969: No Login Required

HUSKY (WooCommerce Products Filter Professional) versions up to and including 1.4.4 pass a shortcode parameter into the woof_draw_products AJAX handler, which resolves a custom_tpl template attribute and includes whatever file path comes out the other end. Wordfence rates it CVSS 8.1, high severity.

What makes this one unauthenticated in practice, not just in theory: the only gate in front of the AJAX action is a nonce check against woof_front_nonce. That nonce isn't a secret. It's emitted into inline JavaScript on every front-end page the plugin runs on, which means any site visitor can read it straight out of the page source and use it to call the vulnerable endpoint. A nonce check that's satisfied by a value the page hands out for free isn't an access control.

The fix ships in 1.4.5. If you're running HUSKY, this is a same-day update, not a scheduled-maintenance one.

WP Travel Engine's CVE-2026-9231: Contributor and Above

WP Travel Engine, the tour booking plugin, has a similar bug in wte_get_template, reached through a template shortcode attribute. Versions through 6.8.0 are affected, fixed in 6.8.1. Wordfence scores it CVSS 7.5.

The access bar is higher here: it needs an authenticated account with contributor-level permissions or above. That's still a meaningfully low bar on any site that accepts guest posts, has a membership tier, or runs a multi-author blog, since contributor is usually the first role above subscriber. A compromised contributor account, or a contributor account created through a separate vulnerability, gets a path to arbitrary PHP file inclusion it shouldn't have.

The Shared Root Cause: Shortcode Attributes as File Paths

Both bugs work the same way at the code level. A shortcode attribute value, something meant to be a short string like a template name or a filter type, gets passed into a function that resolves it to a filesystem path and then includes that file. Neither plugin validated that the resolved path stayed inside its own template directory before calling include or require.

This is the same class of mistake as classic path traversal, just reached through a shortcode instead of a URL parameter or a form field. Shortcode attributes feel like internal, developer-controlled values because they usually appear inside [shortcode attr="value"] markup written by a trusted editor. Both of these plugins expose the same attribute-to-file-path pipeline through an AJAX action or public function, where the value comes from an untrusted request instead of trusted editorial content. The lesson generalizes past these two plugins: any function that turns a shortcode, filter, or template attribute into a file path needs to validate the result stays inside an allowed directory, regardless of how the attribute normally gets set.

We've covered the same mechanics before from a file-deletion angle in our Formidable Signatures writeup: a parameter that should be a filename accepted a path instead, with no containment check.

A Pattern, Not a Pair

We went back through our database to see whether these two were unusual. They aren't. Between July 5 and October 4 we logged 33 high or critical local file inclusion disclosures across 30 plugins, out of 1,206 high or critical plugin disclosures in the same window. More than half need no login.

Local file inclusion disclosures by access level
  • No login18
  • Contributor+8
  • Subscriber+4
  • Other roles3

High or critical, plugins, July 5 to October 4, 2026.

Ten of those 33 advisory titles name the input that carries the file path. Most are a template, layout or page parameter, the same shape as HUSKY and WP Travel Engine. The table lists the nine plugins involved, since Eventin appears twice:

Plugin Parameter Login needed CVSS Fixed in
Visual Composer vcv-template None 9.8 45.16.1
Divi Ajax Filter custom_loop_template None 9.8 5.1.3
HUSKY custom_tpl None 8.1 1.4.5
WP Travel Engine template Contributor 7.5 6.8.1
SiteOrigin Widgets Bundle theme Contributor 7.5 1.74.0
Events Made Easy wp_page_template Contributor 7.5 3.2.6
Eventin event_layout Contributor 7.5 4.1.23
WP Maps page Subscriber 7.5 5.0.0
Simply Schedule Appointments ssa_locale Subscriber 7.5 1.6.12.33

Key finding

Any plugin that turns a request value into a file path is one missing check away from this bug, and nonces, role checks and shortcode context don't make the value safe.

The two top rows score 9.8. That score isn't unusual for an unauthenticated include. It reflects how little an attacker needs to do, not what the file ends up doing, and that part depends on the site.

What an Include Bug Can Lead To

Local file inclusion sounds like a read bug, but include runs PHP. If the file an attacker points at is a PHP file that does something when loaded, that something happens, with the site's full permissions. Whether it becomes remote code execution depends on what's reachable: a file the attacker can upload, a log they can write into, or a script in another plugin that does something dangerous on its own.

None of the 33 advisories we checked use the words remote code execution in their titles, which is a fair framing, because the outcome is conditional. It's still the same bug class as the WordPress core flaw behind CVE-2026-87902, where a template path could be steered to a file outside the theme and attackers were exploiting it within hours. Treat an unauthenticated include as serious even when the advisory stops at "high".

What Plugin Authors Should Change

The fix is the same in every plugin on the list, and it's small. Don't let a request choose a file path at all. Map the value to a fixed list of templates you ship, and fall back to a default when it isn't on the list.

$templates = array(
	'grid' => 'templates/grid.php',
	'list' => 'templates/list.php',
);

$key  = isset( $atts['template'] ) ? sanitize_key( $atts['template'] ) : 'grid';
$file = $templates[ $key ] ?? $templates['grid'];

include plugin_dir_path( __FILE__ ) . $file;

If templates really have to be discovered on disk, resolve the final path with realpath() and refuse it unless it starts with the plugin's own template directory. Strip directory separators and dots before that check, because a value like ../../wp-config is the whole attack.

Two habits prevent most of these. Treat shortcode attributes as untrusted whenever the same code can be reached from an AJAX action or a REST route, since that's where HUSKY went wrong. And don't count a nonce as access control when the page hands it to every visitor.

How to Check and Fix

Confirm your installed versions:

wp plugin list --name=woocommerce-products-filter --field=version
wp plugin list --name=wp-travel-engine --field=version

HUSKY below 1.4.5 or WP Travel Engine below 6.8.1 means you're exposed. Update immediately:

wp plugin update woocommerce-products-filter
wp plugin update wp-travel-engine

If either plugin can't be updated right away, deactivating it removes the exposure entirely since both bugs require the plugin's own AJAX action or shortcode handler to be reachable. For a broader check across everything installed, our guide on checking WordPress plugins for known vulnerabilities walks through the WP-CLI routine we run internally, and WP Vanguard's free surface scan checks your live site against both CVEs and the rest of our database in under a minute.

Looking for signs of probing

If you ran a vulnerable version on a public site, your access logs can tell you whether anyone tried it. Search for requests to admin-ajax.php or the plugin's pages where a parameter named like the ones in the table contains ../, ..%2f or %2e%2e. A value that looks like a path, not a template name, is a probe.

A probe in the log doesn't mean it worked, and a clean log doesn't prove it didn't, since many hosts keep only a few days. Export what you have before it rolls off. If you find probes, check the uploads folder for PHP files and list your administrators, using our guide to the signs of a hacked site.

References

wordpress-lfi-vulnerability local-file-inclusion shortcode-attribute-injection husky-woocommerce-products-filter wp-travel-engine

Related reading

Check Your WordPress Site Security

Free scan, no login required. Find vulnerabilities before attackers do.

Scan Your Site Free

Get weekly WordPress security tips

Vulnerability alerts, plugin updates, and security guides. No spam. Unsubscribe any time.

WP Vanguard is built by Wbcom Designs, makers of Reign, Jetonomy, Listora, and more. Explore our WordPress products →
← Back to Blog