
Root-cause विश्लेषण, passive version checker, और CVE-2026-18322 के लिए lab PoC, जो Smart Popup by Supsystic WordPress plugin में एक unauthenticated privilege escalation है।
CVE-2026-18322 पर सुरक्षा शोध, जो WordPress प्लगइन Smart Popup by Supsystic
(popup-by-supsystic) में एक अनधिप्रमाणित विशेषाधिकार-वृद्धि भेद्यता है। इस रिपॉज़िटरी में
अपस्ट्रीम स्रोत डिफ़ से प्राप्त मूल-कारण विश्लेषण, निकाले गए पैच, प्रभावित इंस्टॉलेशनों की
पहचान के लिए एक डिटेक्शन टूल (निष्क्रिय संस्करण फिंगरप्रिंटर) और शोषण PoC के लिए एक लैब
शामिल है।
यह भेद्यता सार्वजनिक और पैच की गई है। यह कार्य रक्षात्मक उपयोग के लिए प्रकाशित है: ऑपरेटरों को प्रभावित होस्ट खोजने और उनका उपचार करने में सहायता करना।
| संस्करण सीमा | स्थिति |
|---|---|
< 1.13.0 | भेद्य |
>= 1.13.0 | ठीक किया गया |
1.13.0 (31.07.2026 को जारी) पहला पैच किया गया रिलीज़ है; यह नीचे वर्णित श्रृंखला की सभी तीन कड़ियों को ठीक करता है। विश्लेषण का §1 देखें।
तीन स्वतंत्र कमज़ोरियाँ मिलकर अनधिप्रमाणित प्रशासक निर्माण बनाती हैं:
1. एक अनुमति-मैप टकराव — havePermissions() ने दो अनुमति मैप को array_merge() से
संयोजित किया। दोनों स्ट्रिंग कुंजी PPS_USERLEVELS ('userlevels') का उपयोग करते हैं, और
array_merge() स्ट्रिंग कुंजियों को अधिलेखित कर देता है, इसलिए बेस कंट्रोलर की छोटी
डिफ़ॉल्ट सूची ने पॉपअप मॉड्यूल की सूची को पूरी तरह बदल दिया — save और नौ अन्य विधियों से
प्रशासक प्रतिबंध को चुपचाप हटा दिया:
$permissions = $mod->getController()->getPermissions(); // [... 'save' ...]
$permissionsBase = $mod->getController()->getBasePermissions(); // ['getListForTbl','removeGroup','clear']
$permissions = array_merge($permissions, $permissionsBase); // base wins — 'save' is gone
जाँच खुली विफल होती है: मैप में अनुपस्थित क्रिया को कभी अस्वीकार नहीं किया जाता।
2. अजनबियों को भेजा गया पुन: प्रयोज्य nonce — save को अभी भी pps_nonce की आवश्यकता
थी, लेकिन सदस्यता पुष्टिकरण ईमेल में वही nonce क्रिया अंतर्निहित थी। लॉग-आउट उपयोगकर्ताओं
के लिए WordPress nonce uid=0 पर कुंजीबद्ध होता है, इसलिए अनाम ग्राहक के लिए बनाया गया टोकन
किसी भी अनाम हमलावर के लिए सत्यापित होता है।
3. कोई सर्वर-साइड भूमिका अनुमतिसूची नहीं — createWpSubscriber() ने कॉन्फ़िगर की गई भूमिका
को सीधे WP_User::set_role() को पास कर दिया। प्लगइन की सुरक्षित भूमिका सूची (जो
administrator को बाहर रखती है) केवल एडमिन ड्रॉपडाउन प्रस्तुत करते समय लागू की गई थी — एक
केवल-UI नियंत्रण।
श्रृंखलाबद्ध: nonce पाने के लिए सदस्यता लें → popup::save के विरुद्ध इसे पुन: चलाएँ ताकि
sub_wp_create_user_role=administrator सेट हो जाए → सदस्यता प्रवाह को ट्रिगर करें → स्थायी
एडमिन खाता।
फ़ाइल/पंक्ति संदर्भों के साथ पूर्ण वॉकथ्रू: docs/ANALYSIS.md।
poc/cve_2026_18322_check.py निर्धारित करता है कि कोई साइट प्रभावित संस्करण चलाती है या नहीं।
यह कभी शोषण का प्रयास नहीं करता: यह सादे HTTP GET जारी करता है और वह संस्करण पढ़ता है जो
इंस्टॉल अपने बारे में प्रकाशित करता है।
git clone <this-repo> && cd CVE-2026-18322
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
requests अनुशंसित है लेकिन वैकल्पिक — टूल मानक लाइब्रेरी पर वापस चला जाता है।
# Single target
python3 poc/cve_2026_18322_check.py https://example.com
# With supporting evidence
python3 poc/cve_2026_18322_check.py https://example.com -v
# Many targets, concurrently, exporting results
python3 poc/cve_2026_18322_check.py -f targets.txt -t 20 --json results.json
python3 poc/cve_2026_18322_check.py -f targets.txt --csv results.csv --only-vulnerable
# Through a proxy, ignoring TLS errors (lab use)
python3 poc/cve_2026_18322_check.py https://staging.internal -k --proxy http://127.0.0.1:8080
लक्ष्य तर्कों के रूप में या -f के माध्यम से दिए जा सकते हैं (- stdin पढ़ता है)। एक
नंगा होस्टनाम https:// माना जाता है।
$ python3 poc/cve_2026_18322_check.py https://example.com -v
[VULNERABLE] https://example.com/ (Smart Popup by Supsystic 1.11.2) < 1.13.0 affected by CVE-2026-18322; upgrade to 1.13.0+
- asset-version: plugin asset enqueued with ?ver=1.11.2 (https://example.com/)
- homepage-reference: references /plugins/popup-by-supsystic/ (https://example.com/)
- readme: plugin readme.txt is publicly readable (https://example.com/wp-content/plugins/popup-by-supsystic/readme.txt)
- readme-stable-tag: Stable tag: 1.11.2 (.../readme.txt)
एग्ज़िट कोड: 0 = कोई भेद्य लक्ष्य नहीं, 1 = कम से कम एक भेद्य, 2 = उपयोग त्रुटि।
दो निष्क्रिय संकेत, दोनों सार्वजनिक संसाधनों के सादे HTTP GET:
?ver=PPS_VERSION के साथ एनक्यू करता है
(classes/frame.php:410,473), इसलिए होमपेज अक्सर सटीक संस्करण लीक कर देता है।readme.txt — Stable tag: आधिकारिक है और संभावित रूप से पुराने कैश्ड एसेट पर
प्राथमिकता लेता है; चेंजलॉग फ़ॉलबैक के रूप में कार्य करता है।होमपेज स्कैन नाम बदले गए wp-content निर्देशिकाओं (जैसे Bedrock का /app/) को भी खोजता है
ताकि readme लुकअप साइट के वास्तविक लेआउट का अनुसरण करे।
डिटेक्शन टूल शोषण का प्रयास नहीं करता। इस CVE के लिए कोई हथियारबंद एक्सप्लॉइट यहाँ प्रकाशित नहीं है: पूरी श्रृंखला चलाने वाला एकमात्र कोड स्थानीय लैब तक कठोरता से सीमित है (नीचे देखें)।
निष्क्रिय चेकर केवल संस्करण फिंगरप्रिंटिंग करता है। इसका HttpClient GET के अतिरिक्त कोई
HTTP क्रिया उजागर नहीं करता — निर्माण द्वारा लागू और टेस्ट सूट में प्रमाणित, इसलिए यह
गलती से भी स्थिति-परिवर्तनकारी अनुरोध जारी नहीं कर सकता। यह कभी popup::save को कॉल नहीं
करता, sub_wp_create_user_role पैरामीटर नहीं भेजता, सदस्यता फ़ॉर्म सबमिट नहीं करता,
पुष्टिकरण ईमेल ट्रिगर नहीं करता, या किसी उपयोगकर्ता, पॉपअप, या सेटिंग को बनाता या संशोधित
नहीं करता। यह दो सार्वजनिक संसाधन पढ़ता है — होमपेज और readme.txt — और कुछ नहीं।
इस सुरक्षा की एक कीमत है, स्पष्ट रूप से कही गई: निर्णय उतना ही अच्छा है जितना होस्ट द्वारा प्रकाशित संस्करण मेटाडेटा। व्यापार-समझौते और विफलता मोड: §9।
lab/exploit_full_chain.py यहाँ का एकमात्र कोड है जो वास्तविक हमला करता है, और यह एक
WordPress प्रशासक बनाता है। यह इसलिए प्रकाशित है ताकि विश्लेषण पर भरोसा करने के बजाय
पुनरुत्पादित किया जा सके — एक प्राधिकरण बग के बारे में दावा जिसे प्रदर्शित नहीं किया जा सकता,
वह एक कथन है, निष्कर्ष नहीं। यह lab/ में डिस्पोज़ेबल स्टैक के लिए लिखा गया है और
किसी और चीज़ के लिए नहीं:
--lab-confirm अनिवार्य है। दोनों जाँचें पहले HTTP अनुरोध से पहले assert_lab_target()
में चलती हैं, इसलिए अस्वीकृत आह्वान लक्ष्य को बिल्कुल भी स्पर्श नहीं करता।http://localhost:8025/api/v1/…) के माध्यम से सदस्यता पुष्टिकरण ईमेल से पुन: प्रयोज्य
pps_nonce एकत्र करता है। उस ईमेल को कहीं और से प्राप्त करने का कोई कोड पथ नहीं है, इसलिए
लैब के बाहर किसी होस्ट के विरुद्ध श्रृंखला का कोई पहला चरण नहीं है।lab/docker-compose.yml में
प्रत्येक सेवा 127.0.0.1 पर बाइंड होती है।जैसा भेजा गया है, यह इसलिए किसी वास्तविक साइट पर निशाना नहीं बनाया जा सकता — यह अनुरोध
जारी करने से पहले REFUSING TO RUN पर रुक जाता है:
$ python3 exploit_full_chain.py https://example.com --lab-confirm
REFUSING TO RUN: example.com resolves to non-local address(es) 104.20.23.154, ...
This script creates a WordPress administrator and is for the local lab only.
To test a real site, use the non-destructive checker in ../poc/.
ये गार्ड सजावटी नहीं बल्कि भार-वहन करने वाले हैं: स्क्रिप्ट ही वास्तविक श्रृंखला है, और जो
इसे एक प्रदर्शन बनाए रखता है वह यह है कि यह एक फेंकने योग्य स्थानीय स्टैक के बाहर नहीं
चलेगा। कृपया इन्हें न हटाएँ, और SECURITY.md देखें — इन्हें
कमज़ोर करने वाले परिवर्तन, या लैब के बाहर चलाने के लिए बनाया गया एक स्टैंडअलोन एक्सप्लॉइट,
इस रिपॉज़िटरी के दायरे से बाहर हैं। यह पता लगाने के लिए कि कोई वास्तविक साइट प्रभावित है
या नहीं, poc/ का उपयोग करें — चेकर कुछ भी स्पर्श किए बिना वही प्रश्न का उत्तर देता है।
आपके नियंत्रण वाले होस्ट पर आधिकारिक उत्तर के लिए:
grep -n "array_merge(\$permissions, \$permissionsBase)" \
wp-content/plugins/popup-by-supsystic/classes/frame.php
कोई भी आउटपुट का अर्थ है होस्ट भेद्य है — वह पंक्ति ठीक वही है जिसे 1.13.0 ने बदला।
केवल उन सिस्टमों के विरुद्ध उपयोग करें जिनके आप स्वामी हैं या जिनका परीक्षण करने के लिए स्पष्ट रूप से अधिकृत हैं। SECURITY.md देखें।
lab/ में एक डिस्पोज़ेबल Docker स्टैक है जो भेद्यता को एंड-टू-एंड चलाता है, ताकि
विश्लेषण पर भरोसा करने के बजाय सत्यापित किया जा सके:
cd lab
./setup.sh # WordPress + plugin 1.12.0
../.venv/bin/python exploit_full_chain.py http://localhost:8080 --lab-confirm
./verify.sh # confirm from the database
./teardown.sh
इसका सबसे उपयोगी आउटपुट संस्करण तुलना है — तीन रिलीज़ों के विरुद्ध समान अनुरोध:
./setup.sh --plugin-version 1.11.2 # exploit succeeds
./setup.sh --plugin-version 1.12.0 # exploit succeeds
./setup.sh --plugin-version 1.13.0 # exploit fails at phase 2
लैब जानबूझकर भेद्य है और इसकी एक्सप्लॉइट स्क्रिप्ट एक WordPress प्रशासक बनाती है। सब कुछ
127.0.0.1पर बाइंड होता है, और स्क्रिप्ट स्थानीय लैब के अलावा किसी भी चीज़ के विरुद्ध नहीं चलेगी — देखें लैब एक्सप्लॉइट केवल लैब में चलता है। यह स्कैनर नहीं है; वास्तविक साइट की जाँच के लिएpoc/का उपयोग करें।
sub_wp_create_user_role को विशेषाधिकार प्राप्त
भूमिका पर सेट किए गए पॉपअप, और pl=pps&mod=popup&action=save ले जाने वाले
admin-ajax.php पर POST अनुरोध।क्वेरी और लॉग greps: §10।
.
├── README.md
├── SECURITY.md Scope, authorized-use policy, disclosure
├── LICENSE MIT
├── requirements.txt
├── docs/
│ └── ANALYSIS.md Full root-cause analysis and fix rationale
├── patches/
│ ├── 01-frame-permission-merge.diff array_merge() → _mergePermissions()
│ ├── 02-subscribe-role-allowlist-and-nonce.diff Role allowlist + dedicated nonce
│ └── 03-v1.12.0-confirmation-page-hardening.diff Unrelated hardening added in 1.12.0
├── poc/
│ └── cve_2026_18322_check.py Passive version fingerprint (GET only)
├── lab/ Disposable Docker lab — INTENTIONALLY VULNERABLE
│ ├── docker-compose.yml MariaDB + WordPress 1.12.0 + Mailpit (loopback only)
│ ├── setup.sh / teardown.sh Bring the lab up / destroy it
│ ├── exploit_full_chain.py Full attack chain — CREATES AN ADMIN; lab-gated
│ └── verify.sh Independent confirmation from the database
├── scripts/
│ └── fetch_versions.sh Export plugin versions from SVN and regenerate diffs
└── tests/
└── test_check.py Offline unit tests for the passive checker
pip install -r requirements.txt pytest
python3 -m pytest tests/ -v
सूट पूरी तरह ऑफ़लाइन है — यह रिकॉर्ड किए गए फ़िक्स्चर का उपयोग करता है, कभी लाइव लक्ष्यों का नहीं। यह 1.13.0 सीमा पर संस्करण ट्राइएज, लक्ष्य सामान्यीकरण, और प्रत्येक स्थिति तय करने वाले प्रमाण नियमों को कवर करता है।
सुरक्षा गुण केवल प्रलेखित नहीं बल्कि प्रमाणित हैं: कि एक पूर्ण स्कैन कोई स्थिति-परिवर्तनकारी
अनुरोध जारी नहीं करता, और यह कि HttpClient GET के अतिरिक्त कोई HTTP क्रिया उजागर नहीं
करता।
विकास के दौरान पाए गए दो रिग्रेशन लॉक इन हैं: एक CRLF readme.txt जो लाइन-एंकर्ड पार्सिंग
को विफल करता है, और एक HTTP त्रुटि पृष्ठ जिसे प्लगइन प्रमाण के रूप में गलत गिना जाता है।
अपस्ट्रीम स्रोतों से विश्लेषण को पुनरुत्पादित करने के लिए:
./scripts/fetch_versions.sh # exports 1.11.2 / 1.12.0 / 1.13.0 and regenerates patches/
MIT — LICENSE देखें। विश्लेषित प्लगइन GPLv2-or-later है और यहाँ पुनर्वितरित
नहीं किया गया है; scripts/fetch_versions.sh इसे आधिकारिक WordPress SVN रिपॉज़िटरी से
प्राप्त करता है।
| स्थिति | अर्थ |
|---|
VULNERABLE | प्लगइन < 1.13.0 पर पाया गया |
NOT_VULNERABLE | प्लगइन >= 1.13.0 पर पाया गया |
PLUGIN_DETECTED_VERSION_UNKNOWN | प्लगइन मौजूद, संस्करण निर्धारित नहीं — मैन्युअल रूप से सत्यापित करें |
PLUGIN_NOT_DETECTED | प्लगइन का कोई प्रमाण नहीं (अनुपस्थिति का प्रमाण नहीं) |
ERROR | लक्ष्य अगम्य या विकृत |