Back to Blog
Insights

CVE-2026-78159: Comment-to-RCE in The Events Calendar

CVE-2026-78159: Comment-to-RCE in The Events Calendar

The Events Calendar by StellarWP runs on more than 600,000 WordPress sites. Until this month, on any of them that allowed comments on events, a complete stranger could turn a comment into remote code execution on the server.

CVE-2026-78159 is a CVSS 9.8 flaw requiring no login, no form, and no API route, turning a comment into remote code execution on sites running The Events Calendar 6.17.3 or older, a StellarWP plugin on more than 600,000 WordPress sites. The payload arrives as something most site owners treat as noise to be moderated, and the site fires it itself the next time anyone opens the event page. Here is how a comment becomes a shell, and the precondition that decides whether you were ever exposed at all.

What it is

FieldValue
CVECVE-2026-78159
CVSS v3.19.8 Critical: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS0.8% at time of writing: the model is three days behind the disclosure
ClassCWE-94: code injection via callable invocation
AuthenticationNone. Public comment submission
AffectedThe Events Calendar ≤ 6.17.3
Fixed inThe 6.17.4 line: update to 6.17.4.1
Reported byChloe Chamberland and Wordfence Argus
RecordNVD: CVE-2026-78159 · CVE.org

Are you affected?

Three preconditions have to line up, and each one cuts the exposure down:

  1. The Events Calendar at 6.17.3 or older. Six hundred thousand sites start here.
  2. Comments enabled on tribe_events posts. This is the one that matters. The events post type is a custom post type, and comments are disabled on it unless someone explicitly turned them on. A large share of event sites never did. A calendar is not a blog, and most operators never thought about comments on an event listing. If comments were never opened on your events, this chain has no entrance.
  3. A crafted comment sitting in the rendered comment area. The comment has to actually display, which in a default WordPress configuration means it was approved, or moderation was off.

So the honest exposure number is well below 600,000. But read precondition three carefully, because it contains the part most write-ups are missing: the comment only has to exist and be visible. Nobody has to click anything, and the attacker does not even need a victim. They can load the event page themselves to light the fuse.

How the bug works

The chain runs through three pieces that were never meant to meet: WordPress block parsing, the plugin's widget rendering layer, and a CSS-classes utility that supports callables.

The sink: a classes map that can hold callables

The Events Calendar builds CSS class lists for its elements through an internal utility: Element_Classes::parse_array(). By design, an entry in that map can be more than a string: it can be a callable, invoked at render time to compute classes dynamically. That is a legitimate feature for internal template code.

It is also a loaded weapon pointed at whichever data ends up in the map. If attacker-controlled content defines the classes map, the callable is not computing CSS classes. It is call_user_func with the attacker's function and arguments.

The guard: an object check at the wrong boundary

Between unauthenticated input and that sink sits is_safe_widget_instance(): a validation meant to confirm that a widget instance is a genuine, safe widget object. The problem: a plain-array payload gets past it. The check was written for the plugin's internal render path, where instances arrive as real objects. Data shaped differently than expected (an array from parsed block attributes) is not rejected, and the classes map reaches parse_array() unrevalidated.

The delivery: a block inside a comment

Now the piece that turns this from a plugin bug into something genuinely interesting. WordPress runs do_blocks() over rendered post HTML. On a single-event page, that processing covers the whole rendered HTML, including the comment area. Comments are supposed to be inert text. But do_blocks() does not know that, and a comment containing a wp:legacy-widget block is parsed as markup: the attacker has just placed a live widget render, with a fully attacker-controlled instance array, into the page.

The widget layer builds that instance, the classes map inside it reaches parse_array(), and the callable fires.

Attacker submits comment on a tribe_events post
        │
        └── comment body contains a crafted wp:legacy-widget block
        │
        ▼
Anyone loads the single-event page  (the attacker can do this themselves)
        │
        ▼
do_blocks() parses the full event HTML, comment area included
        │
        ├── wp:legacy-widget block ──► widget render with attacker-controlled instance
        │
        ▼
is_safe_widget_instance()  ──► plain-array payload slips past
        │
        ▼
Element_Classes::parse_array()  ──► 'classes' map holds a callable
        │
        ▼
Callable invoked ──► code execution as the web-server user

