《Web前端黑客技术揭秘》笔记

一、Web安全的关键点

浏览器的同源策略

同源策略:不同域的客户端脚本在没明确授权的情况下,不能读写对方的资源。

http://www.topo.com 的不同域/同域

站点是否同域原因
https://www.topo.com不同域协议不同,https与http是不同的协议
http://alpha.topo.com不同域域名不同,alpha子域与www子域不同
http://topo.com不同域域名不同,顶级域与www子域不是一个概念
http://www.topo.com:8080不同域端口不同,8080与默认端口80不同
http://www.topo.com/a/同域满足同协议、同域名、同端口,只是目录不同

默认情况下是不允许跨域访问的,只有目标站点(http://www.topo.com)明确返回HTTP响应头:Access-Control-Allow-Origin: http://www.evil.com,那么www.evil.com上的客户端脚本才有权通过AJAX对www.topo.com上的数据进行读写操作。

AJAX

Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
AJAX是在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。

DOM

Document Object Mode (文档对象模型)
中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。

二、前端基础

Web安全事件的角色:W3C、浏览器厂商、Web厂商、攻击者、用户。

URL

Uniform Resource Locator(统一资源定位符)
protocol://hostname[:port]/path/[;parameters][?query]#fragment

URL有三类编码方式:escape、encodeURI、encodeURIComponent,对应着三个解码函数unescape、decodeURI、decodeURIComponent。

HTTP

Hyper Text Transfer Protocol(超文本传输协议)
《Web前端黑客技术揭秘》笔记

详情

JavaScript

<script>alert(1)</script>
<img src=# onerror="alert(1)" />
<input type='text' value="x" onmouseover="alert(1)" />
<iframe src="javascript:alert(1)"></iframe>
<a herf="javascript:alert(1)">x</a>
...

Cookie

[name] [value] [domain] [path] [expires] [httponly] [secure]
名称、值、所属域名、所属相对根路径、过期时间、是否有HttpOnly标志、是否有Secure标志

三、前端黑客之XSS

Cross Site Script(跨站脚本),发生在目标网站中目标用户的浏览器层面上,当用户浏览器渲染整个HTML文档的过程中出现了不被预期的脚本指令并执行,XSS就会产生。

反射型XSS

发出请求时,XSS代码出现在URL中,作为输入提交到服务端,服务端解析后响应,在响应内容中出现这段XSS代码,最后浏览器解析执行。

存储型XSS

提交的XSS代码会存储在服务端,下次请求目标页面时不用再提交XSS代码。

DOM型XSS

DOM型XSS的XSS代码不需要服务器解析响应的直接参与,触发XSS靠的是浏览器端的DOM解析,可以认为完全是客户端的事情。

常见的输入点:

document.URL
document.URLUnencoded
document.location
document.referrer
document.cookie
windows.location
windows.name

常见输出点:

直接输出HTML内容:
document.write()
document.writeln()
document.body.innerHtml=

直接修改DOM树:
document.forms[0].action=
document.attachEvent()
document.create()
document.execCommand()
document.body. ...()

替换document URL
document.location=
document.location.hostname=
document.location.replace()
document.location.asign()
document.URL=
window.navigate()

打开或修改新窗口
document.open()
window.open()
window.location.href=

直接执行脚本
eval()
window.execScript()
window.setInterval()
window.setTimeout()

XSS危害:挂马、盗取用户Cookie、Dos客户端浏览器、钓鱼攻击、劫持用户Web行为、蠕虫式挂马刷广告等等。

四、前端黑客之CSRF

Cross-site request forgery(跨站请求伪造跨站的请求)

跨站点的请求:跨站点请求的来源一般为其他站点,但是也可以来自本站。
请求是伪造的:发出的请求不是用户的意愿的请求。

HTML CSRF

发起的CSRF请求都属于HTML元素发出的,HTML中能设置href/src等连接的标签都可以发起一个GET请求,如:

<link href="">
<img src="">
<meta http-equiv="refresh" content="0; url=">
<iframe src="">
<script src="">
...

CSS中的:
@import ""
background:url("")

CSRF危害:篡改目标网站上的用户数据、盗取用户隐私数据、作物其他攻击向量的辅助攻击手法、传播CSRF蠕虫。

五、前端黑客之界面操作劫持

界面操作劫持攻击是一种基于视觉欺骗的Web会话劫持攻击,它通过在网页的可见输入控件上覆盖一个不可见的框(iframe),让用户以为在操作可见控件,但是实际上用户的操作行为被其不可见的框所劫持,执行不可见框中的恶意劫持代码,从而完成在用户不知情的情况下窃取敏感信息,篡改数据等攻击。

分类

点击劫持

劫持的是用户的鼠标点击操作,主要的劫持目标是有重要会话交互的页面,比如,银行交易页面、后台管理页面或者劫持用户的摄像头和麦克风。

拖放劫持

在现在的Web应用中,有一些需要用户采用鼠标拖放完成的操作,而且用户也经常在浏览器中使用鼠标拖放操作来代替复制粘贴。因此,拖放操作劫持很大程度的扩展了点击劫持的攻击范围,也将劫持模式从单纯的鼠标点击扩展到了鼠标拖放行为。

通过劫持某个页面的拖放操作实现对其他页面链接的窃取,这些链接中可能会有session key、token、password等信息;或者可以把其他浏览器中的页面内容拖放到富文本编辑器模式中,这样就能够看到页面源代码了,而这些HTML源代码中可能会存在敏感信息。

触屏劫持

移动智能终端设备由于体积限制,一般都没有鼠标、键盘这些输入设备,用户更多的操作是依靠手指在触屏上的点击或滑动等动作完成。在移动设备上,类似点击劫持的攻击模式,实现了对用户触摸屏操作的劫持攻击。

原理分析

点击劫持:CSS透明层+iframe

利用CSS中透明属性opacity,取值范围0~1,取值0时透明度最高
用iframe嵌入被劫持界面:<iframe id = "victim" src="www.victim.com" scrolling="no">

危害

界面操作劫持实际上突破了CSRF的防御策略,这是一种社工色彩很强的跨域操作,而这种跨域正好是浏览器自身的特性,带来的危害可以很大,比如,篡改与删除数据,偷取隐私甚至爆发蠕虫。

六、漏洞挖掘

CSRF的漏洞挖掘

  • 目标表单是否有有效的token随机串
  • 目标表单是否有验证码
  • 目标是否判断了Refere来源
  • 网站根目录下的crossdomain.xml的"allow-access-from domain"是否是通配符
  • 目标JSON是否可以自定义callback函数

界面劫持的漏洞挖掘

  • 目标的HTTP响应头是否设置了X-Frame-Options字段
  • 目标是否有Javascript的Frame Busting机制
  • 更简单的是用iframe嵌入目标网站试试

反射型XSS漏洞挖掘

反射型XSS最常见的就是直接在URL中进行注入,URL的格式如下:
<scheme>://<netloc>/<path>?<query>#<fragment>
在完整的URL构成中,<path>、<query>、<fragment>都是用户可控的

一般情况下可以通过将payload加入到参数来测试XSS的存在与否

<script>alert(1)</script>
'"><script>alert(1)</script>
<img/src=@ onerror=alert(1) />
' onmouseover=alert(1) x='
` onmouseover=alert(1) x=`
javascript:alert(1)//
'";alert(1)//
}x:expression(alert(1))
.....

