sqli-labs 通关指南:Less 24

由于 Less 24 中每次提交参数都会切换页面,因此没有任何注入点。此处考虑的就不是暴露敏感信息了,而是进行其他的破坏,例如夺取其他账号的权限。这时就需要考虑二次注入,此时我们注入恶意的参数,它不会被马上执行而是存入数据库。当该恶意参数被网页调用时,此时的参数往往是不转义的,就会产生攻击的效果。


Second Degree Injections Real treat-Store Injections(二次注入)

判断注入类型

打开网页看到一个登陆界面,输入正确的用户名后是修改密码的界面。改密码涉及到对数据库的记录进行替换,我们怀疑此处有一个 UPDATE 语句。

在任何界面进行注入应该都是无效的,因为操作失败时会跳转到其他页面,而没有任何例如错误的回显信息。此处考虑的就不是之前那些把敏感信息弄出来的注入了,而是考虑利用改密码操作夺取其他账号的控制权。

二次注入

此处我们考虑二次注入,首先我们构造一个特殊的用户,该用户的用户名为 “admin'#”,密码随便设。

用这个特殊的用户进行登录,可以看到网页回显了用户名。

此时我们修改密码,改成什么密码随意,只要修改成功就像。

此时如果将这个用户作为过滤条件实现记录的修改,该用户名后面的 “'#” 不仅能闭合字段,也能把后面的内容注释掉。而且成功闭合后,我们实际上操作的用户名应该是 “admin”。修改密码成功之后,使用用户名 “admin” 和我们修改的密码进行登录,发现我们夺去了该用户的密码,登录成功。

网页源码

登录界面

首先看一下登录界面的源码,源码首先使用 POST 方法读取输入的用户名和密码,然后执行一个 SELECT 查询。如果查询成功,就返回查询到的第一个记录。

function sqllogin(){
      $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'";
      //$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'";
      $res = mysql_query($sql) or die('You tried to be real smart, Try harder!!!! :( ');
      $row = mysql_fetch_row($res);
      //print_r($row) ;
      if ($row[1]) 
      {
            return $row[1];
      } 
      else 
      {
      	    return 0;
      }
}

调用登录函数,如果有查询到记录就分配一下 cookie,然后跳转到 logged-in.php 页面上。如果登录失败就输出失败信息,从中我们可以看到这里是没有注入点的。

$login = sqllogin();
if (!$login== 0) 
{
      $_SESSION["username"] = $login;
      setcookie("Auth", 1, time()+3600);  /* expire in 15 Minutes */
      header('Location: logged-in.php');
} 
else
{
      ?>
      <tr><td colspan="2" style="text-align:center;"><br/><p style="color:#FF0000;">
      <center>
      <img src="../images/slap1.jpg">
      </center>
      </p></td></tr>
      <?PHP
} 

注册页面

注册界面使用 SELECT 语句对用户名进行查找,如果查找不到记录就可以使用 INSERT 语句插入记录。mysql_escape_string() 函数用于对字符串进行转义,因此此处也是不能注入的,同时插入数据库中的特殊字符也会被转义。

if (isset($_POST['submit']))
{
      # Validating the user input........
      //$username = $_POST['username'] ;
      $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>";
		  echo "<center><img src=../images/Less-24-user-created.jpg><font size='3' color='#FFFF00'>";   				
		  //echo "<h1>User Created Successfully</h1>";
		  echo "</br>";
		  echo "</br>";
		  echo "</br>";					
		  echo "</br>Redirecting you to login page in 5 sec................";
		  echo "<font size='2'>";
		  echo "</br>If it does not redirect, click the home button on top right</center>";
		  header('refresh:5, url=index.php');
            }
	    else
	    {
                  ?>
		  <script>alert('Please make sure that password field and retype password match correctly')</script>
		  <?php
		  header('refresh:1, url=new_user.php');
            }
      }
}

密码修改页面

在这个页面中,使用 UPDATE 语句修改记录,以用户名和原密码作为过滤条件来过滤。

if (isset($_POST['submit']))
{
      # Validating the user input........
      $username = $_SESSION["username"];
      $curr_pass = mysql_real_escape_string($_POST['current_password']);
      $pass = mysql_real_escape_string($_POST['password']);
      $re_pass = mysql_real_escape_string($_POST['re_password']);
	
      if($pass == $re_pass)
      {	
            $sql = "UPDATE users SET PASSWORD ='$pass' where username ='$username' and password='$curr_pass' ";
            $res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
            $row = mysql_affected_rows();
            echo '<font size="3" color="#FFFF00">';
            echo '<center>';
            if($row == 1)
            {
                  echo "Password successfully updated";
            }
            else
            {
                  header('Location: failed.php');
                  //echo 'You tried to be smart, Try harder!!!! :( ';
            }
      }
      else
      {
            echo '<font size="5" color="#FFFF00"><center>';
            echo "Make sure New Password and Retype Password fields have same value";
            header('refresh:2, url=index.php');
      }
}

注入原理

观察上述源码中出现的所有 SQL 语句,都没有明显的注入点,因为没有任何的回显信息。但是观察到在密码修改的 UPDATE 语句前,用户名是没有进行转义的,也就是用户名本身可以夹带一些其他字符。

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' 

由于此时符号没有转义,因此该用户名的 “#” 在 SQL 语句中会当做是注释,也就是这样。

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

此时发现,我们并不需要原密码进行验证,并且直接修改了其他用户的密码。这就是所谓的 二次注入,这种注入发生在用户提交的值被存储在数据库中,这个值可能被转义过。当这个值被其他模块调用时,会使用而不转义的原来的数据,如果这个数据是恶意注入,在不转义的情况下就会生效。

posted @ 2020-11-05 00:47  乌漆WhiteMoon  阅读(893)  评论(0编辑  收藏  举报