宽字节注入原理
宽字节注入也是MYSQL的一种特殊的注入类型,它是利用了数据库中对url中的%df%5c的原理形成一个運字,%5c是addslashes这个函数自动添加的一个反斜杠,因此利用这个原理,单引号%27就逃逸出来了。
漏洞展示
数字型
<?php
$id = intval($_GET['id']);
$conn = mysql_connect('localhost','root','root');
mysql_select_db('admin',$conn);
$sql = "SELECT * FROM user WHERE id =$id";
$result = mysql_query($sql) or die(mysql_error());
while ($row = mysql_fetch_array($result)){
echo "ID" .$row['id']."<br>";
echo "USERNAME".$row['username']."<br>";
echo "PASSWORD".$row['password']."<br>";
echo "EMAIL".$row['email']."<br>";
}
mysql_close($conn);
echo "<hr>";
echo $sql;
?>
可以看到,当我们加多一个intval函数对变量id进行强制转换的时候,1’会被强转为1,所以不会有任何的报错
字符型
<?php
$id = addslashes($_GET['id']);
$conn = mysql_connect('localhost','root','root');
mysql_select_db('admin',$conn);
mysql_query("SET NAMES 'GBK'");
$sql = "SELECT * FROM user WHERE id ='$id'";
$result = mysql_query($sql) or die(mysql_error());
while ($row = mysql_fetch_array($result)){
echo "ID" .$row['id']."<br>";
echo "USERNAME".$row['username']."<br>";
echo "PASSWORD".$row['password']."<br>";
echo "EMAIL".$row['email']."<br>";
}
mysql_close($conn);
echo "<hr>";
echo $sql;
?>
这里增多了一个addslashes函数,这个函数是用来防止最基本的sql注入的。它会将用户传入的单引号用反斜杠进行转义(也就是单引号没了),让数据库得到一个正常的sql查询语句
因此,我们利用上面设定的GBK编码进行单引号的逃逸,然后完成一系列的注入
修复方案
- 使用mysql_set_charset(GBK)指定字符集
- 使用mysql_real_escape_string进行转义
二次注入原理
二次注入的条件是用户向数据库插入恶意语句并且数据库对自己存储的数据非常放心,直接取出恶意数据给用户。二次注入无法通过扫描器发现,只能凭借经验和后端原理进行判断是否存在二次注入漏洞。
注入点因为经过过滤处理无法触发sql注入漏洞,比如addslashes函数,将单引号等字符转义变成\’。但是存进数据库后,数据又被还原,当数据库将恶意语句取出并且没经过过滤就赋值给sql语句进行执行,从而引发二次注入漏洞的出现。
漏洞展示
新建两个php文件,一个用于注册将信息存进数据库,一个用于查询数据库信息。
reg.php
<?php
header("content-type:text/html;charset=utf-8");
if(!empty($_POST['submit'])){
$id = addslashes($_POST['id']);
$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);
$email = addslashes($_POST['email']);
$conn = mysql_connect("localhost","root","root");
mysql_select_db("admin",$conn);
$sql = "INSERT INTO USER (id,username,password,email)VALUES ('$id','$username','$password','$email');";
$result = mysql_query($sql) or die("执行语句失败:".mysql_error());
if($result){
echo "注册成功";
}else{
echo "注册失败";
}
}else{
echo "NOT POST";
}
?>
<form action="reg.php" method="post">
id:<input type="text" name="id"><br />
username:<input type="text" name="username"><br />
password:<input type="password" name="password"><br />
email:<input type="text" name="email">
<input type="submit" name="submit" value="点击提交">
</form>
search.php
<?php
header("content-type:text/html;charset=utf-8");
if(!empty($_POST['submit'])){
$id = $_POST['id'];
$conn = mysql_connect("localhost","root","root");
mysql_select_db("admin",$conn);
$sql = "SELECT * FROM USER WHERE id='$id'";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)){
$username = $row['username'];
$sql = "SELECT * FROM USER WHERE username='$username'";
$result = mysql_query($sql) or die(mysql_error());
while($row = mysql_fetch_array($result)){
echo 'ID:'.$row['id']."<br>";
echo 'USERNAME:'.$row['username']."<br>";
echo 'PASSWORD:'.$row['password']."<br>";
echo 'EMAIL:'.$row['email'];
}
}
}
?>
<form action="search.php" method="post">
search ID:<input type="text" name="id"><br />
<input type="submit" name="submit" value="点击查询">
</form>
在用户名中填入' union select 1,2,3,4 #
,数据库显示注册成功
从这里可以看出信息在进入数据库时被还原为单引号,因此我们可以利用这个漏洞查询所需信息。
我们可以在username栏中输入' union select 1,database(),user(),4 #
,然后在search.php页面上查询相应的id号