İçeriğe atla
Noroxi
Araştırma9 dk

Page Optimize: Kimlik Doğrulamasız Önbellek Seli DoS'u (CWE-770)

Page Optimize önbellek anahtarını ham istek URI'sinden üretiyor; kimlik doğrulamasız ziyaretçi, yinelenen dosyalarla diski ve inode'ları tüketebilir (CWE-770).

Noroxi EkibiSızma testi ve güvenlik danışmanlığı

Özet. Automattic'in 200.000'den fazla etkin kuruluma sahip WordPress performans eklentisi Page Optimize, önbellek anahtarını çözümlenmiş ve doğrulanmış dosya listesi yerine ham istek URI'sinden üretiyor. Kimlik doğrulamasız, uzaktaki bir ziyaretçi bu uyumsuzluğu kullanarak sunucuya sınırsız sayıda yinelenen önbellek dosyası yazdırabilir; sonuçta disk alanı ve inode'lar tükenir (CWE-770). Yazıda kusurlu kodu, kusura kavramsal olarak nasıl ulaşıldığını, erişilebilirlik etkisini, düzeltmeyi ve site sahipleri için tespit yöntemini bulacaksınız. Koordineli ifşa gereği üretici bilgilendirildi; çalışan kavram kanıtı, yama çıkana kadar yayımlanmayacak.

Anahtar kelimeler: Page Optimize, WordPress, CWE-770, kontrolsüz kaynak tüketimi, hizmet reddi, önbellek anahtarı, disk tükenmesi, inode tükenmesi.

1. Giriş

Page Optimize, bir sitenin JavaScript ve CSS dosyalarını birleştirip özel bir /_static/ uç noktası üzerinden sunar. 200.000'i aşan etkin kurulumuyla eklentinin istek işleme davranışı, WordPress saldırı yüzeyinin azımsanmayacak bir bölümünü kaplar.

Önbellekleme kodu erken, sık ve her ziyaretçi için çalıştığından incelemeye değer. Talep üzerine dosya oluşturan bir önbellek katmanının en kritik kararı, bir önbellek girdisini neyin tanımladığıdır. Bu kimlik gevşek seçilirse iş tasarrufu için yazılmış özellik, sınırsız iş üreten bir özelliğe dönüşür. Page Optimize bu kimliği yanlış seçiyor ve anonim bir ziyaretçi, önbelleği kontrolsüz kaynak tüketimine (CWE-770) sürükleyebiliyor. Aşağıdaki açıklama hem geliştiricilerin kalıbı tanıması hem de site sahiplerinin tespit edip sınırlaması için yazıldı.

2. Etkilenen bileşen

