The ReadFileTool in Flowise does not restrict file path access, allowing authenticated attackers to exploit this vulnerability to read arbitrary files from the file system, potentially leading to remote command execution.
Flowise supports providing ReadFileTool for large models to read files in the server's file system. The implementation of this tool is located at packages/components/nodes/tools/ReadFile/ReadFile.ts.
The tool directly uses the file_path parameter passed to it without verifying whether the path belongs to Flowise's working directory. Authenticated attackers can exploit this vulnerability to read any known file on the server. For example, if the victim deploys in a Docker environment, the attacker could attempt to read sensitive files such as /root/.flowise/encryption.key, /root/.flowise/database.sqlite, and obtain sensitive information. If the victim does not use a Docker environment for deployment, the attacker could try reading sensitive files like /etc/passwd, /etc/shadow, /root/.ssh/id_rsa, further achieving the effect of remote command execution.
{
"nodes": [
{
"id": "startAgentflow_0",
"type": "agentFlow",
"position": {
"x": -203,
"y": 37
},
"data": {
"id": "startAgentflow_0",
"label": "Start",
"version": 1.1,
"name": "startAgentflow",
"type": "Start",
"color": "#7EE787",
"hideInput": true,
"baseClasses": [
"Start"
],
"category": "Agent Flows",
"description": "Starting point of the agentflow",
"inputParams": [
{
"label": "Input Type",
"name": "startInputType",
"type": "options",
"options": [
{
"label": "Chat Input",
"name": "chatInput",
"description": "Start the conversation with chat input"
},
{
"label": "Form Input",
"name": "formInput",
"description": "Start the workflow with form inputs"
}
],
"default": "chatInput",
"id": "startAgentflow_0-input-startInputType-options",
"display": true
},
{
"label": "Form Title",
"name": "formTitle",
"type": "string",
"placeholder": "Please Fill Out The Form",
"show": {
"startInputType": "formInput"
},
"id": "startAgentflow_0-input-formTitle-string",
"display": false
},
{
"label": "Form Description",
"name": "formDescription",
"type": "string",
"placeholder": "Complete all fields below to continue",
"show": {
"startInputType": "formInput"
},
"id": "startAgentflow_0-input-formDescription-string",
"display": false
},
{
"label": "Form Input Types",
"name": "formInputTypes",
"description": "Specify the type of form input",
"type": "array",
"show": {
"startInputType": "formInput"
},
"array": [
{
"label": "Type",
"name": "type",
"type": "options",
"options": [
{
"label": "String",
"name": "string"
},
{
"label": "Number",
"name": "number"
},
{
"label": "Boolean",
"name": "boolean"
},
{
"label": "Options",
"name": "options"
}
],
"default": "string"
},
{
"label": "Label",
"name": "label",
"type": "string",
"placeholder": "Label for the input"
},
{
"label": "Variable Name",
"name": "name",
"type": "string",
"placeholder": "Variable name for the input (must be camel case)",
"description": "Variable name must be camel case. For example: firstName, lastName, etc."
},
{
"label": "Add Options",
"name": "addOptions",
"type": "array",
"show": {
"formInputTypes[$index].type": "options"
},
"array": [
{
"label": "Option",
"name": "option",
"type": "string"
}
]
}
],
"id": "startAgentflow_0-input-formInputTypes-array",
"display": false
},
{
"label": "Ephemeral Memory",
"name": "startEphemeralMemory",
"type": "boolean",
"description": "Start fresh for every execution without past chat history",
"optional": true,
"id": "startAgentflow_0-input-startEphemeralMemory-boolean",
"display": true
},
{
"label": "Flow State",
"name": "startState",
"description": "Runtime state during the execution of the workflow",
"type": "array",
"optional": true,
"array": [
{
"label": "Key",
"name": "key",
"type": "string",
"placeholder": "Foo"
},
{
"label": "Value",
"name": "value",
"type": "string",
"placeholder": "Bar",
"optional": true
}
],
"id": "startAgentflow_0-input-startState-array",
"display": true
},
{
"label": "Persist State",
"name": "startPersistState",
"type": "boolean",
"description": "Persist the state in the same session",
"optional": true,
"id": "startAgentflow_0-input-startPersistState-boolean",
"display": true
}
],
"inputAnchors": [],
"inputs": {
"startInputType": "chatInput",
"formTitle": "",
"formDescription": "",
"formInputTypes": "",
"startEphemeralMemory": "",
"startState": "",
"startPersistState": ""
},
"outputAnchors": [
{
"id": "startAgentflow_0-output-startAgentflow",
"label": "Start",
"name": "startAgentflow"
}
],
"outputs": {},
"selected": false
},
"width": 103,
"height": 66,
"selected": false,
"positionAbsolute": {
"x": -203,
"y": 37
},
"dragging": false
},
{
"id": "directReplyAgentflow_0",
"position": {
"x": 209,
"y": 30.25
},
"data": {
"id": "directReplyAgentflow_0",
"label": "Direct Reply 0",
"version": 1,
"name": "directReplyAgentflow",
"type": "DirectReply",
"color": "#4DDBBB",
"hideOutput": true,
"baseClasses": [
"DirectReply"
],
"category": "Agent Flows",
"description": "Directly reply to the user with a message",
"inputParams": [
{
"label": "Message",
"name": "directReplyMessage",
"type": "string",
"rows": 4,
"acceptVariable": true,
"id": "directReplyAgentflow_0-input-directReplyMessage-string",
"display": true
}
],
"inputAnchors": [],
"inputs": {
"directReplyMessage": "",
"undefined": ""
},
"outputAnchors": [],
"outputs": {},
"selected": false
},
"type": "agentFlow",
"width": 163,
"height": 66,
"selected": false,
"positionAbsolute": {
"x": 209,
"y": 30.25
},
"dragging": false
},
{
"id": "agentAgentflow_0",
"position": {
"x": -63.5,
"y": 89.125
},
"data": {
"id": "agentAgentflow_0",
"label": "Agent 0",
"version": 2,
"name": "agentAgentflow",
"type": "Agent",
"color": "#4DD0E1",
"baseClasses": [
"Agent"
],
"category": "Agent Flows",
"description": "Dynamically choose and utilize tools during runtime, enabling multi-step reasoning",
"inputParams": [
{
"label": "Model",
"name": "agentModel",
"type": "asyncOptions",
"loadMethod": "listModels",
"loadConfig": true,
"id": "agentAgentflow_0-input-agentModel-asyncOptions",
"display": true
},
{
"label": "Messages",
"name": "agentMessages",
"type": "array",
"optional": true,
"acceptVariable": true,
"array": [
{
"label": "Role",
"name": "role",
"type": "options",
"options": [
{
"label": "System",
"name": "system"
},
{
"label": "Assistant",
"name": "assistant"
},
{
"label": "Developer",
"name": "developer"
},
{
"label": "User",
"name": "user"
}
]
},
{
"label": "Content",
"name": "content",
"type": "string",
"acceptVariable": true,
"generateInstruction": true,
"rows": 4
}
],
"id": "agentAgentflow_0-input-agentMessages-array",
"display": true
},
{
"label": "OpenAI Built-in Tools",
"name": "agentToolsBuiltInOpenAI",
"type": "multiOptions",
"optional": true,
"options": [
{
"label": "Web Search",
"name": "web_search_preview",
"description": "Search the web for the latest information"
},
{
"label": "Code Interpreter",
"name": "code_interpreter",
"description": "Write and run Python code in a sandboxed environment"
},
{
"label": "Image Generation",
"name": "image_generation",
"description": "Generate images based on a text prompt"
}
],
"show": {
"agentModel": "chatOpenAI"
},
"id": "agentAgentflow_0-input-agentToolsBuiltInOpenAI-multiOptions",
"display": false
},
{
"label": "Tools",
"name": "agentTools",
"type": "array",
"optional": true,
"array": [
{
"label": "Tool",
"name": "agentSelectedTool",
"type": "asyncOptions",
"loadMethod": "listTools",
"loadConfig": true
},
{
"label": "Require Human Input",
"name": "agentSelectedToolRequiresHumanInput",
"type": "boolean",
"optional": true
}
],
"id": "agentAgentflow_0-input-agentTools-array",
"display": true
},
{
"label": "Knowledge (Document Stores)",
"name": "agentKnowledgeDocumentStores",
"type": "array",
"description": "Give your agent context about different document sources. Document stores must be upserted in advance.",
"array": [
{
"label": "Document Store",
"name": "documentStore",
"type": "asyncOptions",
"loadMethod": "listStores"
},
{
"label": "Describe Knowledge",
"name": "docStoreDescription",
"type": "string",
"generateDocStoreDescription": true,
"placeholder": "Describe what the knowledge base is about, this is useful for the AI to know when and how to search for correct information",
"rows": 4
},
{
"label": "Return Source Documents",
"name": "returnSourceDocuments",
"type": "boolean",
"optional": true
}
],
"optional": true,
"id": "agentAgentflow_0-input-agentKnowledgeDocumentStores-array",
"display": true
},
{
"label": "Knowledge (Vector Embeddings)",
"name": "agentKnowledgeVSEmbeddings",
"type": "array",
"description": "Give your agent context about different document sources from existing vector stores and embeddings",
"array": [
{
"label": "Vector Store",
"name": "vectorStore",
"type": "asyncOptions",
"loadMethod": "listVectorStores",
"loadConfig": true
},
{
"label": "Embedding Model",
"name": "embeddingModel",
"type": "asyncOptions",
"loadMethod": "listEmbeddings",
"loadConfig": true
},
{
"label": "Knowledge Name",
"name": "knowledgeName",
"type": "string",
"placeholder": "A short name for the knowledge base, this is useful for the AI to know when and how to search for correct information"
},
{
"label": "Describe Knowledge",
"name": "knowledgeDescription",
"type": "string",
"placeholder": "Describe what the knowledge base is about, this is useful for the AI to know when and how to search for correct information",
"rows": 4
},
{
"label": "Return Source Documents",
"name": "returnSourceDocuments",
"type": "boolean",
"optional": true
}
],
"optional": true,
"id": "agentAgentflow_0-input-agentKnowledgeVSEmbeddings-array",
"display": true
},
{
"label": "Enable Memory",
"name": "agentEnableMemory",
"type": "boolean",
"description": "Enable memory for the conversation thread",
"default": true,
"optional": true,
"id": "agentAgentflow_0-input-agentEnableMemory-boolean",
"display": true
},
{
"label": "Memory Type",
"name": "agentMemoryType",
"type": "options",
"options": [
{
"label": "All Messages",
"name": "allMessages",
"description": "Retrieve all messages from the conversation"
},
{
"label": "Window Size",
"name": "windowSize",
"description": "Uses a fixed window size to surface the last N messages"
},
{
"label": "Conversation Summary",
"name": "conversationSummary",
"description": "Summarizes the whole conversation"
},
{
"label": "Conversation Summary Buffer",
"name": "conversationSummaryBuffer",
"description": "Summarize conversations once token limit is reached. Default to 2000"
}
],
"optional": true,
"default": "allMessages",
"show": {
"agentEnableMemory": true
},
"id": "agentAgentflow_0-input-agentMemoryType-options",
"display": false
},
{
"label": "Window Size",
"name": "agentMemoryWindowSize",
"type": "number",
"default": "20",
"description": "Uses a fixed window size to surface the last N messages",
"show": {
"agentMemoryType": "windowSize"
},
"id": "agentAgentflow_0-input-agentMemoryWindowSize-number",
"display": false
},
{
"label": "Max Token Limit",
"name": "agentMemoryMaxTokenLimit",
"type": "number",
"default": "2000",
"description": "Summarize conversations once token limit is reached. Default to 2000",
"show": {
"agentMemoryType": "conversationSummaryBuffer"
},
"id": "agentAgentflow_0-input-agentMemoryMaxTokenLimit-number",
"display": false
},
{
"label": "Input Message",
"name": "agentUserMessage",
"type": "string",
"description": "Add an input message as user message at the end of the conversation",
"rows": 4,
"optional": true,
"acceptVariable": true,
"show": {
"agentEnableMemory": true
},
"id": "agentAgentflow_0-input-agentUserMessage-string",
"display": false
},
{
"label": "Return Response As",
"name": "agentReturnResponseAs",
"type": "options",
"options": [
{
"label": "User Message",
"name": "userMessage"
},
{
"label": "Assistant Message",
"name": "assistantMessage"
}
],
"default": "userMessage",
"id": "agentAgentflow_0-input-agentReturnResponseAs-options",
"display": true
},
{
"label": "Update Flow State",
"name": "agentUpdateState",
"description": "Update runtime state during the execution of the workflow",
"type": "array",
"optional": true,
"acceptVariable": true,
"array": [
{
"label": "Key",
"name": "key",
"type": "asyncOptions",
"loadMethod": "listRuntimeStateKeys",
"freeSolo": true
},
{
"label": "Value",
"name": "value",
"type": "string",
"acceptVariable": true,
"acceptNodeOutputAsVariable": true
}
],
"id": "agentAgentflow_0-input-agentUpdateState-array",
"display": true
}
],
"inputAnchors": [],
"inputs": {
"agentModel": "chatOpenRouter",
"agentMessages": [
{
"role": "",
"content": "<p><span class=\"variable\" data-type=\"mention\" data-id=\"question\" data-label=\"question\">{{ question }}</span> </p>"
}
],
"agentTools": [
{
"agentSelectedTool": "readFile",
"agentSelectedToolRequiresHumanInput": "",
"agentSelectedToolConfig": {
"basePath": "/",
"agentSelectedTool": "readFile"
}
},
{
"agentSelectedTool": "writeFile",
"agentSelectedToolRequiresHumanInput": "",
"agentSelectedToolConfig": {
"basePath": "/",
"agentSelectedTool": "writeFile"
}
}
],
"agentKnowledgeDocumentStores": "",
"agentKnowledgeVSEmbeddings": "",
"agentEnableMemory": false,
"agentReturnResponseAs": "userMessage",
"agentUpdateState": "",
"undefined": "",
"agentModelConfig": {
"cache": "",
"modelName": "qwen/qwen3-30b-a3b",
"temperature": 0.9,
"streaming": true,
"maxTokens": "",
"topP": "",
"frequencyPenalty": "",
"presencePenalty": "",
"timeout": "",
"basepath": "https://openrouter.ai/api/v1",
"baseOptions": "",
"agentModel": "chatOpenRouter"
}
},
"outputAnchors": [
{
"id": "agentAgentflow_0-output-agentAgentflow",
"label": "Agent",
"name": "agentAgentflow"
}
],
"outputs": {},
"selected": false
},
"type": "agentFlow",
"width": 232,
"height": 100,
"selected": false,
"positionAbsolute": {
"x": -63.5,
"y": 89.125
},
"dragging": false
}
],
"edges": [
{
"source": "startAgentflow_0",
"sourceHandle": "startAgentflow_0-output-startAgentflow",
"target": "agentAgentflow_0",
"targetHandle": "agentAgentflow_0",
"data": {
"sourceColor": "#7EE787",
"targetColor": "#4DD0E1",
"isHumanInput": false
},
"type": "agentFlow",
"id": "startAgentflow_0-startAgentflow_0-output-startAgentflow-agentAgentflow_0-agentAgentflow_0"
},
{
"source": "agentAgentflow_0",
"sourceHandle": "agentAgentflow_0-output-agentAgentflow",
"target": "directReplyAgentflow_0",
"targetHandle": "directReplyAgentflow_0",
"data": {
"sourceColor": "#4DD0E1",
"targetColor": "#4DDBBB",
"isHumanInput": false
},
"type": "agentFlow",
"id": "agentAgentflow_0-agentAgentflow_0-output-agentAgentflow-directReplyAgentflow_0-directReplyAgentflow_0"
}
]
}
Authenticated attackers can exploit this vulnerability to steal sensitive information such as encryption keys, databases, and environment variables from the server, potentially leading to remote command execution.
CVE(s) and credit(s) are preferred.
If you have any questions regarding the vulnerability details, please feel free to reach out to us for further discussion. Our email address is xlabai@tencent.com.
Summary
The ReadFileTool in Flowise does not restrict file path access, allowing authenticated attackers to exploit this vulnerability to read arbitrary files from the file system, potentially leading to remote command execution.
Details
Flowise supports providing ReadFileTool for large models to read files in the server's file system. The implementation of this tool is located at packages/components/nodes/tools/ReadFile/ReadFile.ts.
The tool directly uses the file_path parameter passed to it without verifying whether the path belongs to Flowise's working directory. Authenticated attackers can exploit this vulnerability to read any known file on the server. For example, if the victim deploys in a Docker environment, the attacker could attempt to read sensitive files such as /root/.flowise/encryption.key, /root/.flowise/database.sqlite, and obtain sensitive information. If the victim does not use a Docker environment for deployment, the attacker could try reading sensitive files like /etc/passwd, /etc/shadow, /root/.ssh/id_rsa, further achieving the effect of remote command execution.
PoC
This file reading vulnerability has been verified to exist in the latest Flowise Docker image (https://hub.docker.com/layers/flowiseai/flowise/latest/images/sha256-26300377397818a451e0710389eb77615256b0f3ecc895194850ab35dda3ae7b). The reproduction steps are as follows:
Impact
Authenticated attackers can exploit this vulnerability to steal sensitive information such as encryption keys, databases, and environment variables from the server, potentially leading to remote command execution.
Credit
This vulnerability was discovered by:
CVE(s) and credit(s) are preferred.
If you have any questions regarding the vulnerability details, please feel free to reach out to us for further discussion. Our email address is xlabai@tencent.com.