1、漏洞名称

存储型XSS跨站脚本,反射型XSS跨站脚本

2、漏洞描述

跨站脚本攻击的英文全称是Cross Site
Script,为了和样式表区分,缩写为XSS。发生的原因是网站将用户输入的内容输出到页面上,在这个过程中可能有恶意代码被浏览器执行。跨站脚本攻击它指的是恶意攻击者往Web页面里插入恶意JavaScript代码,当用户浏览该页之时,嵌入其中Web里面的JavaScript代码会被执行,从而达到恶意用户的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常呼略其危害性。而本文主要讲的是利用XSS得到目标服务器的shell。技术虽然是老技术,但是其思路希望对大家有帮助。

已知的跨站脚本攻击漏洞有三种:1)存储式;2)反射式;3)基于DOM(文件对象模型)。

1、存储型跨站脚本攻击涉及的功能点:用户输入的文本信息保存到数据库中,并能够在页面展示的功能点,例如用户留言、发送站内消息、个人信息修改等功能点。

2、反射型跨站脚本攻击涉及的功能点:URL参数需要在页面显示的功能点都可能存在反射型跨站脚本攻击,例如站内搜索、查询功能点。

3、基于DOM跨站脚本攻击涉及的功能点:涉及DOM对象的页面程序,包括(不限这些):
document.URL document.URLUnencoded document.location document.referrer window
.location
3、检测条件

1、已知待测目标URL,假设为http://www.exmaple.com/page.xxx

2、待测目标存在参数输入,或者以POST方式提交参数,显示为表单方式,假设为name=value

3、在某种情况下,用户输入被重新显示在网页上,包括名字、帐号、检索结果等等(说明目标网站服务器并没有对用户提交数据检测)

4、检测方法

GET方式跨站脚本:

1、在输入的参数后逐条添加以下语句,以第一条为例,输入
http://www.exmaple.com/page.xxx?name=<script>alert(123456)</script>
只要其中一条弹出显示123456的告警框,就说明存在跨站漏洞,记录漏洞,停止测试。

2、如果没有弹出显示123456的告警框,则在返回的页面上单击鼠标右键,选择“查看源文件”。

3、查找网页源文件中是否包含完整的字符串<script>alert(123456)</script>
,则不管有没有弹出显示123456的告警框,都表明存在跨站脚本漏洞。

4、由于有些HTML元素(比如<textarea>或”
)会影响脚本的执行,所以不一定能够正确弹出123456告警框,需要根据返回网页源文件的内容,构造value的值,比如:
多行文本输入框: </textarea><script>alert(123456)</script> 文本输入框: </td><script>alert(
123456)</script> '><script>alert(123456)</script> "><script>alert(123456)</
script> </title><script>alert(123456)</script> --><script>alert(123456)</script>
[img]javascript:alert(123456)[/img]<scrip<script>t>alert(123456)</scrip</script
>t> </div><Script>alert(123456)</script>
需要对页面上所有可以提交参数的地方进行测试。具体跨站脚本的测试语句根据实际情况的不同而不同,可自行构造,以及触发事件等切换,这里只列出了一些最常见构造语句。
Post方式跨站脚本:

1、在POST表单中逐条输入以下语句,只要其中一条弹出显示123456的对话框,就说明存在跨站漏洞,记录漏洞,停止测试。

2、<script>alert(123456)</script>

3、[img]javascript:alert(123456);[/img]

4、如果没有弹出显示123456的告警框,则在返回的页面上单击鼠标右键,选择“查看源文件”

5、查找网页源文件中是否包含完整的字符串<script>alert(123456)</script>
,则不管有没有弹出显示123456的告警框,都表明存在跨站脚本漏洞

