五百丁登录enPassword参数
难度:★☆☆☆☆ 1星
一、目标
目标网站: https://www.500d.me/login/
登录的时候提交表单里密码字段enPassword是被加密的:
本次目标就是破解这个参数加密。
二、分析
打开登录页:
https://www.500d.me/login/
打开开发者工具,切换到Network,清空掉无关请求,然后页面里输入账号密码尝试登陆,注意账号密码是随便输的,是故意让它登陆失败观察一下流程的:
捕捉到了三个请求,先看下第一个Get请求,链接是:
https://www.500d.me/common/public_key/?_=1605590883257
响应内容:
01 02 03 04 | { "modulus" : "AM+emhhTb5EOH/ZbDg78dHOw79H4aFQkF4pCFCw9yo8oRigsa0p6bIB3UVjK+S5E1v3OSy1+/4WM10Zb+k+qV/7hK0GuoO2w15s+0nYJLjPC4SO8WmFgNC5aQsdHOPXt9hcK6sbJKh4dWR/U/pyfTOlp1IJqx4ZALyAf5sZN25Np" , "exponent" : "AQAB" } |

好的继续看第二个Post请求:
https://www.500d.me/login/submit/
这个是实际提交登录参数的,因此提交了一个表单有用户名密码之类的参数:

第三个请求实际上是一个雪碧图,用于在页面上显示图标用的,这里不再详述。
通过观察请求大致捋出来了登录的流程,先是发送一个请求获取公钥,然后再用js加密密码提交登录表单,接下来的重点就在第二个请求发送前的js逻辑,接下就是想办法去定位到那段js代码,复制第二个请求的url,打一个xhr断点:

然后在页面上重新尝试登录,就卡在了断点这里,格式化代码,同时在调用栈里往前回溯寻找相关的栈帧:

在success方法的栈帧里看到了发出登录请求的代码,这个success是前面那个获取公钥的接口成功时的回调方法:

密码字段加密的核心逻辑:
01 02 03 | var rsaKey = new RSAKey(); rsaKey.setPublic(b64tohex(data.modulus), b64tohex(data.exponent)); var enPassword = hex2b64(rsaKey.encrypt(form.find( "input[name='password']" ).val())); |

然后定位到了一个叫做rsa.js的文件,把这个文件整个抠出来新建一个文件encrypt.js放进去:

然后回到加密的方法,如法炮制找到b64tohex和hex2b64的逻辑:


这两个都是base64.js文件中,同样跟进去,然后整个抠出来放到encrypt.js,然后在encrypt.js中尝试写一个加密密码的方法为外部提供调用的接口:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | /** * 向外界暴露加密密码的方法 * * @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" )); |

都扣完了还是报错:

然后补环境,把下面的代码插入到encrypt.js的最前面:
01 02 03 04 05 06 07 | // 补环境 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就好。
流程基本捋清楚了,接下来是编码实现。
三、编码实现
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #!/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" ) |
仓库:
请注意爬虫文章具有时效性,本文写于2020-11-17日。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2019-11-25 flash逆向练习:以逆向的方式通关flash游戏《谈判专家》