根据请求后的反应看是否有弹出窗或浏览器脚本错误,如果有则说明目标存在XSS漏洞
例如:www.test.com/xss.php?id=1,这里输入点为id=1,既然有输入点,查看结果则依赖于输出点,可能是以下几处:

  • HTML标签之间,<div id="body">[输出]</div>
  • HTML标签内,<input type="text" value=[输出] />
  • Javascript代码的值,<script>a="[输出]";...</script>
  • CSS代码的值,<style>body{font-size:[输出]px;...}</style>

HTML标签之间

有很多标签之间的脚本是无法执行的,如:<title>、<textarea>、<xmp>、<iframe>、<noscript>、<plaintext>,可以先闭合前面的标签,来使得脚本能够成功执行。</title><script>alert(1)</script>

HTML标签内

  1. " onmouseover=alert(1) x=" 这种是闭合属性,然后使用on事件来触发脚本。
  2. "><script>alert(1)</script> 这种是闭合属性后又闭合标签,然后直接执行脚本。

细分为三种场景:
1.输出在src/href/action等属性内
2.输出在on*事件内
3.输出在style属性内

Javascript代码的值

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

CSS代码的值

与“输出再在style属性内”类似,构造能执行的Javascript语句,闭合标签。

存储型XSS漏洞挖掘

