DOM Based Cross Site Scripting (XSS)
前言
DOM(document object model文档对象模型),客户端脚本处理逻辑导致的安全问题。基于DOM的XSS漏洞是指受害者端的网页脚本在修改本地页面DOM环境时未进行合理的处置,而使得攻击脚本被执行。在整个攻击过程中,服务器响应的页面并没有发生变化,引起客户端脚本执行结果差异的原因是对本地DOM的恶意篡改利用。
DOM型的XSS由于其特殊性,常常被分为第三种,这是一种基于DOM树的XSS。例如服务器端经常使用document.boby.innerHtml等函数动态生成html页面,如果这些函数在引用某些变量时没有进行过滤或检查,就会产生DOM型的XSS。DOM型XSS可能是存储型,也有可能是反射型。
下面分析四种不同等级的DOM XSS漏洞
-
Low
服务端核心代码:
<?php
# No protections, anything goes
?>
没有对参数default做任何防护:
漏洞利用
随心所欲的注入, 来一个一般的:
http://localhost/DVWA/vulnerabilities/xss_d/index.php?default= <script>alert('You are attacked!')</script>
可以看到, 注入的js被写到html中, 即执行成功:
-
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;
}
}
?>
- stripos()f函数
查找字符串在另一字符串中第一次出现的位置(不区分大小写)。(strpos区分大小写)
- header() 函数
向客户端发送原始的 HTTP 报头。
服务端当default参数存在且不为空的时候处理, 并且过滤了<script>标签
漏洞利用
由于过滤了script标签, 当我们继续按照原来的方法注入的时候, 就会执行失败:
方法一: 错误事件注入
随意输入一个值, 发现将值写在option标签的value值:
于是我们尝试错误事件注入:
http://localhost/DVWA/vulnerabilities/xss_d/index.php?default=<img src="" onerror=alert("You are attacked!")>
发现被当成字符串写入到value值了,
那么这时候就要闭合标签了 (类似sql注入的闭合);
首先, 要先闭合 value="注入点" 的双引号; 再是 <option>标签, 最后是<select>标签:
http://localhost/DVWA/vulnerabilities/xss_d/index.php?default=English"></option></select><img src="" onerror="alert('You are attacked!')">
闭合之后, 注入的img标签就是一个独立可执行的标签了。
方法二: # 字符绕过
首先引申一个知识点:
- # 号在 URL中的作用
# 号在 URL中的代表网页中的一个位置
具体来说, 在 url 中 # 后面的字符串是该位置的标识符 (可以是内容, id等)
打个比方, 以下url就表示 "标准表" 在网页中的位置:
https://baike.baidu.com/item/ASCII/309296?fr=aladdin#标准表
浏览器读取这个URL后,会自动将print位置滚动至可视区域。
#是用来指导浏览器动作的,对服务器端完全无用, 客户端提交请求的时候, 不会将#后面的内容考虑进去。所以,HTTP请求中不包括#
而且, 单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页。
那么这里就可以利用#后面的内容不会发送到服务端的原理, 将script标签注入到本地游览器执行, 且不经过服务端的处理:
http://localhost/DVWA/vulnerabilities/xss_d/index.php#?default=English<script>alert('You are attacked!')</script>
利用#号绕过, 不会重新加载网页, 隐蔽性比错误事件标签注入强多了。
-
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;
}
}
?>
High级别的服务端用了白名单的过滤方法, 除了case的情况, 其他的一律pass。
漏洞利用
这里的白名单过滤一般比较难绕, 但是这里可以用上述的 # 来绕过:
-
Impossible
服务端核心代码:
<?php
# Don't need to do anything, protction handled on the client side
?>
服务端把锅甩给了客户端。
我们看一下客户端的防护:
客户端所做的"防护"就是将default参数的值全部进行URL编码, 没有进行相应的解码就直接赋给value, 从根本上斩除了xss漏洞。
下一篇: SpringBoot实现文件的上传和下载