无需认证即可执行:Langflow CVE-2026-33017 未授权远程代码执行漏洞深度剖析与靶标实战

原创 创新研究院 2026-04-09 16:31 湖南

本文从漏洞根因出发,深度剖析其完整代码执行链;基于Docker容器构建靶标环境,复现从Token窃取、公共流创建到控制Langflow容器的全过程;最后给出体系化的防御方案。本文旨在为AI平台安全研究者与安全管理人员提供可复现的漏洞剖析与实战防御参考。

一. 背景与机制介绍

Langflow是一个开源的、基于Python的低代码AI编排平台。它通过可视化编辑器,使开发者能够以拖拽组件的方式快速构建流式的AI应用原型[1]。

图1 Langflow拖拽式流应用

Langflow的核心工作单元是“流”(Flow),它是应用程序工作逻辑的功能性表示。流由多个组件(Component)构成,每个组件代表工作流中的一个独立步骤。除系统内置组件外,Langflow还支持自定义组件(Custom Component),组件允许开发者使用Python代码扩展平台功能。这意味着,用户提供的任意Python代码将在流被触发时由Langflow后端动态加载和执行。

为支持便捷的分享与协作场景,Langflow设计了“公共流”(Public Flow)机制。任何知晓该流ID的人均可通过公共流构建接口(/api/v1/build_public_tmp/{flow_id}/flow)匿名执行该流。该接口还接受一个可选的data参数,允许调用者向接口提交额外的数据。

上述机制中的公共流实现正好导致了CVE-2026-33017漏洞。攻击者可通过该漏洞构造特殊请求,在目标服务器上执行任意代码[2]。为了更好地理解该漏洞的形成机制,接下来我们从设计和实现两个层面进行分析。

二、核心原理分析

2.1 

设计缺陷:未认证接口接收可执行代码导致任意代码注入

Langflow的公共流构建接口存在严重的安全风险,其本意是给定一个公共流的ID,从数据库加载该流的定义,然后执行构建。这是一个典型的“只读+执行”模式,无需认证也是可以理解的,因为流本身已经是公开的。

然而,该接口的实现增加了一个可选的data参数,其语义是“如果调用者自定义了流内容,则优先使用该定义,而不是从数据库加载”。这一设计可能出于临时预览或调试的考虑,但却带来了两个致命问题:

  • 无条件覆盖:只要请求中携带了data参数,系统就会完全忽略数据库中已存储的可信流定义,转而使用攻击者提供的内容。

  • 无任何校验:系统没有对data的来源进行任何认证或签名校验,也没有对其内容进行任何安全审查。

这也违反了安全架构的基本要求,系统应当严格区分两种数据源:一是数据库中的可信数据(由授权用户创建),二是用户请求中的不可信数据(完全由外部控制)。Langflow的错误在于将二者置于同一优先级,且无条件地优先使用不可信数据来覆盖可信数据。更严重的是,该接口本身就是无需认证的,这意味着任何能够访问该服务的人都可以利用这一设计缺陷。

2.2 

执行缺陷:无沙箱隔离导致注入代码被直接执行

当包含任意代码的data参数经未授权请求进入接口层后,它并不会被拦截或校验,而是沿着一条预设的函数调用链向下传递,经过“数据分流”、“图构建”、“组件实例化”等多个环节的处理,最终抵达危险的exec()执行点。如图2所示,这条完整的调用路径中,每个环节都暴露出特定的安全缺陷——或是缺失认证,或是无条件覆盖,或是缺乏校验,或是无沙箱执行[3]。

图2 公共流构建调用链路

