Contents
Preface
本博文仅用于信息安全防御,切勿用于其他用途!!!!!
XSS
XSS:跨站攻击脚本
恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页时
,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户
的目的。
OWASP TOP10 一直排前十
XSS 可以用来进行钓鱼攻击,前端js挖矿、获取用户cookie,甚至对
用户主机进行控制
XSS形成原因:
程序对输入和输出的控制不够严格,导致攻击者的脚本输入后,
在输出到前端时被浏览器当作有效的代码解析执行从而产生危害。
XSS攻击的危害:
1.盗取账号
2.控制企业数据,读取、篡改、添加、删除敏感数据
3.盗窃企业重要的具有商业价值的资料
4.非法转账
5.强制发送电子邮件
6.网站挂马
7.控制受害者机器,向其它网站发起攻击
XSS(Reflected)
反射型XSS,也叫非持久性CSS,当用户访问一个带有XSS代码的
URL请求时,服务器端接收数据后处理,然后把带有XSS代码
的数据发送到浏览器,浏览器解析这段XSS代码的数据后,
最后造成XSS漏洞。这个过程就像一次反射,故称反射型XSS
反射型XSS不会存储在服务器端 我们利用需要结合社工等条件。
XSS(Stored)
存储型XSS
存储型XSS和反射型XSS形成原因一样,不同的是存储型XSS
后台会将构造的payload保存起来,构成更加持久的危害,因此
存储型XSS也称“永久型XSS”
存储型XSS 不需要我们结合社工等条件触发,破坏性要大于反射型XSS
可以从网上搜集payload
XSS(DOM)
HTML DOM 树
DOM XSS 是一种特殊类型的反射型 XSS, 它是基于DOM文档
对象模型的一种漏洞。
Low
XSS(Reflected)
Source Code
<?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>';
}
?>
array_key_exists(key,array) 函数检查某个数组中是否存在指定的键名,如果键名存在则返回 true,如果键名不存在则返回 false
key 必需。规定键名。
array 必需。规定数组。
Solution
这关我们由源代码知道,作者并没有对我们的输入进行过滤,所以我们可以任意插入js代码,比如我们插入
<script>alert(1)</script>
XSS(Stored)
Source Code
<?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();
}
?>
$GLOBALS — 引用全局作用域中可用的全部变量
trigger_error(errormsg,errortype) 函数创建用户级别的错误消息。
errormsg 必需。规定错误消息。最大长度 1024 字节。
errortype 可选。规定错误类型。可能的值:
E_USER_ERROR
E_USER_WARNING
E_USER_NOTICE(默认)
mysql_query(query,connection) 函数执行一条 MySQL 查询
query 必需。规定要发送的 SQL 查询。注释:查询字符串不应以分号结束。
connection 可选。规定 SQL 连接标识符。如果未规定,则使用上一个打开的连接。
作者对我们的输入进行了两边去空,然后去除了message变量的反斜杠,并且对我们的输入SQL语句中的特殊字符,但并不影响我们进行xss攻击。
Solution
我们可以直接从message一栏输入我们想要攻击的代码(因为name一栏的最长限制为10,所以如果我们想要在name处攻击,那么就需要更改前端或者抓包更改)
<script>alert(1)</script>
XSS(DOM)
Source Code
服务器端代码
<?php
# No protections, anything goes
?>
Solution
由源代码知道,作者没有做任何防护,所以我们直接在地址栏提交我们的xss攻击
http://127.0.0.1/dvwa/vulnerabilities/xss_d/?default=%3Cscript%3Ealert(1)%3C/script%3E
Medium
XSS(Reflected)
Source Code
<?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>";
}
?>
Solution
这关相较与Low难度,对我们输入的script标签进行了过滤且替换为空,我们一听到替换为空,那么我们就想到用双写或者大小写混写来进行绕过处理
所以我们可以输入
<s<script>cript>alert(1)</s<script>cript>
<sCript>alert(1)</sCript>
XSS(Stored)
Source Code
<?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();
}
?>
strip_tags(string,allow) 函数剥去字符串中的 HTML、XML 以及 PHP 的标签。
string 必需。规定要检查的字符串。
allow 可选。规定允许的标签。这些标签不会被删除。
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
NULL
Solution
此关对我们的message变量彻底进行了过滤,首先在预定义字符增加反斜杠,然后删除我们的html标签,最后又将我们的预定义字符转换为html实体,所以彻底杜绝了我们的攻击
但是对于name变量它只把script标签替换为空,我们可以采用混写或者双写来进行绕过。
我们首先通过更改前端来把最大长度更改,然后写入我们的注入代码
<sCript>alert(1)</sCript>
<s<script>cript>alert(2)</s<script>cript>
XSS(DOM)
Source Code
<?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;
}
}
?>
Solution
这关变化的是,它把我们的script标签进行了处理,只要我们输入script标签(无论大小写)就会跳转到主页面,所以这关我们可以用其它标签进行绕过。(由前端代码得知我们是往option标签拼接语句,所以需要我们注意的是如果用其他标签进行攻击的时候,就要闭合其它的标签)
</option></select><img src='' onerror=alert(1)>
</option></select><body onload=alert(3)>
High
XSS(Reflected)
Source Code
<?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>";
}
?>
Solution
此关源代码,把我们的script只要是按照顺序输入的都替换成了空,且不区分大小写,所以我们的双写绕过和混写绕过都无法进行了,所以我们就不能用script标签了,但是我们可以用运用body、img、iframe等标签配合事件来进行绕过
<body onload=alert(1)></body>
<a herf='' onclick=alert(1)>click here</a>
<img src='' onerror=alert(2)>
//因为src地址错误,所以执行onerror
XSS(Stored)
Source Code
<?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();
}
?>
Solution
此难度相较于medium难度来说,对于message变量的过滤一样严格,而且还相当于屏蔽了我们的script标签,意味着我们需要用别的标签来进行xss攻击
<body onload=alert(3)>
<img src='' onerror=alert(3)>
<a herf onclick=alert(3)>click here</a>
XSS(DOM)
Source Code
<?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;
}
}
?>
Solution
作者这会直接用白名单来限制我们的选择只能是它提供的,如果不是,则直接跳转到主页面
但是我们可以用#来绕过,因为在url中#后面的数据不会传到服务器端。
具体可以参考这片文章:参考文章
所以我们可以构造如下payload
#<script>alert(1)</script>
Impossible
XSS(Reflected)
Source Code
<?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 实体。
预定义的字符是:
& (和号)成为 &
" (双引号)成为 "
' (单引号)成为 '
< (小于)成为 <
> (大于)成为 >
Solution
此难度下,作者不仅验证了我们的token还将我们的所有的输入都当做字符串,且预定义字符转换为HTML实体,因为我们注入的js代码都是标签,都必须有<>,所以我们就彻底不能进行绕过了
XSS(Stored)
Source Code
<?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();
?>
Solution
由源代码可知,作者不仅验证了我们的身份,而且还对我们的所有输入进行了 htmlspecialchars过滤,所以我们无法进行xss攻击
XSS(DOM)
Source Code
<?php
# Don't need to do anything, protction handled on the client side
?>
这里服务器端代码没有了,那么一定是在前端代码上做了手脚。
Solution
我们可以看到前端的源码与前三个难度的源码唯一区别是在,对于lang变量的url解码,但是impossible难度并没有对我们的输入进行解码,所以上传上去的只是URL编码,所以就不存在了XSS攻击。
本文所有函数解析参考自w3cschool