CVE Watch

LiteSpeed Cache CVE-2026-116763: Two Stored XSS Bugs, Two Settings to Check

· 7 min read
A browser window squeezed in a metal press, with a row of blue percent-sign tokens; one amber token is cracked open and a dotted amber line runs from it to an image icon

If you run the LiteSpeed Cache plugin, make sure it’s on 7.9.2. Then check two settings, HTML Minify and Viewport Images, because they decide whether these bugs could ever have reached you. Two stored cross-site scripting flaws in LiteSpeed Cache were published on October 29. One lets an anonymous commenter plant script on your pages. The other lets any logged-in Subscriber do the same through a forum reply. Both need a specific setup, both are fixed in 7.9.2 (released October 20), and neither is being exploited. With more than 7 million installs, though, a narrow setup still covers a lot of sites.

At a glance

CVEsCVE-2026-116763, CVE-2026-109215
PluginLiteSpeed Cache for WordPress (more than 7,000,000 active installs). This is the plugin, not the LiteSpeed web server
TypeStored cross-site scripting: unauthenticated via comments (116763), Subscriber and above via image markup (109215)
SeverityCVSS 5.4 (116763, high attack complexity) and 6.4 (109215)
Affected7.9.1 and earlier
Fixed7.9.2, released October 20, 2026
StatusNo exploitation reported. CISA’s enrichment of both records says “none”

Plugin, not server

Read the product name carefully. “LiteSpeed” is also a web server, and plenty of hosts run it. These CVEs are in the WordPress plugin, the one in your Plugins screen, and the fix is a plugin update. Your host patching its servers changes nothing here, and a site on Apache or Nginx with the plugin installed is still in scope. Getting this wrong sends the ticket to the wrong team.

What’s wrong

CVE-2026-116763: the minifier’s placeholders

When HTML Minify is on, the plugin strips whitespace from every page before caching it. It has to leave some parts alone, such as scripts, styles, <pre> and <textarea> blocks, so it lifts each one out, leaves a placeholder token in its place, minifies the rest and then puts the originals back.

In 7.9.1 that token was built from md5($_SERVER['REQUEST_TIME']), the hash of the current second. Anyone can work that out, and the response’s own Date header gives it away. The tokens were also swapped back in two passes, so a token that appeared inside restored content got replaced a second time. Put together:

  1. An attacker posts a comment that contains guessed placeholder tokens.
  2. Another plugin prints that comment inside a script block on the same page. A JSON-LD structured data block is the usual example.
  3. The minifier lifts that script block out along with the comment in it, then restores it.
  4. The second pass replaces the attacker’s tokens with other captured content, which breaks the markup open, and the attacker’s script runs for every visitor to that cached page.

The record is specific about what has to line up: HTML Minify enabled, the comment stored and shown on the page, and a companion plugin that renders comment content into a raw-text context such as JSON-LD. That’s why it’s scored as high complexity. Reported by Santh.

7.9.2 now builds the token from random_bytes(16) and restores everything in a single pass with strtr(), so injected tokens are never expanded. The changelog describes it as “protected HTML boundaries during resource rewriting, insertion and minification.”

CVE-2026-109215: Viewport Images

Viewport Images (VPI) is a page-speed feature that finds the images visible on first load and adjusts their markup. In 7.9.1, rewriting those image tags didn’t escape attributes properly. A user who can post raw <img> tags could get script into the rewritten markup. The record names the setup: VPI enabled by an administrator, with a configured VPI filename that matches an image the attacker can embed. The example given is a bbPress forum where Subscriber-level Participants can post replies containing image tags. Reported by Adrien Brunner.

Are you exposed?

The plugin version tells you whether you have the vulnerable code. Your configuration tells you whether the attack can reach it. You need both answers.

  • Is HTML Minify on? LiteSpeed Cache → Page Optimization → HTML Settings. It’s off by default, but optimization presets and “score” guides often turn it on.
  • Are comments really off, or just not visibly on? “We turned comments off years ago” usually means the default for new posts. Older posts keep their own setting, and some themes simply hide the form while the endpoint still accepts submissions.
  • Does anything print comment content into structured data? Schema and SEO plugins can include comments in JSON-LD (as a Comment or DiscussionForumPosting). Most teams don’t know which fields theirs pulls in. View the source of a post that has comments and search the application/ld+json blocks for comment text.
  • Is Viewport Images on, and can untrusted users post images? LiteSpeed Cache → Page Optimization → Media Settings. Then think about bbPress, BuddyPress, membership or support plugins that let low-level accounts post HTML.

What to do

1. Update

wp plugin list --name=litespeed-cache --fields=name,status,version

Anything below 7.9.2 is vulnerable. 7.9.2 is a large release that also touches caching, object cache and ESI, so if you stage updates, test logged-in pages and cart or checkout pages before you push it to production. Once it’s live, purge the cache: cached pages were minified by the old code.

wp litespeed-purge all

2. Check your settings

wp litespeed-option get optm-html_min
wp litespeed-option get media-vpi

A 1 means the feature is on. If you can’t update today, turning both off removes the attack paths, at some cost to page speed.

And check your comment exposure:

wp option get default_comment_status
wp post list --post_type=any --post_status=publish --fields=ID,comment_status --format=csv | grep -c ',open$'

If the first says closed and the second isn’t 0, comments are still open on older content. Settings → Discussion has an option to close comments on posts older than a set number of days, which handles that cleanly.

3. Check whether you were hit

Nobody has reported attacks. If you had the vulnerable setup:

  • Comments: search approved comments for the placeholder prefix or for script fragments: wp comment list --status=approve --search=MINIFYHTML --fields=comment_ID,comment_post_ID,comment_author, then the same with --search="<script".
  • Forum replies: on bbPress, look for <img tags with odd attributes in replies from Subscriber-level accounts.
  • Pages: view the source of a few cached pages that have comments, and look for script you didn’t put there.
  • Users: stored XSS is usually aimed at an administrator’s browser. Check for administrator accounts you didn’t create.

Where sources disagree

  • Fixed version. Both records say “up to and including 7.9.1” without naming a fix. The 7.9.2 code and changelog show both fixes, and the record for 109215 links the patched 7.9.2 file.
  • Timeline. The record for 116763 says the vendor was notified October 26, six days after 7.9.2 shipped. The minifier rewrite in 7.9.2 closed it anyway, so it looks like a known weak spot that was hardened before this particular attack was reported.
  • Severity. The unauthenticated bug scores lower (5.4) than the one that needs a login (6.4), because it needs a third-party plugin to line up. Don’t read the lower number as the less interesting bug.

The bigger lesson

A hash of the time is not a secret. md5(time()), md5($_SERVER['REQUEST_TIME']) and uniqid() all look random and are all guessable. Any token that separates your markup from someone else’s content has to be something an attacker can’t predict, and it has to be replaced exactly once.

The other lesson is about exposure assessments. “We don’t use comments” and “we don’t run a forum” are assumptions, and these two bugs only matter to sites where an assumption like that is wrong. A performance plugin rewrites every page your site serves, which makes it one of the most powerful pieces of code on the site. It earns its place by being configured deliberately: turn on the optimizations you’ve measured, and know which ones are on. Patch the plugin, then test the assumptions. Neither replaces the other.

Sources