6、由于有些HTML元素(比如<textarea>或”
)会影响脚本的执行,所以不一定能够正确弹出123456告警框,需要根据返回网页源文件的内容,构造value的值,比如:
多行文本输入框: </textarea><script>alert(123456)</script> 文本输入框: </td><script>alert(
123456)</script> '><script>alert(123456)</script> "><script>alert(123456)</
script> </title><script>alert(123456)</script> --><script>alert(123456)</script>
[img]javascript:alert(123456)[/img]<scrip<script>t>alert(123456)</scrip</script
>t> </div><Script>alert(123456)</script>
需要对页面上所有可以提交参数的地方进行测试。具体跨站脚本的测试语句根据实际情况的不同而不同,可自行构造,以及触发事件等切换,这里只列出了一些最常见构造语句。
如果被过滤,可以进行如下绕过方式:

1、编码绕过。例如HTML编码。

2、也可以尝试其他标签,探测哪些敏感关键字没有被过滤(例如<script>被过滤,可以使用<img>等标签进行测试)。

3、大小写混合。有可能只过滤了小写。
“onfocus=” alert(document.cookie) //如果过滤了< >符号 <scr<script>ipt>alert(123456)</
script> //也可以嵌套绕过 <sCrIPt>alert(123456)</ScRiPT> //alert千万不能大小写
5、修复方案

建议验证所有输入数据,有效检测攻击;对所有输出数据进行适当的编码,以防止任何已成功注入的脚本在浏览器端运行。具体如下:

A、总体修复方式:

验证所有输入数据,有效检测攻击;对所有输出数据进行适当的编码,以防止任何已成功注入的脚本在浏览器端运行。具体如下:

输入验证:某个数据被接受为可被显示或存储之前,使用标准输入验证机制,验证所有输入数据的长度、类型、语法以及业务规则。

输出编码:数据输出前,确保用户提交的数据已被正确进行entity编码,建议对所有字符进行编码而不仅局限于某个子集。

明确指定输出的编码方式:不要允许攻击者为你的用户选择编码方式(如ISO 8859-1或UTF-8)

注意黑名单验证方式的局限性:仅仅查找或替换一些字符(如"<" ">"或类似"script"的关键字),很容易被XSS变种攻击绕过验证机制。

警惕规范化错误:
验证输入之前,必须进行解码及规范化以符合应用程序当前的内部表示方法。请确定应用程序对同一输入不做两次解码。对客户端提交的数据进行过滤,一般建议过滤掉双引号(”)、尖括号(<、>)等特殊字符,或者对客户端提交的数据中包含的特殊字符进行实体转换,比如将双引号(”)转换成其实体形式
",<对应的实体形式是<,<对应的实体形式是>。
以下为需过滤的常见字符: [1] |(竖线符号) [2] & (& 符号) [3] ;(分号) [4] $(美元符号) [5] %(百分比符号) [6]
@(at 符号) [7] '(单引号) [8] "(引号) [9] \'(反斜杠转义单引号) [10] \"(反斜杠转义引号) [11] <>(尖括号) [12
] ()(括号) [13] +(加号) [14] CR(回车符,ASCII 0x0d) [15] LF(换行,ASCII 0x0a) [16] ,(逗号) [
17] \(反斜杠) 在请求返回页面关键字符进行转义。 [1] “(双引号):" [2] ’ (单引号):&apos [3] &(&符号):& [
4] <(左尖括号):< [5] >(右尖括号):> 在不影响应用的前提下,建议将cookie标记为httpOnly,同时禁用TRACE方法。
B、对于java进行的web业务开发的过滤器如下:


在用java进行web业务开发的时候,对于页面上接收到的参数,除了极少数是步可预知的内容外,大量的参数名和参数值都是不会出现触发Xss漏洞的字符。而通常为了避免Xss漏洞,都是开发人员各自在页面输出和数据入库等地方加上各种各样的encode方法来避免Xss问题。而由于开发人员的水平不一,加上在编写代码的过程中安全意识的差异,可能会粗心漏掉对用户输入内容进行encode处理。针对这种大量参数是不可能出现引起Xss和SQL注入漏洞的业务场景下,因此可以使用一个适用大多数业务场景的通用处理方法,牺牲少量用户体验,来避免Xss漏洞和SQL注入。


