当前位置: 首页 > news >正文

CVE-2026-22218 Chainlit 框架任意文件读取漏洞全解析

漏洞简介

Chainlit 是一个开源的 Python 框架,专门用于快速构建对话式人工智能(Conversational AI)应用程序和大语言模型(LLM)接口。该框架基于 FastAPI 和 Socket.IO 构建,提供了丰富的用户界面组件和实时通信能力,使开发者能够轻松创建类似 ChatGPT 的对话界面。Chainlit 广泛应用于聊天机器人、AI 助手、客户服务系统等场景,支持多种 LLM 后端(如 OpenAI、Anthropic Claude、LangChain 等)的集成,并提供了完善的用户认证、会话管理、文件处理等企业级功能。

核心问题:Chainlit 在处理自定义元素(Custom Element)时,没有对用户传入的文件路径做任何验证,也没有对未认证用户进行有效拦截,导致任意人都可以让服务器读取其本地任意文件并通过接口返回给攻击者。

更具体地说,这个漏洞由两个独立的代码缺陷叠加形成:

  • 缺陷一:权限检查形同虚设——当服务器未配置强制身份认证时,核心接口的 if current_user 判断直接被跳过

  • 缺陷二:路径完全不做校验——用户传入的 path 字段被原封不动地传入文件读取函数,攻击者可以指向服务器上任意位置

这两个缺陷单独看都算严重,组合在一起就造成了"无认证+任意文件读取"的高危漏洞

image

漏洞复现

整个攻击分为两步,合计只需两次 HTTP 请求加一个 WebSocket 连接:

  • 第一步:向 /project/element 发送 PUT 请求,在请求体中注入一个包含任意文件路径(如 /etc/passwd)的 path 字段,触发服务器读取该文件并缓存,同时通过 WebSocket 获得一个"文件令牌"(chainlitKey)

  • 第二步:携带这个文件令牌访问 /project/file/{chainlitKey},服务器直接把刚才读取的文件内容返回给攻击者

image

创建一个 demo.py

import chainlit as cl
​
​
@cl.step(type="tool")
async def tool():   # Fake tool   await cl.sleep(2)   return "Response from the tool!"
​
​
@cl.on_message  # this function will be called every time a user inputs a message in the UI
async def main(message: cl.Message):   """   This function is called every time a user inputs a message in the UI.   It sends back an intermediate response from the tool, followed by the final answer.
​   Args:       message: The user's message.
​   Returns:       None.   """
​
​   # Call the tool   tool_res = await tool()
​   await cl.Message(content=tool_res).send()

利用 python 虚拟环境 方便搭建环境

python -m venv venv
venv\Scripts\Activate.ps1
python -m pip install chainlit==2.9.3 #安装存在漏洞的 chainlit 版本
chainlit run demo.py -w

image

运行构造好的 chainlit_file_read_exploit.py 指定 url 和需要读取的文件内容,就可以将文件打印出来

image

漏洞分析

第一步是通过调用 PUT /project/element​ 接口注入恶意文件路径,当攻击者在请求参数中传入包含任意路径的 path​ 字段时(如 /etc/passwd​),服务器端的 persist_file()​ 函数会读取该路径指定的文件内容并将其复制到临时目录中,同时将临时文件路径与一个随机生成的文件标识符(file_id)建立映射关系并注入到当前会话的文件映射表(session.files)中,随后服务器通过 WebSocket 消息将这个文件标识符(chainlitKey)推送给客户端,攻击者需要监听 WebSocket 连接来捕获这个关键的文件ID。

server.py#update_thread_element

image

update_thread_element​ 函数接收到请求后,首先调用 Element.from_dict()​ 方法解析请求体中的元素字典,该方法根据 type​ 字段判断元素类型并创建对应的对象实例,当 type​ 为 custom​ 时会创建 CustomElement​ 对象,随后服务器调用该对象的 update() 方法。

element.py#from_dict

image

在创建过程中 from_dict()​ 方法会提取请求中的所有字段包括用户可控的 path​ 字段并传递给对象构造函数,此时恶意路径(如 /etc/passwd​)被完整保存到 CustomElement​ 对象的 path 属性中。

【----帮助网安学习,以下所有学习资料免费领!加vx:YJ-2021-1,备注 “博客园” 获取!】

 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)

element.py#CustomElement#update

image

调用CustomElement​的 update()​ 方法,该方法内部会调用父类 Element​ 的 send() 方法。

element.py#Element#send

image

首先 send()​ 方法调用 await self._create(persist=persist) 执行文件持久化处理

element.py#Element#create

image

_create()​ 方法检测到对象存在 path​ 属性后会调用 session.persist_file() 函数

session.py#BaseSession#persist_file

image

session.persist_file()​ 函数,该函数使用 aiofiles​ 异步读取攻击者指定路径的文件内容,将内容复制到会话专属的临时目录中(如 /tmp/chainlit/{session_id}/{file_id}​),同时生成一个随机的文件标识符(UUID格式),并在会话的文件映射表(session.files)中建立该标识符与临时文件路径的映射关系。

element.py#Element#send

image

成文件持久化后 send()​ 方法执行第二个关键操作,调用 await context.emitter.send_element(self.to_dict()) 将元素信息发送到前端

element.py#Element#to_dict

image

to_dict()​ 方法负责将 CustomElement​ 对象转换为字典格式,该字典包含对象的所有关键属性如 id​、type​、name​、display​ 以及最重要的 chainlitKey(即刚才获得的文件标识符)

emitter.py#send_element

image

转换后的字典通过 send_element()​ 方法传递给 emitter​ 的 emit 函数

