简介
XSS攻击(Cross Site Scripting,跨站脚本攻击)通常指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
XSS漏洞通常是通过php的输出函数将javascript代码输出到html页面中,通过用户本地浏览器执行的,所以xss漏洞关键就是寻找参数未过滤的输出函数
常见的输出函数有: echo
、printf
、print
、print_r
、sprintf
、die
、var-dump
、var_export
XSS主要分为三类:存储型XSS、反射型XSS和DOM型XSS
DVWA——存储型XSS
什么是存储型XSS
攻击者可以通过XSS漏洞将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。这就意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本,因此储存型XSS的危害会更大。因为存储型XSS的代码存在于网页的代码中,可以说是永久型的。
存储型 XSS 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。
Low
查看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php if ( isset ( $_POST [ 'btnSign' ] ) ) { $message = trim ( $_POST [ 'mtxMessage' ] ); $name = trim ( $_POST [ 'txtName' ] ); $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)) ? "" : "" )); $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)) ? "" : "" )); $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>' ); } ?>
从源码中可以看出,这个对输入的东西没有任何过滤与检查,并且数据是存储在数据库中的。
在Message栏中输入 <script>alert(123)</script>
,Name随便填入一个值,点击Sign Guestbook
提交数据
等页面刷新后会产生弹窗123,说明XSS漏洞存在并且能够利用成功。
再进行尝试也会发现Name框也是存在XSS漏洞的,但是它有10个字符的长度限制,我们可以使用F12的开发者工具修改长度限制,或者直接使用BurpSuite抓包注入绕过限制。
Medium
查看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php if ( isset ( $_POST [ 'btnSign' ] ) ) { $message = trim ( $_POST [ 'mtxMessage' ] ); $name = trim ( $_POST [ 'txtName' ] ); $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 ); $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)) ? "" : "" )); $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>' ); } ?>
可以看到,对于Message参数使用了htmlspecialchars()
进行编码,所以无法在这里利用XSS注入代码。对于Name参数,只有一个字符串替换函数,它只过滤<script>
,将<script>
字符替换为空值,所以可以尝试使用双写或者大写绕过,亦或者使用img,iframe,a等标签绕过
双写或者大写绕过:
<SCRIPT>alert(234)</script>
和 <scr<script>ipt>alert(234)</script>
这里同样也需要多做一步绕过长度限制
a标签 绕过:
这里使用BurpSuite抓包以绕过长度限制
首先,Name参数和Message参数填写任意值并提交使BurpSuite拦截到数据包
然后修改下面的txtName
参数为我们要注入的弹窗代码
1 <a href =javascript:alert(789) > Link</a >
然后点击Forward
发送请求包,关闭BP拦截,就可以在页面看到一个链接
点击链接就可以出发弹窗的恶意代码。
High
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php if ( isset ( $_POST [ 'btnSign' ] ) ) { $message = trim ( $_POST [ 'mtxMessage' ] ); $name = trim ( $_POST [ 'txtName' ] ); $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 ); $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)) ? "" : "" )); $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>' ); } ?>
通过源码可以看到,Message参数依然不能注入,然而对于Name参数,有更高一级的过滤,不再是过滤字符串,而是直接过滤<script>
标签,这里我们就无法再使用含有<script>
标签的代码进行注入,可以换用其他方式进行注入
使用img标签 :
1 <img src =0 onerror =alert(345) >
这里将src的地址设置为一个不可能的地址,所以会报错触发alert弹窗
使用iframe标签 :
1 <iframe onload =alert(345) >
impossible
再看一下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php if ( isset ( $_POST [ 'btnSign' ] ) ) { checkToken ( $_REQUEST [ 'user_token' ], $_SESSION [ 'session_token' ], 'index.php' ); $message = trim ( $_POST [ 'mtxMessage' ] ); $name = trim ( $_POST [ 'txtName' ] ); $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 ); $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 ); $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 (); } generateSessionToken ();?>
没什么漏洞,依旧无懈可击~~ 世界依然很美好~~
DVWA——反射型XSS
什么是反射型XSS
如果一个应用程序使用动态页面向用户显示错误消息,就会造成一种常见的XSS漏洞。通常,该页面
会使用一个包含消息文本的参数,并在响应中将这个文本返回给用户。对于开发者而言,使用这种
机制非常方便,因为它允许他们从应用程序中调用一个定制的错误页面,而不需要对错误页面中的
消息分别进行硬编码。
Low
看一下源码:
1 2 3 4 5 6 7 8 9 10 11 <?php header ("X-XSS-Protection: 0" );if ( array_key_exists ( "name" , $_GET ) && $_GET [ 'name' ] != NULL ) { $html .= '<pre>Hello ' . $_GET [ 'name' ] . '</pre>' ; } ?>
从源码可以知道,代码没有对Name参数的值做任何过滤,所以很容易存在XSS漏洞。
在输入框输入<script>alert('hacked')</script>
并提交
可以成功执行弹窗的恶意代码
Medium
查看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php header ("X-XSS-Protection: 0" );if ( array_key_exists ( "name" , $_GET ) && $_GET [ 'name' ] != NULL ) { $name = str_replace ( '<script>' , '' , $_GET [ 'name' ] ); $html .= "<pre>Hello ${name}</pre>" ; } ?>
过滤方式同存储型XSS基本相同,都是只过滤<script>
字符串,所以可以使用双写或者大写来绕过过滤,也可以使用其他标签实现恶意代码的注入。
1 <Script > alert ("hack!" )</Script >
这里使用大写来绕过script的过滤,提交之后弹出自己构造的弹窗,说明攻击成功
High
先看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php header ("X-XSS-Protection: 0" );if ( array_key_exists ( "name" , $_GET ) && $_GET [ 'name' ] != NULL ) { $name = preg_replace ( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i' , '' , $_GET [ 'name' ] ); $html .= "<pre>Hello ${name}</pre>" ; } ?>
这里也是对<script>
标签进行了过滤,无法通过对标签大小写变形来实现注入
注入代码:
1 <img src =0 onerror =alert( "hacked_high ")>
填入注入代码后提交就可以在页面弹窗实现恶意攻击
对于反射型XSS,它的注入参数多半是在URL中提交的,以High级别为例,它的URL是 http://127.0.0.1/DVWA/vulnerabilities/xss_r/?name=%3Cimg+src%3D0+onerror%3Dalert%28%22hacked_high%22%29%3E#
,可以看到我们刚才注入的语句也是包含在URL中提交的,所以反射型的XSS是非持久性的。
DOM型XSS
什么是DOM型XSS
基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无
关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显
示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如URL ,location,refelTer
等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器
端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生
DOM XSS漏洞。
Low
先看一下源码:
从源代码可以看出,这里low级别的代码没有任何的保护性措施
这里是让我们选择默认语言,但是对default参数没有任何的过滤,所以我们可以在URL中构造XSS代码:
1 http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=<script>alert('hack')</script>
可以看到,代码执行成功了
Medium
先看一下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php if ( array_key_exists ( "default" , $_GET ) && !is_null ($_GET [ 'default' ]) ) { $default = $_GET ['default' ]; if (stripos ($default , "<script" ) !== false ) { header ("location: ?default=English" ); exit ; } } ?>
可以看到,medium级别的代码先检查了default参数是否为空,这里还使用了stripos 用于检测default值中是否有 <script
,如果有的话,则将 default=English
很明显,这里过滤了<script
(不区分大小写),那么我们可以使用<img src=1 onerror=alert('hack')>
进行注入,但是,不行,网页没有弹窗出现
查看源码发现注入的语句是存在value的值中的,需要闭合前面的option标签,于是添加闭合的语句重新构造:
1 ></option > <img src =1 onerror =alert( 'hack ')>
但是我们的注入语句还是没有执行,查看源代码,发现我们注入的语句还是没有逃出<option>
标签的魔爪,依然在它的value参数的值中
看到上面还有<select>
标签,于是考虑将select标签再进行闭合,注入的语句为:
1 ></option > </select > <img src =1 onerror =alert( 'hack ')>
这次是注入成功的
再查看源码,可以看到我们注入的img标签语句被单独列出来执行了
High
先看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php if ( array_key_exists ( "default" , $_GET ) && !is_null ($_GET [ 'default' ]) ) { switch ($_GET ['default' ]) { case "French" : case "English" : case "German" : case "Spanish" : break ; default : header ("location: ?default=English" ); exit ; } } ?>
可以看到,服务器端代码会先判断default的值是否为空,如果不为空则会按照switch语句进行
匹配,匹配不上则使用默认值。所以我们要想执行代码,不能将代码传入服务器后台,必须使用
一种无须将代码发送到服务器即可运行代码的方法。
URL锚 :URL的锚(指URL中#
之后的所有内容)不会发送到服务器,所以不会被服务器端的代码阻止掉。
当我们注入恶意代码的时候,代码不会从服务器加载过来,而是由浏览器加载,从而造成恶意代码的执行。
含有恶意代码的链接:
1 http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=English # <script>alert(101)</script>
加载链接后,页面产生了弹窗
查看源代码,可以看到恶意代码被加载了
impossible
都告诉你不可能了,所以,就想都不要想了—.—