SQL字符限制导致任意用户登陆
上个星期末打NJCTF,一共没做两个题,蓝瘦。
其中有一个Web,是一个后台登陆页面,可以注册新用户,试着注册登陆后,提示不是admin。
所以要以admin身份登陆,于是想到爆破,但感觉这个题并不是。纠结了好一会,表哥告诉是
基于约束的SQL攻击这个东西。
一般用户注册后端代码如下:
<?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){
$row = mysql_fetch_assoc($res);
return $row['username'];
}
}
return Null;
也利用mysql_real_escape_string()函数过滤了用户输入,但是我们攻击的点不在这里,而是在服务器的SQL上。
首先,SQL进行字符串处理时,大部分会将字符串后无用的空格删去(WHERE的字符串或者INSERT的字符串等),也就是说”string[空格]”与”string”是等同的。
当然也有例外,比如LIKE语句,因为LIKE函数比较时,要俩字符串长度相等,所以会将短的字符串后面填充空格。
所以假设username的varchar(10),我们注册username=’admin[十个空格]1’
SELECT * FROM users WHERE username='$username';
执行这条语句时,SQL查询的是’admin[十个空格]1’这个字符串,并不会找到匹配的结果。
INSERT INTO users(username, password) VALUES('admin 1','anypassword');
这里INSERT查询时,SQL会根据创建表时的varchar(n)来限制字符串的最大长度,所以我们插入的username就会变成admin加5个空格。
这样数据库中就会有两个admin用户了,当我们进行登陆admin时,SQL进行SELECT就会返回原始admin的记录,这样我们就可以以admin的身份登陆了。
此攻击已在MySQL和SQLite上成功测试。
如果要防护,可以使用UNIQUE约束,UNIQUE约束确保在非主键列中不输入重复的值。一般使用id作为数据库表的主键,在后端验证时可以验证id。