Revive Adserver用户名验证绕过漏洞深度分析

本文详细分析了Revive Adserver用户名验证绕过漏洞的技术细节,包括三种有效的攻击向量:零宽空格、RTL覆盖和西里尔文同形字,提供了完整的概念验证代码和修复建议。

Revive Adserver | 报告 #3434156 - 用户名验证绕过 | HackerOne

执行摘要

提交d239a0845e4f64fbacd25fff2854426734d43aa2中的安全补丁不足。 测试确认4个攻击向量中有3个仍然可以绕过验证。

漏洞详情

受影响组件:用户注册/创建中的用户名验证 文件:lib/OA/Admin/UI/UserAccess.php 行号:116 当前模式#[\x00-\x1F\x7F\s]#u 严重性:中高(CVSS 6.5) 影响:账户冒充、钓鱼攻击、验证绕过

概念验证 - 有效攻击

攻击1:零宽空格(U+200B)⚠️ 严重

描述:不可见的Unicode字符,可创建重复账户 载荷(十六进制):61646d696ee2808b 载荷(解码):admin + 0xE2 0x80 0x8B(U+200B的UTF-8编码) 载荷(字面):用户名"admin"后跟Unicode字符U+200B

重现方法

1
2
3
$malicious_username = "admin" . "\xE2\x80\x8B"; // 十六进制字节
// 或
$malicious_username = "admin" . mb_chr(0x200B, 'UTF-8'); // Unicode码点

测试代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
$username = "admin" . "\xE2\x80\x8B"; // admin + 零宽空格

echo "用户名: admin[U+200B]\n";
echo "十六进制字节: " . bin2hex($username) . "\n";
echo "期望值: 61646d696ee2808b\n\n";

// 针对修补后的模式进行测试
if (preg_match('#[\x00-\x1F\x7F\s]#u', $username)) {
 echo "结果: 已阻止 ✓\n";
} else {
 echo "结果: 通过 - 存在漏洞! ✗\n";
}
?>

期望输出

1
2
3
4
5
用户名: admin[U+200B]
十六进制字节: 61646d696ee2808b
期望值: 61646d696ee2808b

结果: 通过 - 存在漏洞! ✗

影响

  • 创建与"admin"视觉相同的账户
  • 绕过用户名唯一性约束
  • 支持账户枚举攻击
  • 完美的冒充攻击

攻击2:RTL覆盖(U+202E)⚠️ 高危

描述:从右到左覆盖标记,反转文本显示方向 载荷(十六进制):61646d696ee280ae74657374 载荷(解码):admin + 0xE2 0x80 0xAE + test 载荷(字面):文本"admin" + U+202E + “test”

重现方法

1
2
3
$malicious_username = "admin" . "\xE2\x80\xAE" . "test"; // 十六进制字节
// 或
$malicious_username = "admin" . mb_chr(0x202E, 'UTF-8') . "test"; // Unicode

测试代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?php
$username = "admin" . "\xE2\x80\xAE" . "test"; // admin + RTL覆盖 + test

echo "用户名: admin[U+202E]test\n";
echo "十六进制字节: " . bin2hex($username) . "\n";
echo "期望值: 61646d696ee280ae74657374\n";
echo "视觉显示: 'tset' + 'admin'(反转!)\n\n";

// 针对修补后的模式进行测试
if (preg_match('#[\x00-\x1F\x7F\s]#u', $username)) {
 echo "结果: 已阻止 ✓\n";
} else {
 echo "结果: 通过 - 存在漏洞! ✗\n";
}
?>

期望输出

1
2
3
4
5
6
用户名: admin[U+202E]test
十六进制字节: 61646d696ee280ae74657374
期望值: 61646d696ee280ae74657374
视觉显示: 'tset' + 'admin'(反转!)

结果: 通过 - 存在漏洞! ✗

