XML External Entity Injection (XXE) Nedir?

Sefa Ozan
9 min readSep 10, 2021

--

Kullanıcıdan alınan XML belgesi parse edilirken araya kendi döküman, entity tanımlarımızı yerleştirmemizle “Arbitrary File Read”, “Command Execution” gibi kritik zaafiyetler elde edebildiğimiz bir güvenlik açığıdır.

XXE güvenlik açığı sayesinde aşağıdaki saldırılar gerçekleştirilebilir.

  • SSRF (sunucu taraflı istek sahteciliği)
  • DDOS
  • Sunucudan Dosya Okuma
  • RCE

Yazının ilerleyen kısımlarında, XXE ile yukarıda belirtilen güvenlik açıklarını nasıl birleştirebileceğimizi anlatacağım. Bu kısma geçmeden önce XXE zafiyetini daha iyi anlayabilmemiz için XML’in yapısını inceleyelim.

XML Nedir ?

XML (Extensible Markup Language ya da Türkçesiyle Genişletilebilir İşaretleme Dili), hem insanlar hem bilgi işlem sistemleri tarafından kolayca okunabilecek dokümanlar oluşturmaya yarayan bir işaretleme dilidir. Bu özelliği ile veri saklamanın yanında farklı sistemler arasında veri alışverişi yapmaya yarayan bir ara format görevi de görür.

XML’ in Özellikleri & Kuralları Nelerdir?

Basit bir XML belgesi
  • Bütün XML dökümanlarının bir kök elemanı vardır. Bu kök elemanı diğer bütün elemanları kapsar. Yukarıdaki örnekte <note> bir kök elemanıdır.
  • XML etiketleri büyük-küçük harf’e duyarlıdır. Örneğin <isim> ve <İsim> etiketleri XML’e göre birbirinden farklıdır. Bu nedenle açılış ve kapanış etiketlerinin tamamen aynı biçimde yazıldığına dikkat edilmelidir.
  • Bütün XML elemanlarının kapanma etiketi olmalıdır. Örneğin </isim>, </soyisim> vb.
  • XML belgelerinde, < " ' & > karakterleri etiket tanımlamak haricinde kullanılmaz. Bu karakterleri kullanmanız gerekiyorsa CDATA ile kullanabilirsiniz. Aşağıda CDATA’nın örnek bir kullanımı gösterilmiştir.

