Loading... <div class="tip share">请注意,本文编写于 2102 天前,最后修改于 2030 天前,其中某些信息可能已经过时。</div> 题目:[Bugku CTF(Web)——login1](https://ctf.bugku.com/challenges#login1(SKCTF)) [writeup](https://blog.domineto.top/CTF/Bugku.html#login1) 第一次遇到这种基于约束的SQL攻击,在此记录一下吧。 > 参考文章: > [WangEX's Blog](https://goodwaf.com/2016/12/30/%E5%9F%BA%E4%BA%8E%E7%BA%A6%E6%9D%9F%E6%9D%A1%E4%BB%B6%E7%9A%84SQL%E6%94%BB%E5%87%BB/) > [Dhaval Kapil](https://dhavalkapil.com/blogs/SQL-Attack-Constraint-Based/) ##1. 知识储备 ###1.1数据库字符串比较: 在数据库对字符串进行比较时,如果两个字符串的长度不一样,则会将较短的字符串末尾填充空格,使两个字符串的长度一致,比如,字符串A:`String`和字符串B:`String2`进行比较时,由于`String2`比`String`多了一个字符串,这时MySQL会将字符串A填充为`String `,即在原来字符串后面加了一个空格,使两个字符串长度一致。看如下两条查询语句: ```sql select * from users where username='admin' select * from users where username='admin ' ``` 它们的查询结果是一致的,即第二条查询语句中admin后面的空格并没有对查询有任何影响。因为在MySQL把查询语句里的username和数据库里的username值进行比较时,它们就是一个字符串的比较操作,符合上述特征。 ###1.2INSERT截断: 这是数据库的另一个特性,当设计一个字段时,我们都必须对其设定一个最大长度,比如CHAR(10),VARCHAR(20)等等。但是当实际插入数据的长度超过限制时,数据库就会将其进行截断,只保留限定的长度。 ##2. 漏洞复现 这里我搬运[一篇博客](https://goodwaf.com/2016/12/30/%E5%9F%BA%E4%BA%8E%E7%BA%A6%E6%9D%9F%E6%9D%A1%E4%BB%B6%E7%9A%84SQL%E6%94%BB%E5%87%BB/)里面的场景吧。如果想看具体的题目可以到开头的题目那里去看看。 ------------ 我们把利用场景设在用户登陆的地方,假如有用户[Dumb],我们想要使用他的账号登陆,但是我们又不知道他的密码,那么我们可以注册一个名字叫[Dumb done]的用户,即在目标用户名的后面加一串空格(注意:空格后需再跟一个或多个任意字符,防止程序在检查用户名是否已存在时匹配到目标用户),空格的长度要超过数据库字段限制的长度,让其强制截断。 当我们注册该用户名后,由于截断的问题,此时我们的用户名就为:[Dumb ],即除了后面的一串空格,我们的用户名和目标用户名一样。 假如服务端的用户登陆代码为: ```php <?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注入的角度看,这段代码是不能注入的,但是当我们以目标用户名Dumb和我们自己注册用户的密码进行登陆时就可以绕过认证。当我们以用户名:[Dumb]和密码:[123456](假设)登陆时,对应的SQL语句就为: ```sql SELECT username FROM users WHERE username='Dumb' AND password='123456' ``` 当执行这条语句后,数据库将返回我们自己注册的账户信息,但是注意此处的return $username,虽然此时查询出来的是我们自己的用户信息,但是返回的用户名则是目标的用户名。如果此后的业务逻辑直接以该用户名为准,则我们就达到了水平越权的目的。 ##3. 限制条件 1. 服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就不能导致数据库截断,也就没有利用条件。 2. 登陆验证的SQL语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码,然后再比对密码的话,那么也不能进行利用。因为当使用Dumb为用户名来查询密码的话,数据库此时就会返回两条记录,而一般取第一条则是目标用户的记录,那么你传输的密码肯定是和目标用户密码匹配不上的。 3. 验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户Dumb和密码123456登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是[Dumb ],如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。 ##4. 随便写写 SQL约束攻击还是老问题,对用户输入检查不严格。我觉得应用的场景应该不是很多,毕竟空白符一般都是不给输入的。(本人能力有限,如有错误,欢迎指教。) 最后修改:2019 年 07 月 03 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