该函数是在 WebSocket 连接建立时注入到会话对象中的闭包函数,它调用 Socket.IO 的全局发送方法将包含 chainlitKey​ 的元素字典通过 WebSocket 推送给客户端,攻击者通过监听 WebSocket 消息流捕获事件名为 element​ 的消息,从消息数据中提取 chainlitKey 字段的值即可获得文件标识符,至此完成第一步的路径注入、文件复制和标识符获取操作。

第二步是使用第一步获得的文件标识符访问 GET /project/file/{file_id}​ 接口来读取文件内容,服务器根据请求中的 session_id​ 参数定位到对应的会话对象,从该会话的文件映射表中查找文件ID对应的临时文件路径,由于权限检查存在 if current_user:​ 的逻辑缺陷,未认证用户可以绕过权限验证,服务器直接使用 FileResponse 返回临时文件的内容,而该临时文件已经是目标敏感文件的完整副本,从而实现任意文件读取,整个攻击过程无需任何身份认证,攻击者仅需建立一个匿名 WebSocket 连接即可完成利用。

server.py#get_file

image

get_file()​ 函数通过 WebsocketSession.get_by_id()​ 方法根据 session_id​ 从全局会话字典中获取对应的会话对象,从该会话对象的 files​ 映射表中查找 file_id​ 对应的文件记录,获取其中存储的临时文件路径,最后使用 FileResponse() 直接返回该临时文件的内容给客户端,由于临时文件已经是目标敏感文件的完整副本,攻击者成功获得任意文件的内容。

漏洞修复

目前官方已发布修复版本,建议用户尽快更新至 Chainlit 的修复版本或更高版本:Chainlit ≥ 2.9.4

官方在 2.9.4 版本中通过引入 _sanitize_custom_element()​ 输入清理函数修复了该漏洞,该函数采用白名单机制重构了 update_thread_element()​ 和 delete_thread_element()​ 两个接口的元素处理逻辑,在创建 CustomElement 对象时仅提取并验证 id、name、display、props 等合法字段,而将用户可控的 path 字段从输入参数中完全排除,使得攻击者即使在请求中注入包含路径遍历字符的恶意 path 值,该字段也会在对象构造阶段被自动过滤丢弃,无法传递到后续的文件操作流程中,从根本上阻断了通过 /project/element​ 接口注入任意文件路径并通过 /project/file/{chainlitKey} 接口读取敏感文件的攻击链,有效防止了路径遍历漏洞的利用。

image

image

image

更多网安技能的在线实操练习,请点击这里>>

  

http://www.jsqmd.com/news/776748/

相关文章:

  • 从LED点阵到智能家居:聊聊74HC595这颗“老将”在2024年的新玩法
  • 成都移动流量卡255G月租31.9元到底值不值? - 资讯焦点
  • 谷歌推新款 Fitbit Air 健身手环,与 Whoop 对比谁更值得买?
  • AISMM模型深度拆解(含Gartner验证的5级成熟度判定逻辑+可运行Excel评估器)——今日限领》
  • 2026年Q2高口碑广州印刷厂实力排行榜:丽彩印刷科技领跑,品质与服务双优 - damaigeo
  • Driver Store Explorer:彻底释放Windows磁盘空间的专业驱动管理工具
  • 终极免费文档下载工具:kill-doc浏览器脚本的完整使用指南
  • 告别HALL传感器:用ADC和比较器两种方案搞定BLDC无感方波控制(C语言实战)
  • 免费开源Meshroom:从照片到三维模型的终极视觉编程工具箱
  • 从蓝牙键盘到智能门锁:用BlueZ套件和Spooftooph演示常见的蓝牙设备欺骗与防御
  • 普渡和高仙的清洁机器人哪家更有竞争力?2026年深度选型对比 - 资讯焦点
  • 51单片机IO口选错模式有多坑?对比准双向、推挽、高阻、开漏的适用场景与避坑指南
  • 生物测量仪怎么选?专业眼健康测量设备实用推荐清单 - 资讯焦点
  • PX4飞控用TFmini激光雷达测高,为啥高度会乱跳?我的户外实测与终极解决(附参数配置)
  • FPGA高可靠设计:容错架构、验证策略与工程实践
  • 用STM32F103C8T6和HLW8032做个智能插座:实时监控功率、电压、电流,还能自动断电保护
  • 自动化脚本实现Cursor免费注册:原理、风险与合规实践
  • 基于HuggingFace Chat-UI快速构建AI对话应用:从部署到定制
  • 开发者如何利用Taotoken的聚合API设计更健壮的AI应用架构
  • 哪些商用清洁机器人品牌更适合大型商业场景?2026年大面积清洁自动化深度分析 - 资讯焦点
  • 应对高并发场景Taotoken的稳定性与路由策略解析
  • 闲置物美卡回收,华财回收91%高折变现不踩坑 - 资讯焦点
  • AI工程师晋升加速器,2026大会推荐路径图(含认证学分、面试直通卡、开源项目Commiter提名通道):你的下一次跳槽机会正在倒计时
  • 如何为OBS直播画面注入专业级视觉特效
  • 为品质生活赋能,图特股份构建覆盖铰链、滑轨等全品类产品矩阵 - 资讯焦点
  • 保姆级避坑指南:在Ubuntu22.04上搞定ROS2 Humble与CH340串口通信(含驱动签名、权限、库缺失全流程)
  • 别再手动测速了!用高德/百度地图API+Python脚本,5分钟搞定城市主干道单向绿波基础数据采集
  • Beyond Compare 5 密钥生成器:免费激活的强大工具完整指南
  • 2026年论文AI率过高被预警怎么办?实测5个降AI工具,几分钟从75%降到合格线 - 降AI实验室
  • 2026年商用护眼显示器调研观察:飞利浦舒视蓝4.0技术解析与高性价比选购指南 - 资讯焦点