Automated Threat Modeling

Brief Summary: CVE-2026-23696 — Windmill SQL Injection Enables Full Privilege Escalation and Remote Code Execution

A short review of CVE-2026-23696, a critical SQL injection in Windmill's folder ownership management that lets any authenticated user escalate to super admin and achieve remote code execution as root.

CVE Analysis

8 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-07

Brief Summary: CVE-2026-23696 — Windmill SQL Injection Enables Full Privilege Escalation and Remote Code Execution
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A single unsanitized parameter in Windmill's folder ownership API gives any authenticated user, even one confined to the most restricted operator role, a direct path to forging super admin tokens and executing arbitrary code as root. For organizations that run Windmill as their workflow backbone or deploy it through Nextcloud Flow, this vulnerability (CVE-2026-23696, CVSS 9.9) is a complete system compromise that takes roughly five seconds.

Windmill is an open source workflow engine developed by Windmill Labs for building workflows, data pipelines, and internal tools at scale. It positions itself as an alternative to platforms like Retool and Temporal, with a developer platform for APIs, background jobs, and user interfaces. The Community Edition is distributed under AGPLv3, and Windmill is also embedded in Nextcloud Flow, which carries it into the wider Nextcloud ecosystem.

Technical Information

Root Cause: Unsanitized Input in SQL Query Construction

The vulnerability is in backend/windmill-api/src/folders.rs at lines 698 and 699. The addowner endpoint takes an owner parameter from a JSON request body and interpolates it directly into a PostgreSQL query with Rust's format! macro. The user-supplied value lands in a jsonb_set path argument with no sanitization and no parameterized query binding. This is a textbook case of CWE-89 (Improper Neutralization of Special Elements used in an SQL Command).

Because the input goes straight into the query string instead of through a parameterized query, an attacker can break out of the intended SQL context and inject arbitrary subqueries.

Attack Flow: From Operator to Root in Five Steps

The addowner endpoint requires the caller to be an owner of the target folder. That looks like a meaningful access-control barrier, but it is not. Any authenticated user can create a new folder, and the creator automatically becomes its owner, so even the most restricted operator account meets the requirement.

The full exploitation chain runs like this:

  1. The operator creates a new folder through the standard API. The platform automatically makes the operator the folder's owner.

  2. The operator sends a crafted payload to the addowner endpoint. The injected SQL contains a subquery that extracts the jwt_secret from the global_settings table and stores it in the folder's extra permissions JSONB column.

  3. The operator reads the folder configuration through the normal API and gets back the JWT signing secret that the injected subquery wrote into the JSONB field.

  4. The operator deletes the folder, which removes the forensic artifact from the database and makes post-exploitation detection significantly more difficult.

  5. With the leaked JWT secret, the operator forges a new JSON Web Token with super admin privileges, then uses it to call the job execution APIs and gain remote code execution as root.

Scope of Data Exposure

The SQL injection gives unrestricted read access to the entire PostgreSQL database behind the Windmill instance. These categories of sensitive data are exposed:

Data CategoryImpact
jwt_secretToken forgery leading to immediate RCE
Password hashesOffline password cracking
User emailsAccount enumeration and targeting
API tokensComplete account takeover
Workflow definitionsLeakage of proprietary code and business logic
Resource credentialsLateral movement to connected external systems

Exfiltrating the jwt_secret has the worst consequences, because it turns a data leak into full system compromise without any additional attack surface.

Patch Analysis

The vendor fixed the vulnerability in commit 942fb62 by adding a validate_owner function to folders.rs. The function allows only alphanumeric characters, underscores, hyphens, and slashes in the owner parameter. Input with any other character makes the API return a Bad Request error, so SQL syntax never reaches the query. A later commit (7a9ef14) tightened input handling further across the codebase.

Affected Systems and Versions

These products and version ranges are affected:

  • Windmill Community Edition (CE): versions 1.276.0 through 1.603.2
  • Windmill Enterprise Edition (EE): versions 1.276.0 through 1.603.2
  • Nextcloud Flow: all versions prior to 1.3.0 (which embeds a vulnerable Windmill version)

The patched versions are:

  • Windmill: version 1.603.3 and later
  • Nextcloud Flow: version 1.3.0 and later (which embeds Windmill 1.603.4)

Any Windmill deployment in the affected version range is vulnerable, however user roles are configured, because even the most restricted operator role can trigger the exploit.

Vendor Security History

Several other significant security flaws in the Windmill platform were found in the same timeframe as CVE-2026-23696:

CVETypeSeverityPatched Version
CVE-2026-29059Unauthenticated Path Traversal leading to RCEHigh1.603.3
CVE-2026-23696Authenticated SQL Injection leading to RCECritical1.603.3
CVE-2026-22683Operator Permission BypassHigh1.615.0
CVE-2026-33881NativeTS Code Injection via Environment VariablesN/A1.664.0
CVE-2026-26964Slack OAuth Client Secret ExposureLow1.635.0

With this many flaws in one period, Windmill deployments should be kept strictly up to date with the latest stable releases. The vendor's response to CVE-2026-23696 was notably fast. The researcher reported the vulnerability on January 11, 2026, and the Windmill founder responded within hours on a Sunday, shipped the patch, and awarded a €4,000 bounty the same day. Windmill did not assign a CVE identifier for the flaw, so VulnCheck stepped in as a CNA to publish CVE-2026-23696.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization