XSS简介
0x10
跨站脚本 (Cross-site scripting, 简称XSS) 是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。
**XSS攻击 **通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方式注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScrip,但实际上也可以包括Java,VBScrip,ActiveX,Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、回话和cookie等各种内容。
XSS攻击主要方式
- 反射XSS (Reflected XSS):将用户的输入数据简单的反馈给页面。即非持久型XSS
- 存储XSS (Stored XSS):通过网站数据库的方式执行恶意脚本。即 存储型XSS
- 基于DOM的XSS (DOM-based XSS ):漏洞存在于客户端代码而不是服务端代码,即将js中的代码输出到html中
0x20 XSS的三种方式
0x21 Reflected cross-site scripting
Reflected XSS 是跨站脚本中最简单的一种。它可以发生在HTTP请求中的携带的数据 也可能发生在一种不安全的及时响应的数据中。
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
针对上面一段输入代码,发现服务端 没有过滤 所以我们可以制造出这样的XSS:http://xxxx/xx?name="<script>alert('XSS');</script>"
< input type = "text" value = "<%= getParameter(" keyword ") %> ">
< button > 搜索 </ button >
< div >
您搜索的关键词是: < %= getParameter (" keyword ") %>
</ div >
同理这段代码也是没有做校验,所以我们可以制造出这样的XSS:http://xxxx/search?keyword="<script>alert('XSS');</script>
< input type = "text" value = "<%= escapeHTML(getParameter(" keyword ")) %> ">
< button > 搜索 </ button >
< div >
您搜索的关键词是: < %= escapeHTML ( getParameter (" keyword ")) %>
</ div >
这段代码通过escapeHTML做了转义 如果我们继续传刚才的url会被转义成这样
http://xxxx/search?keyword="<script>alert('XSS');</script>
所以可以通过HTML转义来局部防止XSS攻击
-
在html中 使用HtmlEncode来防止XSS,将字符转换成HTMLEntites。
-
在php中 函数htmlentities() 和htmlspecialchars() 也能达到安全要求。
-
在js中可以使用javascriptEncode(), 使用 \ 对特殊字符进行转义,特殊字符小于127的字符编码使用16进制进行编码,大于127的用unicode。经常用在事件中输出的防护
<a href = "<%=escapeHTML(getparameter("redirect_to")) %> ">跳转.....</a>
HTML转义在跳转链接等输入点是不行的。如:http://xxxx/search?redirect_to=javascript:alert('XSS')
0x22 Stored cross-site scripting
Stored XSS通过服务器保存用户提交的数据,通过数据库后将用户输入显示到网页上,如果用户提交恶意语句到服务端,那么每次该用户访问此网页都会将服务器恶意语句获取到网页中,由此来形成储存型xss 。常见于 博客提交、聊天室、客户订单的联系方式。
<p>Hello, this is my message!</p>
比如上面代码是一个留言板功能,如果没有做校验的情况下,我们可以伪造成这样:
<p><script>alert('XSS');</script></p>
0x23 DOM-based cross-site scripting
DOM XSS 前端 JavaScript通过一种不安全的方式处理不可信的数据,通常通过将数据写到DOM的方式来实现。
var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'You searched for: ' + search;
上面的代码没有做校验 我们可以将下列代码插入到DOM中,从而轻易的伪造出一种XSS,
<img src="" onerror="<script>alert("XSS");</script>">
防护策略
在js输出到<script>时 ,应该先执行javascriptEncode,然后当javascript输出到html时(如results.innerHTML
)
- 输出到事件或者脚本,则做一次JavascriptEncode
- 输出到html或者属性,则做一次HtmlEncode
常见的注入点
- .innerHtml
- .outerHtml
- document.write()
- document.location.replace()
- innerHtml.replace
- window.location(hash、href)
- window.name
- document.cookie
- …
0x30 XSS构造技巧
以下参考《白帽子讲web安全》 吴翰清著
0x301利用字符编码
var redirectUrl="\";alert(/XSS/);"
这段代码输出一个变量,然后使用转义了双引号,系统转义了双引号导致无法escape ()
但对于返回是GBK/GBK2312编码的页面, “%c1\” 是一个Unicode字符,在Firefox下会认为是一个字符,所以只需要构造
%c1";alert(/XSS/);//
这样“%1c”和“\” 构成了一个Unicode字符, // 屏蔽了后面的引号。从而绕过了系统的安全检查。
0x302 绕过长度限制
<input type=text value="$var" />
例如以上代码,如果服务端对var变量进行了长度限制 ,那么攻击者的XSS函数会被切割,从而防止XSS。但如果利用事件(Event) 来缩短所需要的字节数,就可以轻而易举的绕过长度限制。
-
使用XSS payload的方式
将其写到其他位置,然后通过简短的代码加载XSS payload。最常用的藏代码的方式就是 “location.hash ”,那么XSS 可以构造成这样
"οnclick="eval(location.hash.substr(1))
-
利用注释符绕过长度限制
<input id=1 type="text" value=""> xxxxxxxxx <input id=2 type="text" value="">
在第一个input里输入
"><!--
在第二个input里输入
--><script>alert(/XSS/);</script>
这样的就造成了如下结果
<input id=1 type="text" value=""><!--"> xxxxxxxxx <input id=2 type="text" value="--><script>alert(/XSS/);</script> "/>
0x303 利用<base>标签
通过在html中插入<base>标签,就可以改变html代码中图片的链接,从而就可以在远程服务器伪造图片、脚本等达到攻击的目的。
0x40 判断目标浏览器版本
//Firefox detector 2/3 by DoctorDan
FF=/a/[-1]=='a'
//Firefox 3 by me:-
FF3=(function x(){})[-5]=='x'
//Firefox 2 by me:-
FF2=(function x(){})[-6]=='x'
//IE detector I posted previously
IE='\v'=='v'
//Safari detector by me
Saf=/a/.__proto__=='//'
//Chrome by me
Chr=/source/.test((/a/.toString+''))
//Opera by me
Op=/^function \(/.test([].sort)
//IE6 detector using conditionals
try {IE6=@cc_on @_jscript_version <= 5.7&&@_jscript_build<10000} catch(e){IE6=false;}
以上代码写成一行
B=(function x(){})[-5]=='x'?'FF3':(function x(){})[-6]=='x'?'FF2':/a/[-1]=='a'?'FF':'\v'=='v'?'IE':/a/.__proto__=='//'?'Saf':/s/.test(/a/.toString)?'Chr':/^function \(/.test([].sort)?'Op':'Unknown'