Calibre-Web 存储型跨站脚本(XSS)漏洞分析 (CVE-2025-65858)
项目概述
Calibre-Web 是一个开源的、基于Web的Calibre电子书数据库管理工具,提供直观的界面供用户浏览、阅读和下载电子书。然而,在其版本0.6.25的用户管理组件中发现了一个严重的存储型跨站脚本(XSS)漏洞,允许经过身份验证的攻击者(需具备管理员权限)在用户名中注入恶意脚本,进而危及整个应用的安全。
功能特性
- 用户管理:提供管理员创建、编辑和删除用户的功能。
- 用户列表API:通过
/ajax/listusers端点以JSON格式获取所有用户信息,用于前端展示。 - 数据存储:用户信息,包括用户名,直接存储于后端数据库。
漏洞详情 (CVE-2025-65858)
核心问题
该漏洞的根本原因在于,应用程序在创建新用户时,对username字段的输入未进行严格的服务器端验证和消毒。同时,当/ajax/listusers端点返回包含恶意用户名的数据时,前端在渲染用户列表时未对数据进行HTML实体编码,导致注入的脚本在浏览器上下文中被执行。
影响范围
- 确认受影响版本:Calibre-Web
0.6.25 - 可能受影响版本:所有使用相同用户管理和
/ajax/listusers实现逻辑的早期版本。 - 所需权限:攻击者需要拥有管理员权限才能创建新用户。
攻击向量
攻击者利用管理员权限,在创建新用户时,于Username字段中插入精心构造的XSS payload。该payload随后被存储在服务器上。当任何管理员(包括payload的创建者或其他管理员)访问用户列表页面时,前端会请求/ajax/listusers接口,该接口返回包含恶意payload的数据,导致浏览器解析并执行该payload。
安装指南 (用于本地测试与复现)
为了复现此漏洞,您需要搭建一个Calibre-Web0.6.25的测试环境。
克隆仓库并切换到受影响版本
gitclone https://github.com/janeczku/calibre-web.gitcdcalibre-webgitcheckout0.6.25安装依赖
建议使用Python虚拟环境。python3 -m venv venvsourcevenv/bin/activate# 在Windows上使用 `venv\Scripts\activate`pipinstall-r requirements.txt运行应用
python cps.py应用默认会在
http://localhost:8083启动。登录后台
- 访问
http://localhost:8083 - 使用默认管理员凭证登录:
admin/admin123
- 访问
使用说明 (漏洞复现步骤)
以下步骤演示了如何复现此存储型XSS漏洞。
前提条件
- 已按照安装指南搭建并运行Calibre-Web
0.6.25。 - 拥有管理员账户(如默认的
admin)的访问权限。
复现步骤
1. 登录为管理员
使用管理员账户登录Calibre-Web。
2. 创建带有恶意Payload的用户
- 导航至“管理”页面,点击“添加新用户”按钮。
- 在Username字段中输入以下XSS payload:
<img src=x onerror=alert('XSS')> - 填写其他必填字段(如密码、邮箱),然后提交表单。
3. 触发漏洞
- 应用程序已将恶意用户名存储到数据库。
- 现在,直接访问用户列表API端点:
GET /ajax/listusers,或者通过前端页面(如再次查看用户列表)触发请求。 - 浏览器会立即弹出一个包含“XSS”字样的警告框,证明JavaScript代码已成功执行。
核心代码分析
虽然我们无法直接访问Calibre-Web0.6.25的全部源代码,但可以推断出漏洞存在的关键代码位置。
1. 后端:用户创建处理 (假设代码)
在POST /admin/user/new的处理逻辑中,应用程序从请求中获取username参数,并直接存储到数据库,未进行任何过滤或编码。
# 假设的代码: /cps/admin.py (或类似位置)fromflaskimportrequestfrom.modelsimportUserdefcreate_user():username=request.form['username']# 直接从表单获取,无任何验证password=request.form['password']# ... 其他字段new_user=User(username=username,password=password)# 直接存储原始值db.session.add(new_user)db.session.commit()# ... 重定向或返回响应2. 后端:用户列表API (假设代码)
在/ajax/listusers的处理逻辑中,应用程序从数据库查询所有用户,并将包含原始用户名的用户列表以JSON格式返回。
# 假设的代码: /cps/admin.py (或类似位置)fromflaskimportjsonifyfrom.modelsimportUserdeflist_users_ajax():users=User.query.all()user_list=[]foruserinusers:# 直接从数据库取出用户名,未进行编码user_list.append({'id':user.id,'username':user.username})returnjsonify(user_list)3. 前端:渲染用户列表 (假设代码)
前端JavaScript代码接收到/ajax/listusers返回的JSON数据后,使用innerHTML或其他不安全的方法将用户名插入到DOM中。
<!-- 假设的代码: /cps/templates/admin.html (或类似位置) --><script>fetch('/ajax/listusers').then(response=>response.json()).then(users=>{constuserListDiv=document.getElementById('user-list');users.forEach(user=>{// 漏洞点:直接将用户名字符串插入到HTML中,innerHTML会解析并执行其中的脚本userListDiv.innerHTML+='<div>'+user.username+'</div>';});});</script><divid="user-list"></div>结论
CVE-2025-65858 是一个典型的存储型XSS漏洞,其根源在于对用户输入的不信任和对输出编码的缺失。此漏洞允许拥有管理员权限的攻击者将恶意负载持久化,进而危害其他管理员或应用自身的安全。修复方案应包括:在后端对用户名进行严格的输入验证,并在将数据输出到HTML上下文时进行严格的HTML实体编码。建议所有使用受影响版本的用户尽快升级或应用补丁。FINISHED
6HFtX5dABrKlqXeO5PUv/6LSj2QdUDGX+cmMlh0332NX4+31qteAH/vWua1xnBtr
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)