LFI (Local File Inclusion)
Last updated
Last updated
Local File Inclusion kısa haliyle LFI. Arama motorumuza bir web sitesinin adresini girdiğimiz zaman o web sitesinin dosyalarını bulunduran sunucuya istek atmış oluyoruz. Kendisinden ilgili dosyaları bize göndermesini istiyoruz.
Normal bir web sitesinde normal bir kullanıcının görmesi gereken dosyalar olduğu gibi görmemesi gereken dosyalar da mevcuttur. İşte LFI (Local File Inclusion) zafiyeti ile, bir saldırgan, web uygulamasının çalıştığı sunucudaki yerel dosyaları okumasına, çalıştırmasına veya dahil etmesine olanak tanıyan bir güvenlik zafiyetidir. Bu zafiyetin ortaya çıkma şekli kullanıcıdan alınan inputun kontrol edilmeden ya da kontrollerin atlatılmasıyla inputun işleme sokulmasıyla ortaya çıkar.
Bazı durumlarda web uygulamaları ekranda gösterecek olacağı sayfaları, fotoğrafları, videoları vb. parametre olarak alır.
Web sitesini geliştiren kişi ?file parametresiyle sunucuda bulunan dosyaları ekrana yansıtabiliyor. Ama aynı şekilde kötü niyetli insanlar bu parametreyi manipüle ederek normal kullanıcıların görmemesi gereken dosya içeriklerini görebiliyor.
Teorik anlamda LFI bu şekilde kod tarafına baktığımızda basitçe şöyle çalışıyor.
Yukarıda ki kodun açıklaması şu şekildedir. URL'de bulunan file parametresi alınıp include() fonksiyonu ile sayfa içine dahil ediliyor. Gördüğünüz gibi herhangi bir kontrol durumu gerçekleşmiyor.
LFI hakkında temel bilgileri edindik şimdi normal kullanıcının görmemesi gereken bazı hassas bilgileri içeren dizinlerden bahsedelim.
/etc/passwd : Linux/Unix sistemlerinde kullanıcı bilgilerini içerir. Her kullanıcı için bir satır bulunur ve kullanıcı adı, kullanıcı ID'si, grup ID'si, yorum alanı, ana dizin ve kullanıcı kabuğu gibi bilgileri içerir.
/etc/shadow : Kullanıcı parolalarının hash'lerini içerir. Bu dosya sadece root veya yetkili kullanıcılar tarafından okunabilir.
/var/log/ : Çeşitli sistem ve uygulama log dosyalarını içerir. Bu log dosyaları sistemin ve uygulamaların çalışma durumunu, hataları ve kullanıcı aktivitelerini içerir.
LFI sayesinde parametre üzerinde işlem yaparak hassas bilgi içeren bu dosyalara ulaşabiliriz. Daha da ileriye gitmek istersek LFI ile RCE (Remote Code Execution) yapılabilir. Adım adım, labları çözerek ilerleyeceğiz.
Fotoğrafta gördüğünüz gibi normal şartlarda url de bulunan ?file parametresiyle /var/www/app/CVs/userCv.pdf dosyasına ulaşmamız gerekirken, ?file parametresini manipüle ederek /etc/passwd dosyasına ulaşmış oluyoruz.
Fotoğrafta görünen ?file parametresi dikkatinizi çekmiştir, ../../../../../../../../etc/passwd . Parametre değerimizde bulunan ../ bir önceki dizine çıkmak anlamına gelir. Bir sürü kullanmamızın sebebi ise ne kadar kullanırsak kullanalım en fazla / (kök) dizine gidebiliriz.
İlk labımızda bir input alanına girdiğimiz dosya isimlerini bize getiren bir mekanizma var. Olmayan bir dosya adını girdiğimizde ise bir hata alıyoruz.
İlk labımız olduğu için herhangi bir kısıtlama olmadığını düşünüp ve çıkan hataya baktığımızda input alanına yazdığımız içeriğin direkt bir şekilde include (dahil edildiğini) görünce deneme amaçlı input alanına yazıyorum.
Evet herhangi bir kontrol yapılmamış ve direkt bir şekile /etc/passwd dosyasımızın içeriğine ulaşmış olduk.
İkinci laba baktığımızda görünüş olarak diğer labla birebir aynı. Deneme amaçlı rastgele bir girdi girdiğimizde, hata mesajıyla karşılaşıyoruz.
Input kısmına admin yazmama rağmen verdiği hata mesajında includes/admin şeklinde hata veriyor. Yani includes klasörünün altında admini arıyor. Daha önce yaptığımız gibi input kısmına /etc/passwd yazarsak bize şöyle bir hata verecektir.
Bizim burada ilk yapmamız gereken şey includes klasöründen çıkmak olacaktır. Bunun için ../ kullanacağız. Bir tane kullanmak bizim için yetmiyecektir. / dizinine ulaşmamız lazım bu yüzden fazla fazla ../ karakterlerini kullanabiliriz.
.. / karakterleriyle / klasörüne kadar çıktım buradan sonra /etc/passwd klasörüne ulaşabilirim.
2.soruda labda hangi fonksiyonun kullanıldığı sorulmuş daha öncesinde hata mesajımızda include() fonksiyonunu görmüştük.
3.labda baktığımızda yine aynı içerik sayfası karşımızda deneme amaçlı rastgele bir şey yazıyorum ve çıkan hatayı kontrol ediyorum.
Input alanında deneme diye giriş yapıyorum ve hataya baktığımda yine includes dizinini içinde olduğunu görüyoruz 2.labdan farkı girmiş olduğumuz dosyanın sonuna .php eklentisi ile birleştirmesi. 2.labda dizin içinden nasıl çıkacağımızı öğrendik ama uzantıyı nasıl devre dışı bırakacağız bunu bilmiyoruz. .php kısmını devre dışı bırakmak için ise Null Byte denilen bir karakteri kullanacağız.
Null Byte PHP 5.3.4 sürümünden sonra kaldırılmış durumda güncel sürümlerde işe yaramayacaktır. Null byteın amacı bulunduğu yerden sonra gelen karakterlerin işleme tabi tutulmaması için kullanılır. Bizim senaryomuzda da .php yi engellemek için kullanılabilir.
İlk başta içinde bulunduğumuz klasörden, / (kök) dizine kadar geri çıkalım ve sonrasında php uzantısını görmemesi için Null Byte kullanabiliriz. Bu dediklerimizi uygulayınca şöyle bir payload karşımıza çıkıyor:
Doğru bir payload yazmamıza rağmen istediğimiz çıktıyı alamıyoruz. Yazdığımız içerik url de karşımıza çıkıyor. Deneme işlemini bu sefer de url üzerinden yapalım ve çıktımıza bakalım.
Ve evet aynı payload ile url üzerinden denediğimizde çıktıyı alabiliyoruz.
Yine aynı ekran bizi karşılıyor, deneme amaçlı rastgele bir değer giriyoruz.
Göründüğü kadarıyla herhangi bir klasörün içinde değiliz geriye gitmemiz gerekmiyor. Direkt bir şekilde /etc/passwd dosyasın ulaşmaya çalışalım.
Kodu görütüleyemiyoruz. Bazı sistemlerde belirli dizinlere karşı filtreleme kullanılır. LFI zafiyeti olduğunu kanıtlamak /etc/passwd dosyası kullanıldığı için siteyi hazırlayan kişi burada bir filtreleme işlemi yapmış olabilir. Eğer inputtan /etc/passwd değeri gelirse gösterme demiş olabilir.
Burada daha önce kullandığımız dizin değiştirme karakterlerini kullanacağız. Eğer direkt bir şekilde /etc/passwd dosyasına ulaşamıyorsam bir dizin geri çıkarım ve sonrasında tekrar dizin değiştiririm. Bu dediklerim sonucunda şöyle bir input karşımıza çıkıyor.
Yukarıda gördüğünüz payload işe yarayacaktır ama başka bir payload göstermek istiyorum. ../ karakteri gibi . (nokta) karakteri ile de biz bu işlemleri yapabiliyoruz. . (nokta) karakteri içinde bulunduğu dizin anlamına geliyor. Anlamadıysanız cd .
yaptığınızda herhangi bir dizin değişikliğinin olmadığını görmüş olmanız lazım aynı şekilde bizde payloadımızda bu . (nokta) karakterini kullanacağız.
Hatamızda gördüğümüz fonksiyonun ismini cevap olarak giriyoruz.
Daha hızlı ilerleyebilmek için ../../../../../../../../../../etc/passwd
şeklinde deneme yapıyorum.
Girmiş olduğum inputun içinde ../ karakterleri olmasına göre sarı ile belirtmiş olduğum hata mesajında herhangi bir ../ karakteri göremiyorum. Bu yüzden includes klasöründen çıkamıyorum. Yazılımcı siteyi hazırlarken ../ karakterlerini silnmesi için bir kod yazmış olmalı.
Bu durum içinde şöyle bir taktik geliştiriyoruz. 4 tane . (nokta) işaretinden sonra 2 tane / işareti koyuyoruz ve sistem ilk gördüğü ../ karakter grubunu siliyor. Sonrasında ekstradan koyduğumuz noktalar ve slash işareti birleşip yeni bir ../ karkater grubu oluşturuyor.
Belirttiğim duruma göre bir payload hazırladığımızda /etc/passwd dosyasına ulaşıyoruz.
Tekrardan devam ediyoruz aynı sayfa bizi karşılıyor ve yukarıda belirttiğim komutu girdiğimde bana sadece THM-profile altında bulunan dosyalara erişimim olduğunu söylüyor ve input kısmında bulunan placeholder kısmına bakarsanız bize örnek bir dizin gösteriyor.
Bize gösterdiği örnek dizin üzerinden bir şeyler yapabiliriz gibi geliyor o dizinden çıkmamız gerekiyor. Daha önce öğrendiğimiz ../ karakterlerine göre tekrardan payload hazırlayalım ama şuna dikkat etmeliyiz payloadımızın başlangıcı THM-profile ile başlayacak.
Soru olarak baktığımızda nasıl /etc/passwd klasörüne ulaştıysak /etc/ os-release klasörüne erişmemiz isteniyor ve cevabı 12.04 olarak buluyoruz.
Normalde girdiğimiz inputu göndermek istediğimizde girdiğimiz değer GET parametresiyle gidiyor. Soruda da belirttiği gibi bunu POST yapmamız isteniyor. Bunu nasıl yapacağız Burp Suite sayesinde yapacağız. Giden isteği durdurup istek tipini değiştireceğiz.
İnput alanına girdiğimiz değer GET parametresiyle giderken şu şekilde gidiyor.
Turuncu şekilde işaretlenmiş olan özellik ile istek tipini değiştirebiliriz. Artık isteğimiz bu şekilde gözükecektir.
Soruda bizden /etc/flag1 dosyasının içindeki flag değerini istiyor.
Aynı şekilde passwd yerine flag1 yazdığımızda cevaba ulaşıyoruz.
Sayfayı açar açmaz karşımıza böyle bir ekran geliyor. Sadece adminler erişebilir diye bir yazı var.
Bu sayfa benim admin olup olmadığımı nerde biliyor. Sayfayı yenileyelim Burp Suite ile araya girip giden gelen paketleri kontrol edelim.
Giden paketler arasında 11. satırda bir Cookie değeri var ve Guest değerini almış. Bu Cookie değerini manipüle ettiğimde yukarıda ki uyarı ekranında ki Welcome Guest yazısı da değişiyor.
Yani ben Cookie değerine ne yazarsam ekrana o çıktıyı veriyor. Bu olayı farkına vardıktan sonra Cooki değerine ../../../../../etc/flag2
diye değer veriyorum.
Ama görüyoruz ki girmiş olduğumuz payloadın sonuna php uzantısı eklenmiş bu durumu aşmak içinde Null Byte kullanıyoruz ../../../../../etc/flag2%00
ve cevaba ulaşıyoruz.
Burp Suite ile 3.challenge a baktığımızda yine cookie değeri gönderiliyor. Hiç bir şeye dokunmadan GET metodu ile gönderme işlemi yaptığımızda / karakterlerini siliyor ve sona php eklentisi ekliyor. Bu yüzden POST metodu ve Null Byte ekleyerek deniyorum.
Bu sefer / karakterlerimi silmedi pathimi doğru yazdı ama yine istediğim sonuca ulaşamadım. Arkada giden Cookie değerini admin olarak gönderirsem belki işe yarayabilir. Boş yere Cookie değerini göndermez diye düşünüyorum.
Ve bu payload sonucunda çıktıyı alıyoruz.
Ama bize /etc/flag3 dosyasının içindeki değer soruluyor. Aynı işlemleri yapıyoruz sadece passwd yazılacak yere flag3 yazıyoruz ve cevaba ulaşıyoruz.
Soruda bizden hostname komutunu çalıştırmamız isteniyor. İşte burada dışarıdan göndereceğimiz bir komut dosyası olacak ve bu dosya sayesinde hostname komudumuzu çalıştıracağız.
Öncelikle bir PHP dosyası oluşturuyorum. Hostname komudunu çalıştırmalı ve ekrana yansıtmasını istiyorum.
Sonrasında bu dosyayı bir sunucu gibi servis etmem gerekiyor. Bunun için PHP dosyasını oluşturduğum dizinden power shell üzerinden şu komutu çalıştırıyorum.
Bu komut sayesinde Try Hack Me'ye bağlandığımız adres üzerinden dosyalarımızı paylaşabiliyoruz. ipconfig yazarak OpenVPN'in bize sağlamış olduğu ip adresinin 8000 (default) portundan dosyalara erişebiliriz.
Input alanına da bu dosyanın adresini vereceğiz ve sistem php dosyasında bulunan kodu çalıştırıp ekranımıza yazdırıcak.
Linkin başında ki ip OpenVPN'in bağlanırken bana sağlamış olduğu ip adresi.
Ve cevaba ulaşıyoruz.
Bu konuyu uygulamalı anlatmak için labını kullanacağım.
Kaynak:
Bu yazı tarafından hazırlanmıştır.