天天看点

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

01 二次注入原理

二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。

二次注入,可以概括为以下两步:

  • 第一步:插入恶意数据

    进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。

  • 第二步:引用恶意数据

    开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

配合下图可以有更好的理解:

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

二次注入

02 二次注入方法

这里我们使用sqli-labs/Less24为例,进行二次注入方法的练习。

打开页面可以看到一个登陆界面,尝试用

admin'#

进行注入,失败。

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

测试失败

部分源代码如下:

$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'#

的账号,登陆该账号后可以进行密码修改。

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

注册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']);
                 
<span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;font size='3' color='#FFFF00'&gt;"</span>;
$sql = <span class="hljs-string">"select count(*) from users where username='$username'"</span>;
$res = mysql_query($sql) <span class="hljs-keyword">or</span> <span class="hljs-keyword">die</span>(<span class="hljs-string">'You tried to be smart, Try harder!!!! :( '</span>);
$row = mysql_fetch_row($res);

<span class="hljs-comment">//print_r($row);</span>
<span class="hljs-keyword">if</span> (!$row[<span class="hljs-number">0</span>]== <span class="hljs-number">0</span>) 
    {
    <span class="hljs-meta">?&gt;</span>
    &lt;script&gt;alert(<span class="hljs-string">"The username Already exists, Please choose a different username "</span>)&lt;/script&gt;;
    <span class="hljs-meta">&lt;?php</span>
    header(<span class="hljs-string">'refresh:1, url=new_user.php'</span>);
    } 
    <span class="hljs-keyword">else</span> 
    {
        <span class="hljs-keyword">if</span> ($pass==$re_pass)
        {
            <span class="hljs-comment"># Building up the query........</span>
            
            $sql = <span class="hljs-string">"insert into users ( username, password) values(\"$username\", \"$pass\")"</span>;
            mysql_query($sql) <span class="hljs-keyword">or</span> <span class="hljs-keyword">die</span>(<span class="hljs-string">'Error Creating your user account,  : '</span>.mysql_error());
                <span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;/br&gt;"</span>;

          ··················
                

可以看到传入的

username

password

re_password

仍均被

mysql_escape_string

进行了转义处理,但是在数据库中还是插入了

admin'#

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

数据库数据

这是因为当数据写入到数据库的时候反斜杠会被移除,所以写入到数据库的内容就是原始数据,并不会在前面多了反斜杠。

这时,我们用

admin'#

登陆,并进行密码修改,密码修改为`123456``

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

捕获.PNG

执行后,查看数据库数据:

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

数据库数据2

可以看到

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();
$sql="SELECT * FROM users ORDER BY id";
$result=mysql_query($sql);
$num=mysql_num_rows($result);
for ($i=; $i < $num; ++$i) { 
    $row = mysql_fetch_array($result);
    $username = $row[];
    $sql_detail = "SELECT * FROM users where username='$username'";
    $result_detail=mysql_query($sql_detail);
    $num_detail = mysql_num_rows($result_detail);
    for ($j=; $j < $num_detail; ++$j) { 
        $row_detail = mysql_fetch_array($result_detail);
        echo<<<END
        <table  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

,可以看到:

二次注入 sqlilabs less-2401 二次注入原理02 二次注入方法03 二次注入之CTF实例

捕获.PNG

这是因为我们在注册新用户时,

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

</div>
    </div>