基于约束的SQL攻击

本文转载自基于约束条件的SQL攻击SQL Attack(Constraint-based)
最近,我遇到了一个有趣的代码片段。
当新用户尝试注册时,将运行以下代码。

<?php
// Checking whether a user with the same username exists
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT *
          FROM users
          WHERE username='$username'";
$res = mysql_query($query, $database);
if($res) {
  if(mysql_num_rows($res) > 0) {
    // User exists, exit gracefully
    .
    .
  }
  else {
    // If not, only then insert a new entry
    $query = "INSERT INTO users(username, password)
              VALUES ('$username','$password')";
    .
    .
  }
}

使用以下代码验证登录信息。

<?php
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT username FROM users
          WHERE username='$username'
              AND password='$password' ";
$res = mysql_query($query, $database);
if($res) {
  if(mysql_num_rows($res) > 0){
      return $username;//此处较原文有改动
  }
}
return Null;
?>

过滤了用户输入的参数,使用单引号来增加安全性,按理说应该不会出错,然而攻击者依然能够以任意用户身份进行登录。在谈论这种攻击手法之前,首先我们需要了解几个关键知识点。在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说"vampire"等同于"vampire  ",对于绝大多数情况来说都是成立的。例如以下语句的查询结果,与使用用户名"vampire"进行查询时的结果是一样的。
SELECT * FROM users WHERE username='vampire     ';
但也存在异常情况,最好的例子就是LIKE子句了。注意,对尾部空白符的这种修剪操作,主要是在字符串比较期间进行的。这是因为,SQL会在内部使用空格来填充字符串,以便在比较之前使其它们的长度保持一致。在所有的INSERT查询中,SQL都会根据varchar(n)来限制字符串的最大长度。也就是说,如果字符串的长度大于n个字符的话,那么仅使用字符串的前n个字符。比如特定列的长度约束为5个字符,那么在插入字符串"vampire"时,实际上只能插入字符串的前5个字符,即"vampi"。现在,让我们建立一个测试数据库来演示具体攻击过程。执行set @@sql_mode=ANSI;将数据库设置成宽松模式,在这种模式下插入数据如果不符合定义类型或长度,对数据类型调整或截断保存并警告。 



为了展示尾部空白字符的修剪情况,我们可以键入下列命令。

现在我们假设一个存在漏洞的网站使用了前面提到的PHP代码来处理用户的注册及登录过程。为了侵入vampire的账户,只需要使用用户名vampire+若干个空格+1和一个随机密码进行注册即可。

需要注意的是,在执行SELECT查询语句时,SQL是不会将字符串缩短为25个字符的。因此,这里将使用完整的字符串进行搜索,所以不会找到匹配的结果。接下来,当执行INSERT查询语句时,它只会插入前25个字符。

SELECT username FROM users WHERE username='vampire' AND password='random_pass';
当执行这条语句后,数据库将返回我们自己注册的账户信息,但是注意此处的return $username,虽然此时查询出来的是我们自己的用户信息,但是返回的用户名则是目标的用户名。如果此后的业务逻辑直接以该用户名为准,则我们就达到了水平越权的目的。
这种攻击方式有以下限制条件。
1.mysql处于ANSI模式。如果是TRADITIONAL模式或者STRICT_TRANS_TABLES模式会报错data too long for column。
1.服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就自然就不客能导致数据库截断,也就没有利用条件。
2.登陆验证的SQL语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码然后再比对密码,当使用vampire为用户名来查询密码的话,数据库此时就会返回两条记录,而一般取第一条即目标用户的记录,那么传输的密码肯定和目标用户密码匹配不上的。
3.验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户vampire和密码random_pass登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是vampire+若干个空格,如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。


阅读更多
换一批

没有更多推荐了,返回首页