EklentiPage Optimize (Automattic) — slug page-optimize
Test edilen sürüm0.6.3 (test anında güncel/son sürüm)
Etkin kurulum200.000+
Güvenlik açığı türüKontrolsüz Kaynak Tüketimi / Hizmet Reddi
CWECWE-770 — Sınır veya Kısıtlama Olmadan Kaynak Tahsisi (ayrıca CWE-400)
Gereken yetkiYok — kimlik doğrulamasız, uzaktan
Kullanıcı etkileşimiYok
CVSS 3.15.3 (Orta) — AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L (diski tamamen dolduran sürekli bir sel altında 7.5 / A:H'ye doğru yükselir)
DurumTaze (0-day), kodda doğrulandı ve güncel sürümde localhost laboratuvarında yeniden üretildi; yama yok

3. Teknik analiz

3.1 Uç nokta herkese yanıt verir

Eklenti, eklenti-global kapsamda bir yönlendirici kurar. Her ön yüz isteğinde — oturumu kapalı ziyaretçiler dahil — istek URI'sini okur ve yol /_static/ ile başladığında denetimi statik servise devreder. Bu yolu koruyan hiçbir nonce, yetki ya da kimlik doğrulama denetimi yok:

php
// page-optimize.php (lines 28-31)if ( isset( $_SERVER['REQUEST_URI'] ) && '/_static/' === substr( $_SERVER['REQUEST_URI'], 0, 9 ) ) {    require_once __DIR__ . '/service.php';    exit;}

Birleştirilmiş varlıkları anonim ziyaretçilere sunmak tasarım gereğidir. Sorun, önbellek ıskasında (cache miss) başlar.

3.2 Önbellek anahtarı ham istek URI'sidir

Iskada servis, birleştirilmiş çıktıyı üretir ve iki dosya yazar: bir yük dosyası ve bir meta veri dosyası. İkisi de ham istek URI'sinin — sorgu dizesinin tamamıyla birlikte — md5'inden adlandırılır:

php
// service.php (lines 27-30, 73-76)$request_uri_hash = md5( $_SERVER['REQUEST_URI'] ); // <-- anahtar ham URI'dir, saldırgan denetimli$cache_file       = PAGE_OPTIMIZE_CACHE_DIR . "/page-optimize-cache-$request_uri_hash";$cache_file_meta  = PAGE_OPTIMIZE_CACHE_DIR . "/page-optimize-cache-meta-$request_uri_hash";...file_put_contents( $cache_file, $content );file_put_contents( $cache_file_meta, json_encode( array( 'headers' => $headers ) ) );

3.3 Anahtar, çıktının bağlı olmadığı baytları taşır

Can alıcı nokta burası. Servis hangi dosyaları birleştireceğine karar vermeden önce, sondaki ?version işaretinden itibaren her şeyi atar. Dolayısıyla sürüm/çöp son eki ne çözümlenen dosya listesine ne de çıktı içeriğine dokunur; buna rağmen §3.2'de hesaplanan md5 anahtarının parçası olmayı sürdürür:

php
// service.php (lines 117-121) — sürüm son eki dosya çözümlemesinden ÖNCE kırpılır$version_string_pos = strpos( $args, '?' );if ( false !== $version_string_pos ) {    $args = substr( $args, 0, $version_string_pos );}

Neden yanlış? Anahtar ile önbelleğe alınan değer birbirinden kopmuş durumda. Değer, çözümlenmiş ve doğrulanmış dosya listesinin saf bir fonksiyonu; anahtar ise saldırganın denetlediği ve çıktının attığı fazladan baytlarla dolu ham URI'nin fonksiyonu. Bayt bayt aynı çıktıyı veren iki istek bile ayrı anahtarlar altında saklanıyor. Doğru bir önbellek, girdiyi yalnızca değeri belirleyen girdilere göre anahtarlar; ne fazlası ne eksiği.

Kusuru geçici olmaktan çıkarıp kalıcılaştıran iki ayrıntı:

  • Önbellekleme varsayılan olarak açıktır: PAGE_OPTIMIZE_CACHE_DIR her zaman tanımlıdır (wp-content/cache/page_optimize).
  • Temizlik (page_optimize_cache_cleanup) yalnızca günlük WP-cron üzerinde çalışır ve yalnızca 24 saatten eski dosyaları siler; WP-cron ise yalnızca organik trafikte tetiklenir. Bu yüzden yeni oluşan dosyalar en az bir gün hayatta kalır.

4. Kusura nasıl ulaşılır (kavramsal)

Sink'e (kaynağa) ulaşmak yetki istemez, çünkü /_static/ yönlendirici tasarım gereği anonim GET isteklerini yanıtlar. Saldırgana yeten tek şey herkese açık bir bilgidir: sitenin gerçekten sunduğu geçerli bir varlık yolu. WordPress ile gelen yaygın çerçeve varlıkları hemen her yerde bulunur.

Gerisi §3.3'ten doğrudan gelir. Anahtar, çıktının bağlı olmadığı baytları içerdiği için aynı birleştirme fiilen sınırsız sayıda farklı anahtarla adreslenebilir: yok sayılan son ekin her çeşitlemesi yeni bir md5, dolayısıyla diskte aynı içeriği tutan yeni bir dosya çiftidir. IP başına bir sınır, girdi sayısında bir tavan, toplam önbellek boyutunda bir üst sınır, uç noktada bir hız kısıtı yok. Sunucunun ıska saydığı her istek baştan yazma yapar.

Sonuç, hacim/birikim türünden bir hizmet reddidir: sunucu, tek bir gerçek kaynağın asla gerektirmeyeceği kadar çok veri ve çok sayıda ayrı dosya yazmaya itilebilir. Her girdi iki dizin kaydı (iki inode) tükettiği ve en az bir gün kaldığı için hem boş alan bütçesi hem de inode tablosu risktedir. İstek dizisini, son ek değerlerini ve herhangi bir tekrar oynatma komutunu bilerek yayımlamıyoruz; yukarıdaki mekanizma sorunu anlamaya ve savunmaya yeter, işlevsel ayrıntı ise yamayı bekler.

5. Etki değerlendirmesi

Kimlik doğrulamasız, uzaktaki bir aktör, eklentinin önbellek dizinini kalıcı ve sınırsız biçimde büyütebilir. İki ayrı tükenme yolu var:

  1. Boş alan tükenmesi — yazılan baytlar sunulan kaynağın boyutuyla kabaca orantılı birikir; yükseltme oranı 1:1'e yakın olduğundan anlamlı bir kesinti sürekli istek hacmi ister.
  2. Inode tükenmesi — her girdi bir çift küçük dosya olduğundan tüketilen inode sayısı hızla tırmanır ve özellikle küçük VPS sunucularında, ham baytlar diski doldurmadan çok önce dosya sisteminin inode tablosunu bitirebilir.

Bu bütçelerden biri tükendiğinde yazma gerektiren tüm WordPress işlemleri — veritabanı geçici dosyaları, medya yüklemeleri, oturum verileri, ileri önbellekleme — başarısız olmaya başlar ve site çöker. Temel puanın kısmi erişilebilirlik etkisi (A:L) için 5.3 (Orta) seviyesinde kalmasının, diski ya da inode tablosunu baştan sona dolduran sürekli bir sel altında 7.5 / A:H'ye yükselmesinin nedeni budur. Gizlilik ya da bütünlük etkisi yoktur; mesele baştan sona erişilebilirliktir.

Kapsam adına kusurun ne olmadığını da kayda geçirelim. Dosya çözümlemesi sağlamlaştırılmıştır — .. ve null-byte geçişi engellenir, var olmayan yollar yazma yapmadan 404 döner — dolayısıyla bu bir yol geçişi ya da keyfi yazma-konumu kusuru değildir. İsteğe bağlı gzuncompress() dalı pratik bir açma-bombası (decompression bomb) vektörü değildir. Yönetici ayarları ise çekirdek Settings API'sini nonce ve manage_options ardında kullanır. Geriye kalan, dürüstçe Orta olarak sınıflandırılan gerçek bir kaynak tüketimi kusurudur; yüksek yükseltmeli bir yansıtıcı kılığına sokulmamıştır.

6. Düzeltme

Belirleyici düzeltme, önbellek anahtarını sakladığı değerle yeniden hizalamaktır:

php
// Önbelleği, ham istek URI'si yerine ÇÖZÜMLENMİŞ, DOĞRULANMIŞ dosya listesine göre anahtarla.// Sürüm son eki kırpıldıktan ve yollar çözümlenip doğrulandıktan sonra:$resolved = page_optimize_resolve_and_validate( $args ); // sıralı, doğrulanmış dosya listesi$request_uri_hash = md5( implode( "\0", $resolved ) );    // anahtar == tam olarak çıktıyı                                                           // belirleyen girdiler

Çözümlenmiş, doğrulanmış dosya listesine göre anahtarlamak, aynı isteğin tüm çöp son ek çeşitlemelerini tek bir girdide toplar ve çoğalmayı kaynağında keser. Derinlemesine savunma olarak eklenti ayrıca şunları yapmalı:

  • Önbellek girdisi sayısını ve/veya toplam önbellek dizini boyutunu sınırla; yalnızca 24 saatlik yaşa değil, bir üst sınıra göre tahliye et.
  • /_static/ uç noktasını istemci başına hız sınırlamasına tabi tut.
  • Temizliği yalnızca organik trafiğe bağlı günlük cron'da değil, yazma baskısında da tetikle.

Resmî düzeltilmiş sürüm çıkana kadar site sahipleri §7'deki önlemleri uygulamalı.

7. Site sahipleri için tespit ve sağlamlaştırma

Buradaki her madde yukarıdaki mekaniğe dayanır; uydurma ürün özelliği yoktur.

  • Önbellek dizinini izleyin. wp-content/cache/page_optimize dizinini yalnızca boyut değil dosya sayısı açısından anormal büyümeye karşı gözleyin. Aynı yük içeriğine sahip, hızla artan page-optimize-cache-* / page-optimize-cache-meta-* çiftleri en net göstergedir.
  • Yalnızca diski değil inode'ları da izleyin. Küçük sunucularda inode tablosu önce tükenebilir. Boş alanın yanında inode kullanımına da uyarı koyun (Linux'ta df -i).
  • Erişim günlüklerinde /_static/ kalıplarına bakın. Aynı alttaki varlığa yalnızca değişen bir sondaki son ekle ayrılan çok sayıda isteği, özellikle tek bir kaynaktan ya da dar bir kaynak kümesinden geleni arayın.
  • En az maruziyeti uygulayın. Birleştirme özelliği gerekmiyorsa eklentiyi devre dışı bırakın ya da kaldırın. Gerekiyorsa, altyapınızın elverdiği yerde diske önbelleklemeyi kısıtlamayı veya kapatmayı düşünün.
  • Sanal yama / WAF. Bir WAF ya da kenar kuralı, /_static/'i hız sınırına tabi tutabilir ve sorgusunda yüksek kardinaliteli, çıktıyla ilgisiz bir son ek taşıyan istekleri reddedebilir. Bunu geçici bir önlem sayın; önbellek anahtarı düzeltmesinin yerini tutmaz.
  • Zamanlanmış temizlik. Dahili temizlik WP-cron'un tetiklenmesine bağlı olduğundan, bayat önbellek dosyalarının gerçekten silinmesi için bunu gerçek bir sistem cron'u ile destekleyin.

