做ctf的题时刚好做到要用sql约束攻击的题,赶紧去看看别人的文章学习一下这种攻击

题目大致上是一个登陆界面和一个注册界面,需要我们用管理员的身份登陆才能拿到flag值

登陆页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

include('connection.php');

if(isset($_POST['username']) && isset($_POST['password'])){
$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$sql = "SELECT username,password FROM users WHERE username='$username' AND password='$password'";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
if($row){
echo 'hello '.$username;
}
else{
echo 'username was not exist or password was wrong';
}
}

?>

注册页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

include('connection.php');

if(isset($_POST['username']) && isset($_POST['password'])){
$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$sql = "SELECT username FROM users WHERE username='$username'";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
if($row){
die('username has exist');
}
else{
$sql = "INSERT INTO users(username,password) values('$username','$password')";
$result = mysql_query($sql);
echo 'regist successfully';
header('location:login.php');
}
}

?>

看似对用户的输入进行了转义的过滤,能防止sql注入,但是我们还是有办法能登录任意已存在的账号

下面演示具体过程:

首先先创建一个数据库test,再test库下建立users表,里面的列名为username和password

注意这里对用户名和密码字符串长度限制最大为15位

接着创建一个管理员身份账户,用户名为admin,密码为123456

然后我们以攻击者的身份要以管理员身份登陆,首先注册用户,用户名为admin+若干个空格+1,这里空格在10个以上,原因后面会提到

这时候我们注册页面首先会进行是否存在注册的用户名的查询

没有查询到结果,所以成功绕过了查询,这里就可以解释一下为什么要在末尾加1,其实1是随便加的,任何字母数字都可以,原因是mysql进行select查询时,会忽略末尾的若干个空格,例如我们执行sql语句:”select username,password from users where username=’admin ‘ “

结果跟没有加空格是一样的,那么我们要绕过是否存在用户名的查询,如果只在末尾加空格,是会被检测到存在admin用户,所以我们在末尾加个任意字符,就可以绕过这个查询

那么为什么空格要在10个以上呢,我们继续看下去

下面注册页面执行的是insert语句

可见成功的注册了

我们再来查询一下”select username,password from users where username=’admin’ “

这时候发现有两个admin用户,但是这两个用户其实是不一样的,因为我们之前说过了,mysql在select查询时会将末尾空格忽略,而mysql在insert操作时不会忽略空格,也就是说我们以攻击者身份注册的用户名’admin 1’中的空格不会被省略,这里肯定就有疑问,那么1跑哪里去了,答案很简单,我们之前就已经限制了username的最大长度为15,这里空格在10个以上,加上admin的5个长度,所以长度在15以上的字符就被截断了,之前的才会被写入数据库,那么实际上写入数据库的就是admin+10个空格,但是我们登录查询select语句会自动忽略空格,所以将后面没有空格的admin和加了10个空格的admin都查询了出来

也就是说,我们在登录页面输入用户名为admin,密码为aaa,就会有查询结果,也就以管理员身份登录成功了

当然我们查询到的并不是真正的admin用户,但是服务器只检测有没有查询结果,有则认为我们登录成功

那么防御手段也挺简单就能想到,我们只需要对查询的结果进行检查,检查查询到的用户密码是不是之前设置的管理员密码,就可以防御了

最后附上参考链接:https://www.cnblogs.com/ECJTUACM-873284962/p/8977983.html