在做bugku的web题时发现一道sql约束的题,果断学习了一下sql约束攻击的知识,以下部分文字及代码引用网址:https://www.freebuf.com/articles/web/124537.html。
后台在接收到用户名与密码后使用了php的函数mysql_real_escape_string()处理,并使用单引号包裹。验证用户名与密码的代码如下:
<?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']; } }按理说上述代码不会产生SQL注入等漏洞的啊,下面介绍几种关键知识:
在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。例如如下代码:
SELECT userId from user where username = 'test '上述代码和username = 'test'结果是一样的。但也存在异常情况,最好的例子就是LIKE子句了。注意,对尾部空白符的这种修剪操作,主要是在“字符串比较”期间进行的。这是因为,SQL会在内部使用空格来填充字符串,以便在比较之前使其它们的长度保持一致。
在所有的INSERT查询中,SQL都会根据varchar(n)来限制字符串的最大长度。也就是说,如果字符串的长度大于“n”个字符的话,那么仅插入字符串的前“n”个字符。比如特定列的长度约束为“5”个字符,那么在插入字符串“testName”时,实际上只能插入字符串的前5个字符,即“testN”。
当注册时,后台一般先select一下用户名看看是否存在,然后在insert一下。示例代码如下:
<?php $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) { } else { $query = "INSERT INTO users(username, password) VALUES ('$username','$password')";这里注册时使用用户名+【大量空格】和随机密码注册即可完成攻击。
主要原理就是insert时候有varchar(n)的限制,大于n的时候会截取前n个存入。在数据库对字符串进行比较时,即select操作,如果两个字符串的长度不一样,则会将较短的字符串末尾填充空格,使两个字符串的长度一致。注册时select语句不会将"admin+[大量空格]11"删减到n位,所以不会被select查出与admin重复,不会返回数据,接下来就可以插入admin+[空格](截取)11和自定义密码了。
如果使用用户名“vampire”和密码“random_pass”登录的话,对比时是admin与admin+[大量空格],会将前面的admin添加空格与后面的长度相同在进行对比,那么返回的只能是我们自己注册的用户信息,而不会返回目标用户信息。SQL查询语句是一个and操作,如果密码不一样怎么会把目标用户的信息也返回回来?
当登陆时使用admin与自定义密码登陆,数据库将返回我们自己注册的账户信息,但是注意此处的return $username,虽然此时查询出来的是我们自己的用户信息,但是返回的用户名则是目标的用户名。如果此后的业务逻辑直接以该用户名为准,则我们就达到了水平越权的目的。
靶机地址:http://123.206.31.85:49163/
admin+[大量空格]11与随机密码住注册:
注册成功后用admin登陆:
用admin用户名登陆成功。
求助:这个CTF使用admin+一个空格也可以注册成功,求助这是为什么。字符串对比为什么相等了。
将要求或者预期具有唯一性的那些列加上UNIQUE约束,由于’username’列具有UNIQUE约束,所以不能插入另一条记录。将会检测到两个相同的字符串,并且INSERT查询将失败。
最好使用’id’作为数据库表的主键。并且数据应该通过程序中的id进行跟踪。
限制输入长度。