xss和sql注入简单环境的搭建

本文最后更新于:2 年前

xss和sql注入简单环境的搭建

以下环境都是基于PHP study搭建的,版本为 php 5.5.38+Apache

参考了dvwa的漏洞源码与攻击方式。

xss漏洞的搭建

1.网页源码

<!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.攻击效果

在输入栏中输入以下

<script>alert("xss")</script>

<img src=1 onerror=alert(/xsss/)>

xss.png

xss1.png

3.漏洞修复

修复漏洞只需要对用户的输入内容进行检测和过滤,并将一些可能造成攻击的特殊字符进行转义,让其不起到原本的作用。

过滤函数如下

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); //给单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)  加上反斜线转义
          }
          $value       = preg_replace($ra,'',$value);     //删除非打印字符
          $arr[$key]     = htmlentities(strip_tags($value)); //去除 HTML 和 PHP 标记并转换为 HTML 实体
        }
        else
        {
          SafeFilter($arr[$key]);
        }
     }
   }
}

各个函数功能如下:

magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误

在magic_quotes_gpc=On的情况下,如果输入的数据有

单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)等字符都会被加上反斜线。

addslashes函数

addslashes.png

htmlentities() 函数把字符转换为 HTML 实体。

sql注入环境搭建与攻击

网页源码,最常见的登录页面,其中没有对用户名和密码进行过滤,就将其带入sql语句中查询造成了sql注入的出现。

login.php源码

<!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源码

<?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";
}
// echo "</TABLE>";
// echo "</div>";
$mysqli->close();
$result->close();
?>

逻辑很简单,在login.php页面提交用户名和密码,将username和password发送到check.php页面连接数据库检查用户是否合法,用户名和密码都正确则,打印出用户名和密码。

在数据库建立了一张users和flag表,便于注入。

表中的内容如下

sql1.png

sql2.png

sql3.png

1.漏洞利用

直接使用万能密码登陆

username:1' or 1=1 #
username:111

结果打印出了第一个用户的用户名和密码

sql4.png

这个结果也说名了是字符型注入,接下来利用改注入点获取flag

判断表有几列

1' order by 3#
页面显示正常,而改为4的时候网页出现报错,说明了只有三列
1' order by 4#

判断显示位

' union select 1,database(),3#

说明有两个显示位,选择其中一个位置进行注入即可。

获取表名

' union select 1,group_concat(table_name),3 from information_schema.TABLES where TABLE_SCHEMA=database()#

sql6.png

获取列名

' union select 1,group_concat(COLUMN_name),3 from information_schema.COLUMNS where TABLE_NAME='flag'#

sql7.png

(fl4g是dvwa实验中建立没有删除,所以也显示出来了)

获取flag

' union select 1,group_concat(flag),3 from flag#

sqlflag.png

2.漏洞防御

方法一:

最简单的方法对用户名和密码的长度限制,一般用户名的长度不超过十五个字符,而密码的长度一般不超过16个字符长度,所以对用户输入限制长度是最有效的方法之一。因为一般的注入语句都是超过十六个字符的,想要在十六个字符之内构造出有效的注入语句是一件很难的事情。

代码实现

<?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.";
}
// echo "</TABLE>";
// echo "</div>";
$mysqli->close();
$result->close();
?>

方法二

对用户输入进行检测和过滤,将其输入的可能产生恶意行为的代码删除或者转义,使其失去原来的功能。

代码实现

<?php
$pwd=$_POST['password'];
$uname=$_POST['username'];
//echo "select * from admin where passward='$pwd' and name='$uname'<br/>";
//echo "<hr>";

function inject_check($Sql_Str) {//自动过滤Sql的注入语句。
   $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(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.";
}
// echo "</TABLE>";
// echo "</div>";
$mysqli->close();
$result->close();
?>

过滤函数如下,其中将一般注入需要用到的函数和符号都过滤了。

function inject_check($Sql_Str) {//自动过滤Sql的注入语句。
    $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;
    }
}

方法三

使用预编译语句

代码如下

<?php
$pwd=$_POST['password'];
$uname=$_POST['username'];

// function inject_check($Sql_Str) {//自动过滤Sql的注入语句。
//     $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();
}

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.";
}



// if(strlen($pwd)>=16||strlen($uname)>=15)
// {
// 	echo "It is too long.";
// }
echo "</TABLE>";
echo "</div>";
$result->close();
$mysqli->close();
?>

应用预编译语句后,再次输入注入语句后就不再起到注入作用,只是将其当成正常的查询过程,返回相应的结果。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

 目录

Copyright © 2020 my blog
载入天数... 载入时分秒...