第一个环节是构建公共流入口函数

    (build_public_tmp),位于:
    src/backend/base/langflow/api/v1/chat.py,关键代码如下:
    @router.post("/build_public_tmp/{flow_id}/flow")
    async def
    build_public_tmp(
        *,
        data: Annotated[FlowDataRequest | None,
    Body(embed=True)] = None,
        ……
    ):
        # 无认证检查
        await start_flow_build(data=data, ……)

    该函数是公共流构建的HTTP接口,其设计预期是从数据库加载已存储的流定义并执行,因此无需认证。然而,该接口直接接受一个可选的data参数,且未添加任何认证依赖。攻击者可任意构造data内容并在请求中提交。

    第二个环节是位于

    src/backend/base/langflow/api/build.py的数据分流函数(create_graph),相关代码逻辑如下:

      .......
      if not data:
      return await build_graph_from_db(
      .......
      )

      .......
      return await build_graph_from_data(
      flow_id=flow_id_str,
      payload=data.model_dump(),
      .......
      )

      该函数根据是否存在data参数决定构建图的数据来源。其缺陷在于:只要data非空,就会无条件使用攻击者提供的数据,完全忽略了数据库中已存储的可信流定义。

      第三个环节的图构建(from_payload)和第四环节的组件实例化函数(_instantiate_components_in_vertices)均位于src/lfx/src/lfx/graph/graph/base.py中。

        def
        from_payload(
            .......
            if "data" in payload:
                    payload = payload["data"]
                try:
                    vertices =
        payload["nodes"]
                    edges = payload["edges"]
                    graph = cls(flow_id=flow_id,
        flow_name=flow_name, user_id=user_id, context=context)
                    graph.add_nodes_and_edges(vertices,
        edges)
        .......

          def _instantiate_components_in_vertices(self)
          -> None:
                  """Instantiates the
          components in the vertices."""
                  for vertex in self.vertices:

          vertex.instantiate_component(self.user_id)
          ......

          前者解析攻击者提供的节点等数据,将其转换为内部图结构;后者遍历图中的每个顶点并触发组件的实例化流程。遗憾的是,这两个函数均未对节点内容进行任何安全校验或沙箱隔离,只是简单地执行解析和实例化操作

          第四个环节是位于

          src/backend/base/langflow/interface/initialize/loading.py的代码提取函数(instantiate_class),关键代码为:

            ......
            code =
            custom_params.pop("code")
            class_object:
            type[CustomComponent | Component] = eval_custom_component_code(code)
            ......

            在组件实例化过程中,该模块负责从节点的模板中提取code字段,该字段包含了用户编写的自定义组件Python代码。此模块的缺陷在于仅直接提取了用户提供的代码字符串,未做任何校验、过滤或长度限制。

            第五个环节是动态执行,包括

            eval_custom_component_code和prepare_global_scope两个函数。其中,prepare_global_scope

            位于src/lfx/src/lfx/custom/validate.py,漏洞核心代码为:

              ......
              if definitions:
                  combined_module =
              ast.Module(body=definitions, type_ignores=[])
                      compiled_code =
              compile(combined_module, "<string>""exec")
                      exec(compiled_code, exec_globals)
              ......

              提取到的代码字符串会被传递至此,prepare_global_scope函数会编译用户提供的代码模块,并执行所有顶层语句。值得关注的是,该函数没有使用任何沙箱机制。攻击者只需在code中写入形如_rce = os.system(“任意注入代码”)的顶层赋值语句,即可在图构建阶段立即触发命令执行,无需等待组件方法被调用。

              纵观整个调用链,入口没有认证限制,多源数据允许无条件覆盖,代码提取无校验,无沙箱动态执行——这四个缺陷环环相扣,使得Python代码在无沙箱环境下直接执行,由此构成了完整的未授权远程代码执行漏洞。为进一步验证上述分析并直观展示漏洞的利用过程,接下来我们将基于此构建一个靶场场景,并搭建一个可控的靶标环境。

              三、靶场场景构建与搭建

              3.1 

              靶场场景构建

              依据前文的理论分析, 本节将设计一个贴近真实渗透测试的靶场场景,攻击链路如下:

              某企业内部署了Langflow服务(默认配置,AUTO_LOGIN=true),版本为受漏洞影响的1.8.1。攻击者获得该服务网络访问权限后,首先发现该Langflow服务可未授权获取管理员Token,于是利用该Token创建了一个名为test的公共流。

              随后,攻击者向无需认证的公共流构建接口发送恶意构造的data参数,其中嵌入一段Python代码。该代码在服务端exec()执行后,向攻击者指定的IP和端口发起反向Shell连接,从而完全控制容器。

              图3 靶场场景逻辑时序图

              3.2 

              核心环境依赖

              组件

              版本/标识

              说明

              宿主机操作系统

              Ubuntu   22.04

              支持Docker即可

              Langflow

              1.8.1(漏洞影响版)

              使用对应版本的容器镜像 

              Docker

              20.10+

              运行容器

              攻击机

              同网段任意主机

              使用curljqnc,与靶机网络互通

              3.3 

              漏洞靶标构建

              步骤1:导入容器镜像

                docker load -i langflow-1.8.1.tar

                图4 容器镜像

                步骤2:运行Langflow容器

                  docker run -d -p 7860:7860 --name langflow-vuln langflowai/langflow:1.8.1

                  图5 运行Langflow容器

                  接下来访问 http://<靶机IP>:7860 来验证服务启动,可以看到Langflow界面。

                  图6 Langflow界面

                  步骤3:验证auto_login可用
                  curl http://<靶机IP>:7860/api/v1/auto_login返回JSON包含access_token字段。

                  图7 获取Token

                  3.4 

                  攻击端部署

                  搭建满足以下要求的攻击机:

                  • 能够网络访问靶机的7860端口。

                  • 安装curl和jq(用于解析JSON,非必需)。

                  四、漏洞复现与利用

                  4.1 

                  构造恶意请求

                  至此,我们搭建了完整的靶场环境,接下来我们需要根据靶场场景构造包含Python代码的特定data,其主要结构为: 

                    data": {
                          "nodes": [{
                             ……
                            "data": {
                               ……
                              "node": {
                                "template": {
                                  "code": {
                                     ……
                                    "value": "<python代码>",
                                     ……
                                  },
                                  ……
                                },
                                …… 

                    可执行的python代码需要以字符串的形式赋值给"value"字段,具体内容此处不在赘述,但以下两个点需要注意:

                    1. 关于Payload编码:由于JSON中需要转义双引号和换行符,实际发送时需将Python代码压缩为一行,或使用\n转义。示例中采用\n和反斜杠转义双引号。

                    2. 关于持久化技巧:bash反弹Shell命令是最常用的方式,但某些容器环境可能没有bash,所以需要使用其他手段进行替代。另外将内容写入文件后执行可绕过一些简单的执行限制。

                    最终我们构造的Payload分为两步:先写入反弹Shell脚本到临时文件,然后执行该脚本。

                    4.2 

                    执行与效果验证

                    第一步:获取Token并创建公共流
                    创建公共流的接口是要求认证的,所以这里需要利用/api/v1/auto_login接口的匿名特性创建token,然后使用此处的token创建公共流,获取公共流ID。当然,获取公共流ID的方式有很多种,例如扫描泄露的ID或猜测ID。

                    图8 创建Langflow公共流ID

                    第二步:监听Shell 
                    在攻击机上新开窗口执行nc -lvnp 4444 监听反弹shell端口。

                    图9 监听反弹shell端口

                    第三步:发送请求
                    在攻击机上执行curl命令请求/api/v1/build_public_tmp/${FLOW_ID}/flow接口,发送构造好的Payload。

                    图10 发送恶意请求

                    第四步:验证Shell
                    此时,监听窗口会收到来自目标容器的反向连接,执行id、whoami等命令确认控制权。

                    图11 攻击端接收到shell

                    4.3 

                    后续步骤

                    获得Shell后,攻击者可以进一步持久化与横向移动:

                    • 读取环境变量:cat /proc/self/environ 窃取API密钥、数据库密码。

                    • 扫描内网:利用容器作为跳板攻击其他内部服务。

                    • 植入后门:在容器中写入定时任务或SSH密钥。

                    由于Langflow通常具有较高权限(实际部署中容器可能以高权限运行,或者直接部署在宿主机上),因此漏洞风险极大,此处不再进行延伸。

                    五、安全防护最佳实践

                    官方建议立即更新Langflow至1.9.0及以上版本。修复版本已移除build_public_tmp中的data参数,确保公共流仅从数据库加载。

                    图12  1.9版本Langflow公共流构建参数

                    当然,如果由于业务或其他强制原因暂时无法升级的环境可以使用临时缓解方案

                    • 在反向代理层(Nginx/Apache)拦截对/api/v1/build_public_tmp/*/flow且请求体包含data字段的POST请求。

                    • 使用WAF规则检测请求中是否存在"data":{"nodes"等模式并阻断。

                    • 将AUTO_LOGIN显式设置为false,减少攻击者自行创建公共流的能力(但不能根除漏洞,因为已有公共流仍可被利用)。

                    • 限制容器网络出站:禁止容器主动连接外部IP(如通过iptables或Docker网络策略),可阻止恶意外联。

                    六、绿盟AI靶场创新方案

                    绿盟科技星云实验室基于云靶场构建面向AI场景的创新方案,该方案引入多类威胁模型,构建了覆盖实战攻防全链路的靶场环境,重点呈现三大核心场景:

                    • AI系统对外部环境的威胁场景: 在这一类场景中,靶场重点还原大模型被纳入系统后,其输出结果被自动采信并直接作用于外部环境(本地终端与开发机、浏览器与 IDE、云原生基础设施等等)所形成的真实攻击路径。该类威胁并非源于模型本身的缺陷,而是源于模型能力与外部环境执行能力之间缺乏有效安全边界。

                    • 外部环境对AI系统威胁场景:在此类威胁场景中,靶场重点关注外部环境如何成为攻击大模型的关键跳板。攻击者不再局限于通过提示词影响模型输出,而是借助外部环境中的执行能力、逃逸路径、供应链环节与控制面权限,从运行环境、权限体系与数据上下文等多个层面,直接接管或长期影响大模型的行为。

                    • AI系统自身的内生安全风险场景:如输入与指令安全、输出与交互安全、数据与知识安全、自治与资源治理安全。 

                    图13 靶场场景概览

                    内容编辑:张小勇

                    责任编辑:陈佛忠

                    本公众号原创文章仅代表作者观点,不代表绿盟科技立场。所有原创内容版权均属绿盟科技研究通讯。未经授权,严禁任何媒体以及微信公众号复制、转载、摘编或以其他方式使用,转载须注明来自绿盟科技研究通讯并附上本文链接。

                    关于我们

                    绿盟科技研究通讯由绿盟科技创新研究院负责运营,绿盟科技创新研究院是绿盟科技的前沿技术研究部门,包括星云实验室、天枢实验室和孵化中心。团队成员由来自清华、北大、哈工大、中科院、北邮等多所重点院校的博士和硕士组成。

                    绿盟科技创新研究院作为“中关村科技园区海淀园博士后工作站分站”的重要培养单位之一,与清华大学进行博士后联合培养,科研成果已涵盖各类国家课题项目、国家专利、国家标准、高水平学术论文、出版专业书籍等。

                    我们持续探索信息安全领域的前沿学术方向,从实践出发,结合公司资源和先进技术,实现概念级的原型系统,进而交付产品线孵化产品并创造巨大的经济价值。

                    跳转微信打开

                    原始链接: https://mp.weixin.qq.com/s?__biz=MzIyODYzNTU2OA==&mid=2247499810&idx=1&sn=7375526e25605ffefa701ee5b2d5b474
                    侵权请联系站方: [email protected]

                    相关推荐

                    换一批