8. Daha geniş ders

İlke basit ve geniş çapta geçerlidir: önbellek anahtarı, önbelleğe alınan değeri belirleyen girdilerin tam olarak bir fonksiyonu olmalı; ne fazlası ne eksiği. Anahtara, değerin yok saydığı saldırgan denetimli baytlar girerse önbellek aynı içeriğin sınırsız kopyasını saklamaya zorlanabilir; anahtar, değerin bağlı olduğu baytları atlarsa önbellek karışması ya da zehirlenmesi doğar. İki yöndeki sapma da birer kusurdur. Aynı disiplin memoizasyon, tekilleştirme tabloları, hız sınırlayıcı kovaları ve istek verisiyle anahtarlanan talep üzerine her kaynak için geçerlidir. Doğru anahtarlamayı açık kaynak tavanlarıyla — girdi sayısı, toplam boyut, istemci başına hız sınırı — birlikte kurun; çünkü sınırı olmayan bir önbellek, anonim bir ziyaretçinin sürebileceği bir tahsis motorudur.

9. Sorumlu ifşa

Bu araştırma koordineli (sorumlu) ifşa kapsamında yürütüldü. Üretici özel olarak bilgilendirildi; tam işlevsel ayrıntı — tam istek dizisi, son ek değerleri ve yeniden üretim komutları — bir düzeltme yayımlanana kadar bu herkese açık yazıdan bilerek çıkarıldı. Şimdi yayımlamanın amacı savunmacıdır: düzeltme hazırlanırken site sahiplerinin hata sınıfını tanımasını, tespit etmesini ve sınırlamasını sağlamak.

Tüm testler yalnızca, herkese açık GPL eklentisini çalıştıran, yalıtılmış ve localhost'a bağlı bir Docker WordPress laboratuvarında yapıldı. Üçüncü taraf ya da üretim sistemlerine erişilmedi; laboratuvar ve önbellek dizini testin ardından kaldırıldı. Noroxi LLC yalnızca yetkili güvenlik araştırması yürütür ve test etme izni olmayan sistemleri test etmez.

Kaynaklar

Savunma ve eğitim amacıyla yayımlanmıştır. Noroxi LLC yalnızca yetkili güvenlik araştırması yürütür; işlevsel istismar ayrıntısı, bir düzeltme yayımlanana kadar saklı tutulur.

Bu hafta sömürülenler · her pazartesi

KEV'e girenler, yeni PoC'ler, WordPress triyajı ve bu hafta ne yapmalı. Hesap gerekmez.

Adresiniz yalnızca bu e-posta için kullanılır; üçüncü tarafla paylaşılmaz.

Sıradaki

Premium Addons for Elementor: Bozuk Erişim Denetimi (CWE-862)