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/)>
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函数
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表,便于注入。
表中的内容如下
1.漏洞利用
直接使用万能密码登陆
username:1' or 1=1 #
username:111
结果打印出了第一个用户的用户名和密码
这个结果也说名了是字符型注入,接下来利用改注入点获取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()#
获取列名
' union select 1,group_concat(COLUMN_name),3 from information_schema.COLUMNS where TABLE_NAME='flag'#
(fl4g是dvwa实验中建立没有删除,所以也显示出来了)
获取flag
' union select 1,group_concat(flag),3 from flag#
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 协议 ,转载请注明出处!