影响

  • 文本在UI中显示为反转(“testadmin"显示为不同顺序)
  • 混淆管理员审查用户账户
  • 支持社会工程攻击
  • 在日志和管理面板中的视觉欺骗

攻击3:西里尔文同形字(U+0430)⚠️ 严重

描述:西里尔字母’а’(U+0430)看起来与拉丁字母’a’(U+0061)完全相同 载荷(十六进制):d0b0646d696e 载荷(解码):0xD0 0xB0 + dmin 载荷(字面):西里尔文’а’(U+0430)后跟拉丁文"dmin”

重现方法

1
2
3
$malicious_username = "\xD0\xB0" . "dmin"; // 十六进制字节
// 或
$malicious_username = mb_chr(0x0430, 'UTF-8') . "dmin"; // Unicode

测试代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
$username = "\xD0\xB0" . "dmin"; // 西里尔文 а + 拉丁文 dmin

echo "用户名: [U+0430]dmin(看起来像'admin')\n";
echo "十六进制字节: " . bin2hex($username) . "\n";
echo "期望值: d0b0646d696e\n";
echo "视觉: 与'admin'完全相同\n\n";

// 与真实admin比较
$real_admin = "admin";
echo "真实'admin'十六进制: " . bin2hex($real_admin) . "\n";
echo "看起来相同?是 | 十六进制相同?否\n\n";

// 针对修补后的模式进行测试
if (preg_match('#[\x00-\x1F\x7F\s]#u', $username)) {
 echo "结果: 已阻止 ✓\n";
} else {
 echo "结果: 通过 - 存在漏洞! ✗\n";
}
?>

期望输出

1
2
3
4
5
6
7
8
9
用户名: [U+0430]dmin(看起来像'admin')
十六进制字节: d0b0646d696e
期望值: d0b0646d696e
视觉: 与'admin'完全相同

真实'admin'十六进制: 61646d696e
看起来相同?是 | 十六进制相同?否

结果: 通过 - 存在漏洞! ✗

完整测试脚本

保存为test_vulnerability.php并使用php test_vulnerability.php运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

echo "========================================\n";
echo "Revive Adserver - 漏洞测试\n";
echo "========================================\n\n";

// 测试1:零宽空格
echo "[测试1] 零宽空格 (U+200B)\n";
echo "----------------------------------------\n";
$test1 = "admin" . "\xE2\x80\x8B";
echo "载荷十六进制: " . bin2hex($test1) . "\n";
echo "期望值:    61646d696ee2808b\n";
echo "匹配: " . (bin2hex($test1) === "61646d696ee2808b" ? "是 ✓" : "否 ✗") . "\n";
$blocked1 = preg_match('#[\x00-\x1F\x7F\s]#u', $test1);
echo "状态: " . ($blocked1 ? "已阻止 ✓" : "存在漏洞 ✗") . "\n\n";

// 测试2:RTL覆盖
echo "[测试2] RTL覆盖 (U+202E)\n";
echo "----------------------------------------\n";
$test2 = "admin" . "\xE2\x80\xAE" . "test";
echo "载荷十六进制: " . bin2hex($test2) . "\n";
echo "期望值:    61646d696ee280ae74657374\n";
echo "匹配: " . (bin2hex($test2) === "61646d696ee280ae74657374" ? "是 ✓" : "否 ✗") . "\n";
$blocked2 = preg_match('#[\x00-\x1F\x7F\s]#u', $test2);
echo "状态: " . ($blocked2 ? "已阻止 ✓" : "存在漏洞 ✗") . "\n\n";

// 测试3:西里尔文同形字
echo "[测试3] 西里尔文同形字 (U+0430)\n";
echo "----------------------------------------\n";
$test3 = "\xD0\xB0" . "dmin";
echo "载荷十六进制: " . bin2hex($test3) . "\n";
echo "期望值:    d0b0646d696e\n";
echo "匹配: " . (bin2hex($test3) === "d0b0646d696e" ? "是 ✓" : "否 ✗") . "\n";
$blocked3 = preg_match('#[\x00-\x1F\x7F\s]#u', $test3);
echo "状态: " . ($blocked3 ? "已阻止 ✓" : "存在漏洞 ✗") . "\n\n";

// 测试4:表意空格(应该被阻止)
echo "[测试4] 表意空格 (U+3000) - 控制组\n";
echo "----------------------------------------\n";
$test4 = "admin" . "\xE3\x80\x80" . "test";
echo "载荷十六进制: " . bin2hex($test4) . "\n";
echo "期望值:    61646d696ee3808074657374\n";
echo "匹配: " . (bin2hex($test4) === "61646d696ee3808074657374" ? "是 ✓" : "否 ✗") . "\n";
$blocked4 = preg_match('#[\x00-\x1F\x7F\s]#u', $test4);
echo "状态: " . ($blocked4 ? "已阻止 ✓" : "存在漏洞 ✗") . "\n\n";

// 摘要
echo "========================================\n";
echo "摘要\n";
echo "========================================\n";
$vuln_count = (!$blocked1 ? 1 : 0) + (!$blocked2 ? 1 : 0) + (!$blocked3 ? 1 : 0);
echo "发现的漏洞: $vuln_count/3\n";
echo "补丁有效性: " . ((3 - $vuln_count) / 3 * 100) . "%\n\n";

if ($vuln_count > 0) {
 echo "  严重: 补丁不足!\n";
 echo "有效攻击:\n";
 if (!$blocked1) echo "  • 零宽空格\n";
 if (!$blocked2) echo "  • RTL覆盖\n";
 if (!$blocked3) echo "  • 西里尔文同形字\n";
} else {
 echo "✓ 所有攻击已被阻止\n";
}
echo "\n========================================\n";
?>

推荐修复

将当前的黑名单模式替换为白名单方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 当前(存在漏洞):
if (preg_match('#[\x00-\x1F\x7F\s]#u', $this->request['login'])) {
 $this->aErrors = [$GLOBALS['strInvalidUsername']];
}

// 推荐(安全):
if (empty($this->request['login']) ||
 strlen($this->request['login']) > 255 ||
 !preg_match('/^[a-zA-Z0-9._@-]+$/D', $this->request['login']) ||
 $this->request['login'] !== trim($this->request['login'])) {
 $this->aErrors = [$GLOBALS['strInvalidUsername']];
}

额外建议

  • 验证前的Unicode规范化(NFC)
  • 实现易混淆字符检测
  • 记录可疑的用户名模式
  • 在管理UI中显示十六进制表示

开发人员验证步骤

  1. 将上面的测试脚本保存为test_vulnerability.php
  2. 运行:php test_vulnerability.php
  3. 观察输出 - 应该看到3个存在漏洞的结果
  4. 应用推荐的修复
  5. 再次运行测试 - 所有结果应显示已阻止

影响

最危险 - 与"admin"视觉无法区分

  • 完美的钓鱼载体
  • 用户无法检测伪造账户
  • 凭据收集
  • 管理员冒充攻击

时间线

  • 2025年11月19日:kassem_s94提交报告
  • 2025年11月19日:更新漏洞信息
  • 2025年11月20日:itz_hari_提交重复报告
  • 2025年11月20日:mbeccati将严重性从严重更新为中(5.4)
  • 2025年11月20日:应用CVE-2025-55127相同评分
  • 2025年11月20日:kassem_s94确认新修复成功阻止所有攻击
  • 2025年11月22日:khoof提交重复报告
  • 2025年11月24日:计划于11月26日发布安全修复版本
  • 2025年11月26日:分配新CVE-ID:CVE-2025-55129
  • 2025年11月26日:报告公开披露

CVE信息

CVE ID:CVE-2025-55129 弱点:不正确的身份验证 - 通用 严重性:中(5.4) 状态:已解决

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计