The exploit request and the trigger are two different, ordinary requests. Delivery looks like a comment-post. Detonation looks like a page view. There is nothing loud anywhere in the access logs.

Once code executes, the fallout is the standard RCE set: wp-config.php and the database credentials behind it, an administrator account of the attacker's choosing, persistence that survives a plugin update, and a foothold on the hosting account.

Is it being exploited?

Not yet, as far as anyone can say. CISA's SSVC assessment records exploitation: none, alongside automatable: yes and technical impact: total, the full worst-case triple short of observed abuse.

Two things to weigh against that calm. First, everything needed to weaponise this is now public: the sink, the bypass, and the delivery mechanism are described in the advisory and in the patched source. Second, the delivery channel is one attackers already operate at industrial scale. Comment-spam infrastructure exists precisely to post content on sites like these, and this bug converts that existing fleet into an RCE delivery system. The marginal cost of firing CVE-2026-78159 at a million event pages is a change to an existing bot.

The disclosure timeline itself was handled well:

DateEvent
23 August 2026CVE reserved; vendor notified
3 September 2026The Events Calendar 6.17.4 released with the fix
10 September 20266.17.4.1 released: changelog notes strengthened widget-instance validation
11 September 2026Public disclosure
12 September 2026CVE published

Notice the shape of that gap: patched code shipped roughly a week before anyone was told there was a reason to update. Sites with auto-updates enabled were protected before the bug had a name. Sites that treat plugin updates as a monthly chore were not, and were exposed for that week with no way of knowing why it mattered.

What to do today

  1. Update The Events Calendar to 6.17.4.1 or later. This is a plugin update; it takes a minute. It goes at the top of the queue for any site with comments on events.
  2. If you cannot update immediately, close the delivery channel: disable comments on the tribe_events post type. Uninstalling comments from events costs most sites nothing: the vast majority of event listings never wanted them.
  3. Turn on comment moderation if it is off. A comment that never displays never renders, and a block that never renders never fires. Moderation is not a UX preference here; it is a control.
  4. Audit what else can render in your comment area. If your theme or plugins run block parsing or shortcodes over comment text, assume comment content is executable context and decide deliberately whether that is what you want.

How to check whether someone got there first

The indicator of compromise lives in your comments table, not your access logs. Search for block markup where it should never appear:

wp db query "SELECT comment_ID, comment_post_ID, comment_date_gmt, comment_approved
FROM $(wp db prefix)comments
WHERE comment_content LIKE '%wp:legacy-widget%'
   OR comment_content LIKE '%wp:block%';"

(Adjust the table prefix if your install does not use the default.) A hit for wp:legacy-widget on a site that ran an affected version is not spam to be deleted. It is evidence of an RCE attempt, and possibly a successful one. Treat the host as compromised: rotate the database password from wp-config.php, rotate administrator and application passwords, audit the users table for accounts you did not create, and check for scheduled tasks and mu-plugins that arrived without you. That response checklist, executed calmly and completely, is what SOC as a service is for.

The broader point

Three things about this bug generalise past one plugin.

Block markup in comment fields is code, not text. do_blocks() does not distinguish post content from comment content, so any block an attacker can place in a comment becomes executable page structure. That assumption (comments are inert) is now permanently retired wherever block parsing touches comment HTML.

A guard is only as good as its assumptions about data shape. is_safe_widget_instance() was a real control, placed in a real position, and a payload with a different shape walked past it. One type check between anonymous input and a callable sink was never defense in depth; it was a single point of failure with good intentions.

Configurable callables plus attacker-controlled data equals RCE, every time. This is the same failure family as CVE-2026-15748 in Forminator and CVE-2026-66583's object injection: powerful features designed for trusted internal data, reached by untrusted input through a path nobody audited. In an application penetration test, the questions are always the same: where does attacker data land, and what would happen if it arrived in a shape the author never imagined.

If your business runs on WordPress, the questions worth answering are: which plugins render attacker-supplied content through powerful code paths, who reviews those paths, and how fast do you find out when one of them ships a 9.8. We build and harden WordPress sites for a living. See secure web development, or talk to us if you would rather have someone check your comment areas before someone else does.

Get Started

Want a security-first build?

Get a free security review. We'll look at where you stand today and tell you what to fix first, no strings attached.

Talk to an Expert
CVE-2026-78159: Comment-to-RCE in The Events Calendar - IKZERO