#免责声明:
本文属于个人笔记,仅用于学习,禁止使用于任何违法行为,任何违法行为与本人无关。
漏洞原理
XSS又叫CSS(Cross Site Script),跨站脚本攻击。它指的是恶意攻击者往Web页面
里插入恶意JS代码,当用户浏览该页之时,嵌入其中Web里面的JS代码会被执行,从而达到恶意的特殊目的。它同xxe是有很大区别的,相同点是对用户输入的数据过于信任导致恶意代码被执行,可以看我另一篇博客对xxe漏洞的分析http://t.csdn.cn/kyX8v
XSS漏洞分类
1.反射型
反射型XSS也叫做非持久型XSS,攻击者在URL中插入XSS代码,服务端将URL中的XSS代码输出到页面上,攻击者将带有XSS代码的URL发送给用户,用户打开后受到XSS攻击。
2.存储型
攻击者在页面上插入XSS代码,服务器端将数据存入数据库,当用户访问到存在XSS漏洞的页面时,服务器端从数据库中取出数据展示到页面上,导致XSS代码执行,达到攻击效果。
3.DOM型
通过修改页面的DOM节点形成的XSS,称之为XSS;效果跟反射型XSS类似,攻击者在URL中插入XSS代码,前端页面直接从URL中获取XSS代码并且输出到页面,导致XSS代码的执行,攻击者将带有XSS代码的URL发给用户,用户打开后受到XSS攻击。
漏洞危害
XSS漏洞的危害
1.窃取用户Cookie,如果用户Cookie被窃取,攻击者可以不通过密码,而直接登录用户账户
2.使用XMLHttpRequest构造模拟用户请求操作
3.XSS钓鱼攻击
4.用户PC信息探测收集器
5.XSS蠕虫攻击
防御措施
(1) 特殊字符HTML实体转码。最好的过滤方式是在输出和二次调用的时候进行加HTML实体一类的转码,防止脚本注入。
(2) 标签事件属性黑名单。特殊字符容易被绕过,所以还得加标签事件得黑名单或者白名单,这里推荐使用白名单的方式,实现规则可以直接使用正则表达式来匹配,如果匹配到的事件不在白名单列表,就可以直接拦截,而不是过滤为空。
(3)HttpOnly
HttpOnly是微软公司的Internet Explorer 6 SP1引入的一项新特性。这个特性为Cookie提供了一个新的属性,用以阻止客户端脚本访问Cookie。至今已成为了一个标准,几乎所有的浏览器都会支持HttpOnly
许多 XSS 攻击的目的就是为了获取用户的 cookie,将重要的 cookie 标记为 http only,这样的话当浏览器向服务端发起请求时就会带上 cookie 字段,但是在脚本中却不能访问 cookie,这样就避免了 XSS 攻击利用 js 的 document.cookie获取 Cookie。
靶场实战
DOM-XSS
Low难度
源码分析
<?php
# No protections, anything goes
?>
这里可以发现,源码没有进行任何过滤,且发出的GET请求,参数是default值。
漏洞复现
直接将要执行的js代码:<script>alert(/XSS/)</script>
赋值给default,即可看到代码被执行成功,且网页界面的DOM节点被修改。
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;
}
}
?>
可以看到这段代码过滤default提交参数中的“<script”字符串,stripos() 函数会匹配提交的参数中第一次出现”<script“,如果出现了,就会手动通过location 将URL 后面的参数修改为?default=English。
漏洞复现
这里可以利用input标签来绕过过滤:
?default=English<input onclick=alert(/XSS/)>
还可以利用ing标签来绕过,但前提是需要闭合前面的option标签和select 标签闭合:
?default=</option></select><img src='' onerror=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其中一个凡是不在白名单中的,强制修改其为English。
漏洞复现
这里可以利用English#与English&x来绕过。
比如:
?default=English#<script>alert(/XSS/)</script>
?default=English&x<script>alert(/XSS/)</script>
?default=English#<input onclick=alert(/XSS/)>
?default=English&x<input onclick=alert(/XSS/)>
?default=English#</option></select><img src='' onerror=alert(/XSS/)>
?default=English&x</option></select><img src='' onerror=alert(/XSS/)>
Impossible难度
源码分析
<?php
# Don't need to do anything, protction handled on the client side
?>
可以发现这里对我们输入的参数(lang)并没有进行URL解码,而在其他级别中是有解码过程的decodeURI(lang)。所以我们输入的任何参数都是经过URL编码,然后直接赋值给option标签。
reflected-XSS
Low难度
源码分析
<?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>';
}
?>
可以看到,代码直接引用了name参数,并没有任何的过滤与检查,存在明显的XSS漏洞。
漏洞复现
直接注入js代码:<script>alert(/XSS/)</script>
Medium难度
源码分析
<?php
header ("X-XSS-Protection: 0");
// 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() 函数对name 变量中的敏感字符
漏洞复现
这里可以通过嵌套绕过:<sc<script>ript>alert(/XSS/)</script>
大小写绕过:<Script>alert(/XSS/)</sCript>
使用其他标签进行绕过:<input onclick=alert(/XSS/)>
</option></select><img src='' onerror=alert(/XSS/)>
High难度
源码分析
<?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' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
同样使用黑名单过滤输入,preg_replace() 函数将包含<script的字符,不管大小写,不管后面跟着1个或多个与之相同的字符都转换为空。用于正则表达式的搜索和替换,这使得双写绕过、大小写混淆绕过(正则表达式中i表示不区分大小写)不再有效。
漏洞复现
当然同样可以使用其他标签绕过:
<input onclick=alert(/XSS/)>
</option></select><img src='' onerror=alert(/XSS/)>
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()函数进行过滤,htmlspecialchars() 函数把预定义的字符转换为 HTML 实体,输入到
标签中,目前没有什么好的方法进行绕过。
Stored-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(string,charlist)
函数移除字符串两侧的空白字符或其他预定义字符,预定义字符包括、\t、\n、\x0B、\r以及空格,可选参数charlist支持添加额外需要删除的字符。
// stripslashes(string)
函数删除字符串中的反斜杠。
// mysql_real_escape_string(string,connection)
函数会对字符串中的特殊符号(\x00,\n,\r,\,‘,“,\x1a)进行转义。
这些函数只是对数据库进行了保护,并没有对XSS进行过滤.
漏洞复现
直接注入js代码:<script>alert(/XSS/)</script>
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();
}
?>
1、addslashes
addslashes(string)//在预定义字符前添加反斜杠,预定义字符 ’ " \ NULL
2、strip_tags
strip_tags(string,allow)//去除字符串中的HTML、XML、PHP 的标签
//allow 参数可选,规定允许这些标签
3、htmlspecialchars
htmlspecialchars(string,flags,character-set,double_encode)
//把预定义的字符转换为HTML实体,预定义字符 & " ’ < >
可以看到对message 变量过滤的很彻底,但是对name变量,只对其进行简单的过滤,过滤掉了“
漏洞复现
字符长度限制可以本地浏览器直接手动修改长度,当然也可以抓包修改:
接下来使用和reflected-XSS(反射型跨站脚本攻击)Meduim难度一样的方法进行绕过:
1、嵌套
name <sc<script>ript>alert(/XSS/)</script>
message XXX
2、大小写
name <Script>alert(/XSS/)</script>
message XXX
3、其他标签
name <img src=’’ onerror=alert(/XSS/)>
message XXX
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进行了非常严格的过滤。
漏洞复现
但是对于name依旧可以采取其他标签的形式绕过:<img src=’’ onerror=alert(/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();
?>
这里对message和name变量都进行了严格的过滤,而且还检测了用户的token,没有办法进行绕过了。
又是朴实无华的一天!!!!!!!!!!!!