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