难度:★☆☆☆☆ 1星
一、目标目标网站: https://www.500d.me/login/
登录的时候提交表单里密码字段enPassword是被加密的:
本次目标就是破解这个参数加密。
打开登录页:
https://www.500d.me/login/
打开开发者工具,切换到Network,清空掉无关请求,然后页面里输入账号密码尝试登陆,注意账号密码是随便输的,是故意让它登陆失败观察一下流程的:
捕捉到了三个请求,先看下第一个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也是可以访问的:
好的继续看第二个Post请求:
https://www.500d.me/login/submit/
这个是实际提交登录参数的,因此提交了一个表单有用户名密码之类的参数:
第三个请求实际上是一个雪碧图,用于在页面上显示图标用的,这里不再详述。
通过观察请求大致捋出来了登录的流程,先是发送一个请求获取公钥,然后再用js加密密码提交登录表单,接下来的重点就在第二个请求发送前的js逻辑,接下就是想办法去定位到那段js代码,复制第二个请求的url,打一个xhr断点:
然后在页面上重新尝试登录,就卡在了断点这里,格式化代码,同时在调用栈里往前回溯寻找相关的栈帧:
在success方法的栈帧里看到了发出登录请求的代码,这个success是前面那个获取公钥的接口成功时的回调方法:
密码字段加密的核心逻辑:
var rsaKey = new RSAKey(); rsaKey.setPublic(b64tohex(data.modulus), b64tohex(data.exponent)); var enPassword = hex2b64(rsaKey.encrypt(form.find("input[name='password']").val()));然后把鼠标放到RSAKey上悬停一会儿,会弹出弹窗表明出处,单击跟进入:
然后定位到了一个叫做rsa.js的文件,把这个文件整个抠出来新建一个文件encrypt.js放进去:
然后回到加密的方法,如法炮制找到b64tohex和hex2b64的逻辑:
这两个都是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", "{\n" + " \"modulus\": \"AJbFLrvha10BPOdevQ+cuIDirMylI9srBg3MQe/3jG3FovKT3+/hSHPZbJljaOHnLHskJh1+r8ECwpJEU16xA73D+SbCcK83my+vMH2VLdP9w6eRfqkEfo+W/5yn7ZmNAnGTPlTC29I3b8cyVEuUHHO8HQGpgJXJp7FDbTjxgj6R\"\n" + "}", "AQAB"));同时node运行测试一下是否OK,然后发现报错了,因为扣的js不全,有些依赖没有放到encrypt.js中,回到登录页面,查看源代码,搜索rsa定位到这里,把红色方框内没有扣的js一股脑儿扣了放到encrypt.js中:
都扣完了还是报错:
然后补环境,把下面的代码插入到encrypt.js的最前面:
// 补环境 const window = { navigator: { appName: "Netscape" } }; const navigator = window.navigator;然后再运行一下,能够加密出密码了:
把893行的测试代码注释掉,拷到pycharm里,接下来会在python中调用这个js加密密码。
这里还有一个需要注意的问题,就是在登录的时候提交登录表单的接口必须带上token请求头,如果不带的话就会405(方法不被允许: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/405 ),使用ModHeader手动搞掉token试下:
可以看到405了:
这个token是用jQuery的ajaxSend方法设置的一个hook里设置到请求头上的,jQuery的ajaxSend可以设置一个函数在ajax发送之前运行,通过正则搜索“header.{0,100}token”,表示搜索出现在header附近的token,因为js设置的话有可能就是这样设置上的:
于是就定位到了这个文件:
https://static.500d.me/resources/500d/js/utils.js?v=V7119
文件的这个位置,就是在这里设置的请求头:
这个名为token的cookie是在第一次访问登录页的时候设置的cookie:
访问一次登录页拿到这个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日。