与反射型XSS相比,存储型XSS漏洞挖掘的差别在于:存储型XSS一般都是基于表单的提交,然后进入服务端存储,最终在某个页面输出。

一般情况下,存储型XSS表单提交之后的输出点有以下几种可能:

  • 表单提交后跳转到的页面可能是输出点。
  • 表单所在的页面可能是输出点。
  • 表单提交后不见了,整个网站的某个源文件是输出点,需要借助爬虫进行分析。

DOM型XSS漏洞挖掘

静态方法

一旦发现可以特征就得中断下来人工分析。就工具化而言,静态方法可以使用正则表达式匹配输入点和输出点。

输入点:

/(location\s*[\[.])|([.\[]\s*["']?\s*(arguments|dialogArguments|innerHTML|write(ln)?|open(Dialog)?|showModalDialog|cookie|URL|documentURI|baseURI|referrer|name|opener|parent|top|content|self|frames)\W)|(localStorage|sessionStorage|Database)/

输出点:

/((src|href|data|location|code|value|action)\s*["'\]]*\s*\+?\s*=)|((replace|assign|navigate|getResponseHeader|open(Dialog)?|showModalDialog|eval|evaluate|execCommand|execScript|setTimeout|setInterval)\s*["'\]]*\s*\()/

动态方法

与静态方法只需进行特征匹配不同,动态方法要对程序的执行流程进行跟踪,除了输入点和输出点外还要关心逻辑过程。一种比较简单的方式是借用浏览器动态执行的优势,以DOM树的改变为判断依据,对输入点进行模糊测试,然后判断渲染后的DOM树中是否有期望的值,不过这种方法无法逻辑判断导致的误报。

字符集缺陷导致的XSS

概念

  1. 字符与字节
    肉眼看到的一个文字或符号单位就是一个字符(包括乱码),一个字符可能对应1~n字节。
    一个字节为8个bit位,每个bit位要么为0要么为1.
  2. 字符集
    一个字符对应1~n字节是由字符集及编码决定的,比如,ASCII字符集就是一个字符对应一个字节,不过1字节只用了7位,最高位用于其他目的,所以ASCII字符集共有2的7次方(128)个字符。
  3. 字符集编码
    这些字符集大都对应一种编码方式,不过Unicode字符集的编码方式有UTF-8、UTF-16、UTF-32、UTF-7,常见的是UTF-8与UTF-7。
    编码的目的是最终将这些字符正确地转换位计算机可理解的二进制,对应的解码就是将二进制最终解码位人类可读的字符。

宽字节编码带来的安全问题

GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。
宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,比如,下面这个PHP示例,在magic_quotes_gpc=On的情况下,如何触发XSS?

<?php header("Content-Type: text/html;charset=GBK"); ?>
<head>
<title>gb xss</title>
</head>
<script>
a="<?php echo $_GET['x'];?>";
</script>

我们会想到,需要闭合双引号才行,如果只是提交如下语句:gb.php?x=1";alert(1)//

双引号会被转义成 \",导致闭合失败:a="1\";alert(1)//";

由于这个网页头部响应指明了这是GBK编码,GBK编码第一字节(高字节)的范围是0x81~0xFE,第二字节(低字节)的范围是0x40~0x7E与0x80~0xFE,这样的十六进制表示。而 \ 符号的十六进制表示为0x5C,正好在GBK的低字节中,如果之前有一个高字节,那么正好会被组成一个合法字符,于是提交如下:gb.php?x=1%81";alert(1)//

双引号会继续被转义成 \",最终如下:a="1[0x81]\";alert(1)//";

[0x81]\ 组成了一个合法字符,于是之后的双引号就会产生闭合,这样我们就成功触发了XSS。

这些宽字节编码的高低位范围都不太相同,具体可以查相关维基百科。

这里有一点要注意,GB2312是被GBK兼容的,它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE(0x5C不在该范围内),把上面的PHP代码的GBK改为GB2312,在浏览器中处理行为同GBK,也许是由于GBK兼容GB2312,浏览器都做了同样的兼容:把GB2312统一按GBK行为处理。

上面这类宽字节编码问题的影响非常普遍,不仅是XSS这么简单,从前端到后端的流程中,字符集编码处理不一致可能导致SQL注入、命令执行等一系列安全问题。

绕过浏览器的XSS Filter

XSS Filter主要针对反射型XSS,大体采用的是启发式的检测,即根据用户提交的参数是否判断是否有潜在的XSS特征,并重新渲染响应的内容保证潜在的XSS特征不会触发,IE自动修改页面就是重新渲染的例子。

响应头CRLF注入绕过

若目标网页存在响应头部CRLF注入,在HTTP响应头注入回车换行符,就可以注入头部:X-XSS-Protection: 0,用于关闭XSS Filter机制,

例如:http://topo.com/xx.action?id=%0d%0aContent-Type:%20text/html%0d%0aX-XSS-Protection:%200%0d%0a%0d%0ax%3Cscript%3Ealert%28123%29%3B%3C%2fscript%3Ey

这段URL如果在urldecode后是如下内容:

http://x.com/xx.action?id=
Content-Type: text/html
X-XSS-Protection: 0
x<script>alert(1);</script>y

针对同域的白名单

  1. IE会判断Referer来源是否是本域,如果是则XSS Filter不会生效,如:referer:<?php echo $_SERVER['HTTP_REFERER'] ?>
  2. Chrome则与IE不一样,如果<script>嵌入同域内的js文件,XSS Filter便不会防御。http://www.foo.com/xss.php?x=<scriptsrc=alert.js></script>

混淆的代码

浏览器的进制

HTML属性中用的最多是十进制和十六进制,十进制在HTML中用形如&#56;的方式表示(前缀为,中间为十进制数字,后缀为半角分号;可以没有后缀);十六进制则为&#x5a;的形式(比十进制多了个x,进制码中也多了a~f这6个字符来表示10~15)。

CSS代码中只能用到十进制和十六进制,除了兼容HTML中的进制表示外,十六进制还可以使用 \6c 的形式表示(使用斜线作为进制数值前缀)。

JavaScript代码中可直接通过eval执行的字符有八进制(\56)和十六进制(\x5c)两种编码方式,它们都不能给多字节字符编码,只能用十六进制的Unicode编码。

在线编解码工具:http://monyer.com/demo/monyerjs/

浏览器的编码

JavaScript中有三套编码/解码函数:

  • escape / unescape
  • encodeURL / decodeURL
  • encodeURLComponent / decodeURLComponent

三种加密方法近乎相同,实际上它们还是有少许区别的:

escape不编码的字符有69个:
*、+、-、.、/、@、_、0~9、a~z、A~Z而且escape对0~255以外的unicode值进行编码时输出%u****格式。

encodeURI不编码的字符有82个:
!、#、$、&、'、(、)、*、+、,、-、.、/、:、;、=、?、@、_、~、0~9、a~z、A~Z

encodeURIComponent不编码的字符有71个:
!、'、(、)、*、-、.、_、~、0~9、a~z、A~Z

HTML中的代码注入

完整的HTML代码分为:标签名、属性名、属性值、文本、注释。

1、标签
HTML标签不区分大小写,所以可以构造<ScrIPT>的形式绕过对<script>的过滤,这对代码的运行没有丝毫的影响,但是这样会阻碍过滤器对关键词的识别。

由于现代浏览器对XHTML的支持,使得我们可以在某些浏览器的某些版本中插入XML代码、SVG代码或未知标签。
如在IE 6下可以构造如下代码:<XSS STYLE="xss:expression(alert('XSS'))">如果对方站点的HTML过滤器是基于黑名单的,很明显,<XSS>标签不在名单之列,使得插入的代码得以被绕过。

有些过滤器的HTML Parser很强大,会判断当前代码是否存在于注释中,如是注释则忽略;反过来说,有些则不关心是否有注释,只关心HTML标签、属性、属性值是否有问题。由于注释的优先级较高,可以构造如下代码:<!--<a href="--><img src=x onerror=alert(1)//">test</a>
扫描器忽略了HTML注释后,会认为<a href="<img src=x onerror=alert(1)//">test</a>是一个完整的HTML语句,<img src=x onerror=alert(1)//">则被认为是属性href的值,而对浏览器来说,<img src=x onerror=alert(1)//">是一个完整的img标签。

HTML语法中标签同样有优先级的区别,<textarea>、<title>、<style>、<script>、<xmp>等标签具有非常高的优先级,因此使其结束便可直接中断其他标签的属性,<style><a href="</style><img src=x onerror=alert(123)//>,这里<style>优先级高于<a>,因此</style>出现与前面闭合之后就会中断<a>锚标签。

2、属性
HTML标签的属性同样不区分大小写,属性值可以用双引号、单引号括起来,不用引号语法上也可以接受,甚至在IE下还可以使用反引号(`),<img sRc = `#`>

标签和属性、属性名和等号、等号和属性名之间可用若干个空格、换行符(chr(13))、回车符(chr(10))、TAB(chr(9))。

另外还可以在属性的头部和尾部插入系统控制符(ASCII值1~32):<a &#8 href="&#32 javascript:alert(123)&#16">test</a>,不过要注意,不同浏览器、不同版本之间处理方式都存在差异,使用控制字符前最好有一个预期。

HTML的属性按用途分大致可以分为:普通属性、时间属性、资源属性几种。

对于普通属性我们可以控制属性值,应该尝试突破当前属性去构造新的属性或标签,常用的方法是构造闭合。

如果可以控制事件属性,那么除了上面说的突破当前属性,我们还可以插入代码等待用户触发。对于形如<a href="#" onclick="function(\"<?=$_GET['a']?>\")">test code</a>可以说得上是最爱了,因为用户可以完全控制输入代码,闭合函数构造自己的代码。有时候碰到匿名函数也是一件高兴的事,因为匿名函数具有可以在一个函数中执行另一个函数的特性,或多或少给了我们机会。

资源类属性,可以理解为属性值需要为URL的属性,通常属性名为src或href,这类属性一般都支持浏览器的预定义协议:http、ftp、https(网络交互协议)、file、data(本地协议)、mailto、javascript、vbscript(伪协议)

常见支持资源属性的HTML标签:

applet,embed,frame,iframe,img
input type=image,
xml,a,link,area,
table\tr\td\th的background属性,
bgsound,audio,video,object,meta refresh,script,base,source

3、事件
HTML的事件有很多,包括鼠标事件、键盘事件、表单元素相关事件及内容编辑事件等。

CSS中的代码注入

CSS分为选择符、属性名、属性值、规则和声明。

@charset "UTF-8";
body{
    background:red;
    font-size:16px;
}
a{
    font-size:14px!important;
}   //body和a为选择符,background为属性名,为属性值,@charset为规则,!important为声明

CSS中能利用进行XSS的只有属性值和@import规则。
CSS与HTML类似,CSS语法对大小写不敏感,属性值对单双引号不敏感,对资源类属性来说URL部分的引号也不敏感,同样空格、回车、tab等也能被浏览器解析。

Javascript中的代码注入

当XSS点出现在JavaScript代码的变量中时,只要我们可以顺利闭合之前的变量,接下来就可以插入我们的代码了,示例如下:var a = "[userinput]";

假设其中的[userinput]是用户可控变量,则可以尝试用引号来闭合变量,假如输入:123";alert(1);b="

那么代码效果如下:var a = "123";alert(1);b="";

a变量被闭合,alert(1)得以逃脱出来,而b变量的存在是为了使语法保持正确,当然,我们也可以输入注释符“//”来忽略掉后面的错误语法:var a = "123";alert(1);//";

XSS寻找注入点的过程其实便是寻找用户可控变量的过程,当然有时候并不是这么简单就能利用的。比如说:目标站点对输入变量使用了addslashes(单引号、双引号和反斜线的前面都会增加一条反斜线),这样就无法通过闭合引号利用。如果处于宽字节环境,就可以通过构造%81%5c的方式进行绕过,</script>(</script>)能用的话,也可以闭合<script>标签,</script>闭合标签具有最高优先级。

突破URL过滤

我们注入URL时,可以参考如下一些技巧来绕过过滤:
正常:<a href="https://66.102.7.147/">XSS</a>
URL编码:<a href="https://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">XSS</a>
十进制:<a href="https://1113982867/">XSS</a>
十六进制:<a href="https://0x42.0x0000066.0x7.0x93/">XSS</a>
八进制:<a href="https://0102.0146.0007.00000223/">XSS</a>
混合编码:<a href="http://6&#9;6.000146.0x7.147/">XSS</a>
不带http: :<a href="//www.google.com/">XSS</a>
最后加个点:<a href="https://www.google.com./">XSS</a>

Checklist

Checklist:http://html5sec.org/


参考:《Web前端黑客技术揭秘》 作者:钟晨鸣、徐少培

相关推荐