<data><![CDATA[ < " ' & > ]]></data>

Document Type Definition (DTD) Nedir?

Bir XML belgesinin hangi etiketleri içerebileceği, etiketlerin sahip olabileceği özellikleri, hangi elemanların diğer hangi elemanları içerebileceği gibi dil yapısı bilgileri o uygulama için geliştirilmiş olan DTD adlandırılan belge tanımlama dosyasında belirtilir.

DTD’ler, XML belgesinin içerisinde (internal) tanımlanabileceği gibi başka bir kaynak (external) üzerinden de dahil edilebilir. Harici bir kaynağa erişim sağlamak için XML’in desteklendiği SYSTEM niteliğini kullanıyoruz. SYSTEM niteliği, HTTP ve FILE gibi protokolleri desteklemektedir.

Aşağıdaki resimlerde internal ve external dtd’lerin nasıl tamınlanabileceği gösterilmiştir.

external dtd
Note.dtd dosyasının içeriği

Aşağıdaki XML belgesini incelediğimizde;

  • note adında bir döküman tanımının olduğunu ve bu döküman tanımının içerisinde isim adında bir entity tanımlandığını görüyoruz.
  • Entity’leri programlama dillerindeki değişkenler olarak düşünebiliriz. Aşağıdaki entity tanımında isim değişkeninin içerisine “Sefa” değerinin atıldığını görüyoruz.
  • <email> etikeninin olduğu satıra baktığımızda, daha demin tanımlamış olduğumuz entity’nin çağırıldığını görüyoruz. XML parser tarafından aşağıdaki XML belgesi parse edildiğinde &isim; değişkeninin olduğu yere Sefa değeri yazılacaktır.
internal dtd

Temel anlamda XML’in ne olduğunu anladığımıza göre XXE zafiyetinin nasıl sömürülebileceğine geçebiliriz.

Senaryo #1

Aşağıdaki form’u doldurup, BurpSuite ile sunucuya giden isteği yakalıyoruz.

BurpSuite ile yakaladığımız isteği incelediğimizde. Sunucuya bir XML belgesi gönderildiğini görüyoruz. Gönderilen XML’e baktığımızda kullanıcıdan alınan email bilgisinin ekrana bastırıldığını görüyoruz. Bu da akıllara XSS’i getiriyor.

Email değerini XSS payload’ı ile değiştirip sunucuya istek yaptığımızda XSS’in çalışmadığını görürüz. Bunun sebebi XML’in kurallarında da belirttiğimiz gibi < " ' & >gibi karakterleri kullanamıyor olmamızdır. XML belgesinde gönderilen değer ile XSS yapmak için CDATA kulllanılmalıdır.

CDATA kullanılarak nasıl XSS yapılabileceği ileride daha detaylı anlatılcaktır.

Kendimizin bir döküman tanımı oluşturabilip oluşuramayacağını anlamak için gönderilen XML üzerinde bir döküman tanımı yapıp sunucuya öyle istekte bulunuyoruz.

Kullanıcıdan alınan email değerinin ekrana basıldığını gördüğümüzden dolayı &isim;‘i email etiketinde çağırıyoruz.

Sunucudan dönen yanıtta Sefa’yı görebildiğimize göre döküman tanımı yapabiliyoruz.

Kendi döküman tanımımızı yapabildiğimizi öğrendikten sonra bize bir sürü yeni özellik açılıyor.

Döküman tanımını external bir şekilde yapabileceğimizi yukarıda bahsetmiştik. Aynı şekilde entity tanımını da external bir şekilde yapabiliriz. Bu sayede sistemden istediğimiz dosyayı okuyup kendi oluşturduğumuz entity’e aktarıp bunu da ekrana bastırta biliriz.

Aşağıdaki XML dosyası;

  • Sunucudaki gizliDosya.txt isimli dosyayı okuyup bu dosyanın içeriğini, oku isimli değişkene kaydedecek.
  • &oku; sayesinde de bu dosyanın içeriğini ekrana bastırılacak.

Aşağıdaki yanıtı incelediğimizde, gizliDosya.txt dosyasının içeriğini görebiliyoruz.

Sonuc :

Yukarıdaki saldırının geçekleşmesinin sebebi kullanıcıdan alınan alınan değerin tekrar ekrana bastırılmasıydı.

Kodda ufak bir değişiklik yaparak kullanıcıdan alınan input’un ekrana basılmamasını sağlıyoruz.

Senaryo #2

Peki, kullanıcıya hiçbir şey bastırılmamış olsaydı bu XXE zafiyetini nasıl sömürebilirdik?

Aşağıdaki isteği incelediğimizde bir önceki örnekten farklı olarak kullanıcıdan alınan email bilgisinin kullanıcıya tekrar bastırılmadığını görüyoruz.

Ekrana herhangi bir şey bastırılmadığından dolayı aşağıdaki payload’ımız da çalışmayacaktır. (Aslında çalışıyor ama ekrana hiçbir şey bastırılmadığından dolayı, biz zafiyetin olup olmadığını göremiyoruz)

Zafiyetin olup olmadığını tespit etmemiz için XXE zafiyetini içeren sunucuyu kendi sunucumuza istek yaptırtmaya zorluyoruz. (SSRF)

SYSTEM operantı sayesinde External entity tanımını yapabileceğimizi yukarıda bahsetmiştik.

Aşağıdaki XML belgesini gören parser;

  • note adında bir döküman tanımının yapıldığını,
  • istekYap adında bir entity’nin tanımlandığını ve bu entity’nin SYSTEM operantının kullanıldığını görünce “https://xxeilessrf.free.beeceptor.com/poc” adresine bir istekte bulunacak.

Kendi sunucumuzun loglarına baktığımızda /poc dizinine bir istek yapıldığını görüyoruz.

Senaryo #3

Bu senaryoyu anlayabilmeniz için öncelikle parameter entity kavramının ne olduğuna değinmemiz gerekiyor.

Parameter Entity

  • Entity’lerin bir değişken olduğunu unutmayalım. Parameter entity de bir değişkendir.
  • Parameter entity’nin çalışma mantığı normal entity ile aynıdır. Tek fark; parameter entity, sadece oluşturulduğu döküman tanımında çağırılabilir.
  • Paremeter Entity tanınımının yapılabilmesi için entity isminden önce % işareti konulur.
  • Parameter entity sayesinde başka bir entity tanımının yapılması da mümkündür ve bu kabiliyet Blind XXE ile sunucudan veri çıkarmada sıkça kullanılır.

Parameter Entity’nin ne olduğunu daha iyi anlayabilmemiz için aşağıdaki örneği inceleyelim.

  • note adında bir döküman tanımı yapılmış. ( <!DOCTYPE note [ )
  • parameterEntity adında bir parameter entity tanımlanmış.( % parameterEntity )
  • Bu entity’nin içeriği de başka bir entity’dir. ( <!ENTITY isim ‘Sefa’> )

Parse işlemi geldiğinde %parameterEntity; değişkenini gören parser; oraya %parameterEntity; değişkeninin değerini yani <!ENTITY isim ‘Sefa’> değerini koyacak.

Son durum aşağıdaki gibi olacaktır. isim değişkeni normal bir entity olduğundan dolayı email etiketinde çağırılabiliyor.

Not: Kodda yaptığım ufak değişikliği geri aldım.

Yukarıdaki yanıtta da gördüğümüz gibi parameter entity kavramını kullanarak da istediğimiz mesajları ekrana bastırabiliyoruz.

Senaryo #4

Kullanıcıdan alınan inputların ekrana basılmadığını varsayalım. Bu durumda sistemdeki dosyaları okuyabilmemiz için dosyanın içeriğini içeren bir isteği kendi sunucumuza yapmamız gerekiyor. Bu sayede, kendi sunucumuza düşen logları incelediğimizde okumak istediğimiz dosyanın içeriğini görebiliriz.

Aşağıdaki XML belgesi XML Parser tarafından parse edildiğinde;

  • Sistemdeki gizliDosya.txt dosyasının içeriğini okuyup dosyaOku değişkenine kaydedecek.
  • parameterEntity değişkeninde bir başka entity tanımı yapıldığını görecek.
  • parameterEntity değişkeninde tanımlanan entity’nin yaptığı iş; https://xxeilessrf.free.beeceptor.com/ ‘a gizliDosya.txt’nin içeriği ile bir istekte bulunmaktır.

&parameterEntity; parse edildikten sonra aşağıdaki gibi bir görüntü olacak.

Yukarıda hazırladığımız payload’ı yolladığımızda; bu payload’ın bir işe yaramadığını görüyoruz.

Gördüğünüz gibi kendi sunucumuza hiçbir istek gelmedi.

Bunun sebebi aşağıda yazan açıklamada.

Internal tanımlanan DTD’lerde parameter entity’ler;

  • Parameter entity tanımlarken kullanılabilirken,
  • Bir başka parameter entity’de referans çağırılarak kullanılamazlar.

*** Bu kural external yüklenen DTD’ler için geçerli değildir. ** *

Bu kısıtlamadan kurtulmak için döküman tanımımızı external bir şekilde yapıyoruz.

Dışarıdan yüklediğimiz dtd dosyamızın içeriği aşağıdaki gibidir.

evil.dtd

Payload’ımızı aşağıdaki gibi değiştirip tekrar istek yaptığımızıda yine çalışmadığını görüyoruz.

Gördüğünüz gibi kendi sunucumuza hiçbir istek gelmedi.

Bu isteğin gelmemesi gerçekten beni şaşırtmıştı. XML herhangi bir hata vermediğinden dolayı sorunun nerede olduğunu anlamamıştım. Uzun bir araştırma sürecinden sonra, sorunun okumak istediğimiz dosyanın içeriğinden kaynaklandığını anladım.

Okumak istediğimiz dosyanın içeriğine baktığımızda birkaç sorun ile karşılaşılıyoruz. Bunlar;

  • Türkçeye özgü karakter içeriyor. (ş)
  • Kelimelerin arasında boşluklar var. (Sebebini bilmediğim bir şekilde URL encode yapmıyor)
Okuyacağımız dosyanın içeriği

Dosyanın içeriğini, ArtikIstekYapabiliyorum ile değiştiriyoruz.

Okuyacağımız dosyanın içeriği

Tekrar istek yapıyoruz.

Gördüğünüz gibi kendi sunucumuza istek geliyor. Bu sayede gizliDosya.txt dosyasının içeriğini okumayı başardık.

Boşluk ya da Türkçe karakter içeren dosyaları nasıl okuyabileceğimizi araştırırken şu blog ile karşılaştım. Burada adam, okumak istediği dosyanın içeriğini önce base64 ile encode ediyor. Bu sayede, dosyada herhangi bir boşluk ya da Türkçe karakter kalmıyordu.

Ben de okumak istediğim dosyanın içeriğini aşağıdaki gibi base64 ile encode edip öyle sunucuma istekte bulundum.

Gördüğünüz gibi sunucuma base64 ile encode’lanmış bir string geldi.

Gelen bu string’i decode ettiğimizde; önceden okuyamadığımız dosyanın içeriği ile karşılaşıyoruz.

Senaryo #5.1

Eğer okumak istediğimiz dosyanın içerisinde XML belgemizi bozabilecek < " ' & >karakterler varsa bu gibi dosyaları dışarı çıkarabilmek için dosyanın içeriğini yine base64 ile encode’layabiliriz.

Okuyacağımız dosyanın içeriği
evil.dtd

Gelen bu string’i decode ettiğimde dosyanın içeriği ile karşılaşıyoruz.

Senaryo #5.2

Eğer herhangi bir sebepten dolayı encryption işlemi gerçekleştiremezsek. Kullanabileceğimiz bir başka yöntem daha var.

Okuyacağımız dosyanın içeriği

CDATA içerisine yazılan özel karakterler özelliklerini yitirir normal bir string olarak yorumlanırlar. Bu sayede < > gibi karakterler XML’in yapısını bozmamış olur.

Sunucudan dönen yanıtta, okumak istediğimiz dosyanın içeriğini görebiliyoruz.

Senaryo #6.1

Kullanıcının gönderdiği data üzerinde herhangi bir sanitization ya da output encoding olmadan ekrana basıldığı durumlarda aşağıdaki paylaod’ı kullanarak XSS gerçekleştirebiliriz.

poc

Senaryo #6.2

Parameter Entity kullanarak da aşağıdaki gibi XSS çalıştırabiliriz.

poc

XXE Güvenlik Açığının Oluşmasını Engellemek İçin Alınacak Önlemler Nelerdir?

  • Tüm XML parser ve kütüphaneler güncellenmeli ve yamaları yüklenmelidir.
  • Uygulamada bulunan tüm XML parser’ların XML External Entity özelliğinin kapatılmalıdır.
  • Uygulamanın ihtiyaç duymadığı ya da kullanmak istemediği potansiyel olarak tehlikeli XML özellikleri devre dışı bırakılmalıdır.
  • XML yerine JSON gibi daha az karmaşık veri biçimlerini kullanabilirsiniz.

Kaynaklar

--

--

Sefa Ozan
Sefa Ozan

No responses yet