利用Servlet的过滤器机制,编写定制的XssFilter,将request请求代理,覆盖getParameter和getHeader方法将参数名和参数值里的指定半角字符,强制替换成全角字符。使得在业务层的处理时不用担心会有异常输入内容。
XssFilter.java package filter; import java.io.IOException; import
javax.servlet.Filter; import javax.servlet.FilterChain; import
javax.servlet.FilterConfig; import javax.servlet.ServletException; import
javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import
javax.servlet.http.HttpServletRequest;public class XssFilter implements Filter {
public void init(FilterConfig config) throws ServletException { } public void
doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
throws IOException, ServletException { XssHttpServletRequestWrapper xssRequest =
new XssHttpServletRequestWrapper((HttpServletRequest)
request);chain.doFilter(xssRequest, response); }public void destroy() { }
XssHttpServletRequestWrapper.javapackage filter; import
javax.servlet.http.HttpServletRequest; import
javax.servlet.http.HttpServletRequestWrapper; }public class
XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest =null; public XssHttpServletRequestWrapper
(HttpServletRequest request) { super(request); orgRequest = request; }
//覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/>
//如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
//getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 public String
getParameter(String name) { String value = super.getParameter(xssEncode(name));
if (value != null) { value = xssEncode(value); } return value; }
//覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/>
//如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/> //getHeaderNames 也可能需要覆盖 public
StringgetHeader(String name) { String value = super.getHeader(xssEncode(name));
if (value != null) { value = xssEncode(value); } return value; }
//将容易引起xss漏洞的半角字符直接替换成全角字符 private static String xssEncode(String s) { if (s ==
null || s.isEmpty()) { return s; } StringBuilder sb = new
StringBuilder(s.length() +16); for (int i = 0; i < s.length(); i++) { char c =
s.charAt(i);switch (c) { case '>': sb.append('>'); //全角大于号 break; case '<':
sb.append('<'); //全角小于号 break; case '\'': sb.append('‘'); //全角单引号 break; case
'\"': sb.append('“'); //全角双引号 break; case '&': sb.append('&'); //全角 break; case
'\\': sb.append('\'); //全角斜线 break; case '#': sb.append('#'); //全角井号 break;
default: sb.append(c); break; } } return sb.toString(); } //获取最原始的request public
HttpServletRequestgetOrgRequest() { return orgRequest; } //获取最原始的request的静态方法
public static HttpServletRequest getOrgRequest(HttpServletRequest req) { if(req
instanceof XssHttpServletRequestWrapper){return
((XssHttpServletRequestWrapper)req).getOrgRequest(); }return req; } }
--------------------------------------------------------------------------------
在web.xml中添加: <filter> <filter-name>xssFilter</filter-name>
<filter-class>filter.XssFilter</filter-class> </filter> <filter-mapping>
<filter-name>xssFilter</filter-name> <url-pattern>/*</url-pattern>
</filter-mapping>
C、对于PHP语言所编写的网站系统的XSS过滤器:
<?php function RemoveXSS($val) { $val = preg_replace(
'/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val); $search =
'abcdefghijklmnopqrstuvwxyz'; $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $search
.='[email protected]#$%^&*()'; $search .= '~`";:?+/={}[]-_|\'\\'; for ($i = 0; $i <
strlen($search); $i++) { $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search
[$i])).';?)/i', $search[$i], $val); $val = preg_replace('/(�{0,8}'.ord($search
[$i]).';?)/', $search[$i], $val); } $ra1 = Array('javascript', 'vbscript',
'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script',
'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound',
'title', 'base'); $ra2 = Array('onabort', 'onactivate', 'onafterprint',
'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut',
'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint',
'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange',
'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut',
'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick',
'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave',
'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate',
'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp',
'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload',
'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove',
'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend',
'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset',
'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit',
'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange',
'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); $ra = array_merge
($ra1, $ra2); $found = true; while ($found == true) { $val_before = $val; for (
$i = 0; $i < sizeof($ra); $i++) { $pattern = '/'; for ($j = 0; $j < strlen($ra[
$i]); $j++) { if ($j > 0) { $pattern .= '('; $pattern .=
'(&#[xX]0{0,8}([9ab]);)'; $pattern .= '|'; $pattern .= '|(�{0,8}([9|10|13]);)'
;$pattern .= ')*'; } $pattern .= $ra[$i][$j]; } $pattern .= '/i'; $replacement
= substr($ra[$i],0,2).'<x>'.substr($ra[$i], 2); $val = preg_replace($pattern,
$replacement, $val); if ($val_before == $val) { $found = false; } } } return
$val; } //测试一下效果 //echo RemoveXSS("<script language='javascript'>alert('hello
world');</script>") ; ?>
D、asp程序网站出现SQL注入、跨站脚本攻击(XSS)漏洞的时候可以使用
源码如下:url=trim(request("url")) 修改如下:url=Server.HTMLEncode(trim(request("url")))
使用Server.HTMLEncode它来解决脚本跨站攻击漏洞 或者:
对用户输入区域只允许适应应用功能所必需的规定字符输入,对于合法的“>”、“<”“&”这三个符号进行替换:replace(str,"<","<")
replace(str,">",">") replace(str,"&","&")
6、XSS防护外篇

Spring MVC防护
项目级过滤: <context-param> <param-name>defaultHtmlEscape</param-name> <param-value>
true</param-value> </context-param> 页面级过滤: 在包含form的jsp页面中添加 <spring:htmlEscape
defaultHtmlEscape="true" /> 表单元素级过滤: 在form元素中添加 <form:form htmlEscape="true">或 <
form:input path="someFormField" htmlEscape="true" />
7、DOM XSS详解

文档对象模型(Document Object
Model),即大名鼎鼎的DOM。DOM可以被认为是一种通过将页面元素以对象的树形方式表现,以便由Javascript组织处理的实现方法。基于DOM的跨站点脚本漏洞可以在许多情况下执行,不需要服务器确定哪些需要执行。这可能导致许多一般的跨站脚本过滤和检测规则对此类攻击无能为力。

第一个假设案例使用下面的客户端代码:
<script>document.write("Siteisat:"+document.location.href+".");</script>
攻击者可能给受感染页面追加#<script>alert('xss')</script>,导致在执行该脚本时会显示警告框。在这种情况下,由于浏览器认为凡是有#
字符都不是查询的一部分,而只是一个片段,因此不会将附加的代码发送给服务器。在这个例子中,立刻执行代码将在网页中显示“跨站脚本”警报。常见的跨站脚本(持久性和非持久)的代码会发送到服务器,并重新显示给用户。跟这些不同的是这中代码只会在用户的浏览器上立即执行。

基于DOM的跨站点脚本漏洞测试

用户输入来源于两种主要形式:
1、服务器在不允许直接XSS情况下写入网页中的输入 2、从客户端JavaScript对象获得的输入
下面两个例子说明服务器如何将数据插入到JavaScript:
var data = "<escaped data from the server>"; var result = someFunction(
"<escaped data from the server>");
下面是来源于客户端JavaScript对象的输入:
var data = window.location; var result = someFunction(window.referer);

由于自动化测试通常通过发送具体的有效载荷并尝试在服务器反应中对其进行观测,这就导致其在确定和验证基于DOM的XSS时成功率很低。它对下面的案例还是能起到一定作用,其中信息参数会反馈到用户:
<script> var pos=document.URL.indexOf("message=")+5; document.write(document
.URL.substring(pos,document.URL.length)); </script>
但下面的人为案件却不能检测到:
<script> var navAgt = navigator.userAgent; if (navAgt.indexOf("MSIE")!=-1) {
document.write("You are using IE: " +document.location.href + "."); } else{
document.write("You are using an unknown browser."); } </script>
出于这个原因,除非测试工具能对客户端代码执行进更多分析,否则自动化测试不能检测对基于DOM的XSS敏感的区域。


因此可以采用手工测试方式检测代码中的区域,而代码中的参数可能都能被攻击者利用。这些区域包括:动态写入页面的代码区域和其它修改DOM的区域,或者是直接执行脚本的区域。