AI 摘要(由 ChatGPT 总结生成):
文章讲述了SQL注入的二次注入原理和防范方法。二次注入是SQL注入的一种,主要有两步:首先,攻击者构造一个恶意的SQL语句并存储在数据库中;然后,将存储在数据库中的恶意SQL语句读取并查询,从而发生SQL二次注入。防范二次注入主要有两点:使用MySQLi参数化更新,以及对所有输入进行过滤和转义,无论输入来自用户还是存储。

前言

最近看到一个文档有提到关于 SQL 注入的二次注入,来了一点兴趣,遂简单水个文。

原理

二次注入也是 SQL 注入的一种,平时比较难发现。该漏洞的发生主要有两步,第一步是攻击者构造一个恶意的 SQL 语句,SQL 语句存储在数据库中;第二步是将存储在数据库中的恶意 SQL 语句读取并查询,于是就发生了 SQL 二次注入。

整个流程即:插入恶意数据、利用恶意数据。

靶场演示

这里可参考 SQLI-Labs 靶场第 24 号靶场。

1、 打开靶场,用一些 SQL 语句简单测试注入一下皆无果:

image-20240701140452067

简单阅读一下相关登录逻辑代码,发现使用了 mysqli_real_escape_string 转义函数对传入的字符串进行了转义,一些常规的 SQL 注入流程就不容易进行了。

function sqllogin($con1){
   
   $username = mysqli_real_escape_string($con1, $_POST["login_user"]);
   $password = mysqli_real_escape_string($con1, $_POST["login_password"]);
   $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
   //$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'";
   $res = mysqli_query($con1, $sql) or die('You tried to be real smart, Try harder!!!! :( ');
   $row = mysqli_fetch_row($res);
    //print_r($row) ;
   if ($row[1]) {
    return $row[1];
   } else {
    return 0;
   }
   
}

2、 于是从注册账户那里去注册一下,这里我们注册的账号为:admin'#

image-20240701141242859

注册成功后并登录刚刚注册的账号,发现可以修改密码:

image-20240701141624785

这里我们重新修改一下当前用户的密码:

image-20240701142230264

接下来,我们使用系统内的 admin 用户,在结合上述刚刚修改的密码,可发现成功登录 admin 用户:

image-20240701142422107

代码分析

既然该题是关于 SQL 二次注入的,且上述涉及到了注册、修改密码的操作,此举成功将原注册用户 admin'# 在修改密码操作时将系统内的 admin 用户“误伤”,从而将 admin 用户密码进行了修改。下面将从用户创建和修改密码的代码分析一下:

1、 login_creat.php 分析:

……
if (isset($_POST['submit']))
{


    # Validating the user input........

    //$username=  $_POST['username'] ;
    $username=  mysqli_real_escape_string($con1, $_POST['username']) ;
    $pass= mysqli_real_escape_string($con1, $_POST['password']);
    $re_pass= mysqli_real_escape_string($con1, $_POST['re_password']);

    echo "<font size='3' color='#FFFF00'>";
    $sql = "select count(*) from users where username='$username'";
    $res = mysqli_query($con1, $sql) or die('You tried to be smart, Try harder!!!! :( ');
    $row = mysqli_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\")";
            mysqli_query($con1, $sql) or die('Error Creating your user account,  : '.mysqli_error($con1));
            echo "</br>";         
             ……
}
……

在注册用户时,仍然使用了 mysqli_real_escape_string 函数对传入的usernamepasswordre_password 字符进行了转义并重新赋值给相关变量,然后判断注册的用户是否已存在数据库中。若数据库中无该用户,且 $pass$re_pass 变量类型一致则进行插入语句操作来进行用户的新增,其语句如下:

$sql = "insert into users (username, password) values(\"$username\", \"$pass\")";

注意这里将数据写入数据库时还是使用原来的数据,也就是将你注册的用户名和密码完完全全插入数据库的。

2、 pass_change.php 分析:

……
if (isset($_POST['submit']))
{
    
    
    # Validating the user input........
    $username= $_SESSION["username"];
    $curr_pass= mysqli_real_escape_string($con1, $_POST['current_password']);
    $pass= mysqli_real_escape_string($con1, $_POST['password']);
    $re_pass= mysqli_real_escape_string($con1, $_POST['re_password']);
    
    if($pass==$re_pass)
    {    
        $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
        $res = mysqli_query($con1, $sql) or die('You tried to be smart, Try harder!!!! :( ');
        $row = mysqli_affected_rows($con1);
        echo '<font size="3" color="#FFFF00">';
        echo '<center>';
        ……
}
……

可以看到,此处仍使用了 mysqli_real_escape_string 函数对提交的 current_passwordpasswordre_password 参数进行了转义并赋值给相关变量,但 username 则未进行转义。此时 $pass$re_pass 变量类型一致则进行更新相关用户的密码操作。注意下述的 SQL 语句:

$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";

此处的 username 则是一开始我们注册的含有恶意字符的用户名,此时,我们若传入该 admin'# 参数,其 SQL 更新语句就会变成:

UPDATE users SET PASSWORD='$pass' where username='admin'# and password='$curr_pass' 

显而易见,此处原本修改的是 admin'# 用户的信息,现在却变成了修改 admin 用户的信息。

其用户admin'# 中的 ' 号此时则是将原 SQL 语句形成闭合,# 号则将后续的 SQL 语句注释掉不在执行,这就是二次注入达到的效果。于是造成了拿刚刚修改后的相关密码去登录 admin 用户,发现成功登入。

防范

SQL 注入的二次注入,往往需结合代码审计去发现,平时黑盒测试过程时很难发现的。防范主要有以下几点:

1、使用 MySQLi 参数化更新

2、对输入一视同仁,无论输入来自用户还是存储,在进入到 SQL 查询前都对其进行过滤、转义。

End

本文标题:SQL注入之二次注入

本文链接:https://www.isisy.com/1556.html

除非另有说明,本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

声明:转载请注明文章来源。

如果觉得我的文章对你有用,请随意赞赏