形成原理
xss 中文名是“跨站脚本攻击”,英文名“Cross Site Scripting”。
xss也是一种注入攻击,当web应用对用户输入过滤不严格,攻击者写入恶意的脚本代码(HTML、JavaScript)到网页中时,如果用户访问了含有恶意代码的页面,恶意脚本就会被浏览器解析执行导致用户被攻击。
常见的危害有
cookie窃取,session劫持,钓鱼攻击,蠕虫,ddos等。
xss的分类
xss根据其特性和利用方式可以分为三大类:反射型xss,存储型xss,DOM型xss
1、反射型xss
反射型xss一般出现在URL参数中及网站搜索栏中,由于需要点击包含恶意代码的URL才可以触发,并且只能触发一次,所以也被称为“非持久性xss”。
2、存储型xss
存储型xss一出现在网站留言板,评论处,个人资料处,等需要用户可以对网站写入数据的地方。
比如一个论坛评论处由于对用户输入过滤不严格,导致攻击者在写入一段窃取cookie的恶意JavaScript代码到评论处,这段恶意代码会写入数据库,当其他用户浏览这个写入代码的页面时,网站从数据库中读取恶意代码显示到网页中被浏览器执行,导致用户cookie被窃取,攻击者无需受害者密码即可登录账户。所以也被称作“持久性xss”。持久性xss比反射型xss危害要大的多。
3、dom型xss
DOM xss是基于dom文档对象模型,前端脚本通过dom动态修改页面,由于不与服务端进行交互,而且代码是可见的,从前端获取dom中的数据在本地执行。
常见的可以操纵dom的对象:URL,localtion,referrer等
案例分析
案例环境:DVWA
DVWA (Dam Vulnerable Web Application)DVWA是用PHP+Mysql编写的一套用于常规WEB漏洞教学和检测的WEB脆弱性测试程序。
包含了SQL注入、XSS、盲注等常见的一些安全漏洞。
官网:http://www.dvwa.co.uk
以下用dvwa中4个漏洞等级Low、Medium、High、Impossible的xss进行举例:
1、反射型xss
①、low
漏洞代码:
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
分析与利用:
直接通过$_GET方式获取name的值,之后未进行任何编码和过滤,导致用户输入一段js脚本会执行。
构造payload:
<script>alert(/xss/)</script>
直接执行代码,如下图:
②、medium
漏洞代码:
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
分析与利用:
str_replace对输入的<script>
标签进行替换为空,这样当在输入上面low等级的payload的时候,就会进行过滤导致代码执行不成功,此时可以多写入一个<script>
。
EG:
<scrip<script>t>alert(/xss/)</script>
,过滤方法把中间的<script>
标签替换为空之后 <scrip 与t>
重新组合一个<script>
,成功执行代码。<>
也可以构造别的标签 如<img src=0 onerror=alert(/xss1/)>
或者把标签转换大小写的方式进行绕过<scRipt>alert(/xss2/)</sCript>
执行payload之后URL分别变化为:
http://localhost/dvwa/vulnerabilities/xss_r/?name=%3Cscrip%3Cscript%3Et%3Ealert%28%2Fxss%2F%29%3C%2Fscript%3E#
http://localhost/dvwa/vulnerabilities/xss_r/?name=%3Cimg+src%3D0+onerror%3Dalert%28%2Fxss1%2F%29%3E+#
http://localhost/dvwa/vulnerabilities/xss_r/?name=%3CscRipt%3Ealert%28%2Fxss2%2F%29%3C%2FsCript%3E#
③、high
漏洞代码:
<?php
// 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' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
分析与利用:
preg_replace执行一个正则表达式的搜索和替换,这时候不论是大小写、双层<script>
都无法绕过,此时可以使用别的标签,比如刚刚使用过的<img>
,构造payload
<img src=0 onerror=alert(/xss/)>
执行payload之后URL变化为
http://localhost/dvwa/vulnerabilities/xss_r/?name=%3Cimg+src%3D0+onerror%3Dalert%28%2Fxss%2F%29%3E#
④、impossible
安全代码::
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
分析:
Htmlspecialchars方法将用户输入的特殊字符转换为 HTML 实体,< > “ ‘ &等字符会被转换,于是不存在xss漏洞。
存储型xss
①、low
漏洞代码:
?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去除字符串首尾处的空白字符(或者其他字符)。之后stripslashes方法返回一个去除转义反斜线后的字符串(’ 转换为 ’ 等等),双反斜线()被转换为单个反斜线()。
之后mysqli_real_escape_string对字符串特殊符号n r ‘ “ 等进行转义
最终未对用户输入数据进行xss检测编码,直接写入到数据库中,于是造成存储型xss漏洞。
构造利用:
Message文本框可以直接写入<script>
:
name字段对输入字符有长度限制,这个可以通过burpsuite抓包改包绕过:(burp使用在此不再赘述,请自行查阅教程)
修改txtname字段为payload脚本:
成功执行xss脚本:
②、medium
漏洞代码:
<?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();
}
?>
分析与利用:
以上代码我们重点关注两行:
$message = htmlspecialchars( $message );
$name = str_replace( '<script>', '', $name );
Message由于使用了htmlspecialchars方法对用户输入数据进行编码转换,因此不存在xss漏洞。
但是name由于仅仅用了str_replace方法把<script>
替换为空,于是我们有以下三种方法来绕过:
非<script>
标签:
<img src=0 onerror=alert(/xss1/)>
大小写转换:
<Script>alert(/xss2/)</sCript>
双重
<sc<script>ript>alert(/xss3/)</script>
由于name字段对长度有限制,使用以上改包抓包方式,依次修改参数值,结果如下:
③、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();
}
?>
分析与利用:
这里我们重点关注一行代码:
$message = htmlspecialchars( $message );
Message依旧不可绕过
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
preg_replace执行一个正则表达式的搜索和替换,此时可以使用别的标签<img> <a> <iframe>
等,比如刚刚使用过的<img>
,构造payload :<img src=0 onerror=alert(/xss/)>
,改包替换绕过,成功执行xss代码:
④、impossible
安全代码:
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 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)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = stripslashes( $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)) ? "" : ""));
$name = htmlspecialchars( $name );
// Update database
$data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
$data->bindParam( ':message', $message, PDO::PARAM_STR );
$data->bindParam( ':name', $name, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
分析:
在此name和message都使用了htmlspecialchars方法,于是此处不存在xss漏洞。
申明:
本文借鉴:看雪学院 常规Web漏洞
https://www.kanxue.com/book-6-41.htm