• 五百丁登录enPassword参数


    难度:★☆☆☆☆ 1星

    一、目标

    目标网站: https://www.500d.me/login/
    登录的时候提交表单里密码字段enPassword是被加密的:
    0
    本次目标就是破解这个参数加密。

    二、分析

    打开登录页:
    https://www.500d.me/login/
    打开开发者工具,切换到Network,清空掉无关请求,然后页面里输入账号密码尝试登陆,注意账号密码是随便输的,是故意让它登陆失败观察一下流程的:
    1
    捕捉到了三个请求,先看下第一个Get请求,链接是:
    https://www.500d.me/common/public_key/?_=1605590883257
    响应内容:

    {
        "modulus": "AM+emhhTb5EOH/ZbDg78dHOw79H4aFQkF4pCFCw9yo8oRigsa0p6bIB3UVjK+S5E1v3OSy1+/4WM10Zb+k+qV/7hK0GuoO2w15s+0nYJLjPC4SO8WmFgNC5aQsdHOPXt9hcK6sbJKh4dWR/U/pyfTOlp1IJqx4ZALyAf5sZN25Np",
        "exponent": "AQAB"
    }
    
    乍一看有点懵逼,其实如果之前搞过类似的话看一眼url里面的public_key和响应内容,大概就知道这应该是就是获取加密密码时使用的公钥了,密码也可能就是rsa加密的,我们借助ModHeader来测试一下这个请求是否对cookie或者referer做了检查,可以看到不带cookie和referer也是可以访问的:
    2
    好的继续看第二个Post请求:
    https://www.500d.me/login/submit/
    这个是实际提交登录参数的,因此提交了一个表单有用户名密码之类的参数:
    3
    第三个请求实际上是一个雪碧图,用于在页面上显示图标用的,这里不再详述。
    通过观察请求大致捋出来了登录的流程,先是发送一个请求获取公钥,然后再用js加密密码提交登录表单,接下来的重点就在第二个请求发送前的js逻辑,接下就是想办法去定位到那段js代码,复制第二个请求的url,打一个xhr断点:
    4
    然后在页面上重新尝试登录,就卡在了断点这里,格式化代码,同时在调用栈里往前回溯寻找相关的栈帧:
    5
    在success方法的栈帧里看到了发出登录请求的代码,这个success是前面那个获取公钥的接口成功时的回调方法:
    6
    密码字段加密的核心逻辑:
    var rsaKey = new RSAKey();
    rsaKey.setPublic(b64tohex(data.modulus), b64tohex(data.exponent));
    var enPassword = hex2b64(rsaKey.encrypt(form.find("input[name='password']").val()));
    
    然后把鼠标放到RSAKey上悬停一会儿,会弹出弹窗表明出处,单击跟进入:
    7
    然后定位到了一个叫做rsa.js的文件,把这个文件整个抠出来新建一个文件encrypt.js放进去:
    8
    然后回到加密的方法,如法炮制找到b64tohex和hex2b64的逻辑:
    9
    10
    这两个都是base64.js文件中,同样跟进去,然后整个抠出来放到encrypt.js,然后在encrypt.js中尝试写一个加密密码的方法为外部提供调用的接口:
    /**
     * 向外界暴露加密密码的方法
     *
     * @param passwd
     * @param modulus
     * @param exponent
     * @returns {string|*}
     */
    function encryptPasswd(passwd, modulus, exponent) {
        const rsaKey = new RSAKey();
        rsaKey.setPublic(b64tohex(modulus), b64tohex(exponent));
        return hex2b64(rsaKey.encrypt(passwd));
    }
    
    console.log(encryptPasswd("cc11001100", "{
    " +
        "    "modulus": "AJbFLrvha10BPOdevQ+cuIDirMylI9srBg3MQe/3jG3FovKT3+/hSHPZbJljaOHnLHskJh1+r8ECwpJEU16xA73D+SbCcK83my+vMH2VLdP9w6eRfqkEfo+W/5yn7ZmNAnGTPlTC29I3b8cyVEuUHHO8HQGpgJXJp7FDbTjxgj6R"
    " +
        "}", "AQAB"));
    
    同时node运行测试一下是否OK,然后发现报错了,因为扣的js不全,有些依赖没有放到encrypt.js中,回到登录页面,查看源代码,搜索rsa定位到这里,把红色方框内没有扣的js一股脑儿扣了放到encrypt.js中:
    11
    都扣完了还是报错:
    12
    然后补环境,把下面的代码插入到encrypt.js的最前面:
    // 补环境
    const window = {
        navigator: {
            appName: "Netscape"
        }
    };
    const navigator = window.navigator;
    
    然后再运行一下,能够加密出密码了:
    13
    把893行的测试代码注释掉,拷到pycharm里,接下来会在python中调用这个js加密密码。
    这里还有一个需要注意的问题,就是在登录的时候提交登录表单的接口必须带上token请求头,如果不带的话就会405(方法不被允许: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/405 ),使用ModHeader手动搞掉token试下:
    14
    可以看到405了:
    15
    这个token是用jQuery的ajaxSend方法设置的一个hook里设置到请求头上的,jQuery的ajaxSend可以设置一个函数在ajax发送之前运行,通过正则搜索“header.{0,100}token”,表示搜索出现在header附近的token,因为js设置的话有可能就是这样设置上的:
    16
    于是就定位到了这个文件:
    https://static.500d.me/resources/500d/js/utils.js?v=V7119
    文件的这个位置,就是在这里设置的请求头:
    17
    这个名为token的cookie是在第一次访问登录页的时候设置的cookie:
    18
    访问一次登录页拿到这个token就好。
    流程基本捋清楚了,接下来是编码实现。

    三、编码实现

    #!/usr/bin/env python3
    # encoding: utf-8
    """
    @author: CC11001100
    """
    import functools
    import time
    
    import execjs
    import requests
    
    session = requests.session()
    
    
    def login(username, passwd):
        token = get_token()
        print(f"token = {token}")
    
        public_key = get_public_key()
        print(f"public key = {public_key}")
    
        data = {
            "username": username,
            "enPassword": load_js_context().call("encryptPasswd", passwd, public_key["modulus"], public_key["exponent"]),
            "service": "",
            "remember": True
        }
        print(data)
    
        headers = {
            # 这个参数是必须要带的,什么鬼情况,是后端的框架要检测还是手动做的检测,好像有些框架需要这个参数知道是个xhr请求
            "X-Requested-With": "XMLHttpRequest",
            # U-A反倒不是必须的...
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
            # token是必须要带的
            "token": token,
        }
    
        url = "https://www.500d.me/login/submit/"
        r = session.post(url, data=data, headers=headers)
        print(r.status_code)  # 200
        print(r.text)  # {"type":"success","content":""}
    
    
    @functools.lru_cache(maxsize=1)
    def load_js_context():
        with open("./encrypt.js", encoding="UTF-8") as f:
            js_code = f.read()
            return execjs.compile(js_code)
    
    
    def get_token():
        url = "https://www.500d.me/login/"
        return session.get(url).cookies["token"]
    
    
    def get_public_key():
        url = f"https://www.500d.me/common/public_key/?_={int(time.time() * 1000)}"
        return session.get(url).json()
    
    
    if __name__ == "__main__":
        # 注册资料:
        # 邮箱: korowof384@rvemold.com
        # 昵称: cc11001100_test
        # 密码: ccIs0KccIs0K
        login("korowof384@rvemold.com", "ccIs0KccIs0K")
    


    仓库:

    https://github.com/CC11001100/misc-crawler-public/tree/master/001-anti-crawler-js-re/01-005-www.500d.me


    请注意爬虫文章具有时效性,本文写于2020-11-17日。

  • 相关阅读:
    tflearn alexnet iter 10
    自然语言处理中的Attention Model:是什么及为什么
    深度学习的seq2seq模型——本质是LSTM,训练过程是使得所有样本的p(y1,...,yT‘|x1,...,xT)概率之和最大
    java 提取主域名
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(11月14日~11月20日)
    2个月女婴注射疫苗后死亡?启示!!
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(11月9日~11月13日)
    北京Uber优步司机奖励政策(11月16日~11月22日)
    【独家:震惊!——西城区所有学区优质度透解与大排名,泄密了!】
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(10月31日~11月6日)
  • 原文地址:https://www.cnblogs.com/cc11001100/p/14038179.html
Copyright © 2020-2023  润新知