当前位置: 首页 > 后端技术 > Python

【JS逆向100例】如何用栈调试?一e网通AES加密解析

时间:2023-03-25 21:25:05 Python

关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶、JS/Android逆向工程等技术干货!声明本文所有内容仅供学习交流。抓拍内容、敏感网址、数据接口均已脱敏处理,严禁用于商业或非法用途。否则,由此产生的一切后果与作者无关。Infringement,pleasecontactmetodeleteimmediately!逆向目标目标:某e网通登录接口主页:aHR0cHM6Ly93ZWIuZXd0MzYwLmNvbS9yZWdpc3Rlci8jL2xvZ2lu接口:aHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA==逆向参数:RequestHeaders:sign:3976F10977FC65F9CB967AEF79E508BDRequestPayload:password:"A7428361DEF118911783F446A129FFCE"逆向过程抓包分析来到某e网通的登录页面,随便输入一个Loginwithaccountandpassword,capturepacketsandlocatethelogininterfaceasaHR0cHM6Ly9nYXRld2F5LmV3dDM2MC5jb20vYXBpL2F1dGhjZW50ZXIvdjIvb2F1dGgvbG9naW4vYWNjb3VudA==,intherequestheader,thereisasign,andinthePayload,thepasswordpasswordisencrypted.参数反向标志先看请求头的标志,直接尝试搜索,发现不是部分请求返回的数据,观察其他请求,可以发现也有标志,并且值为每个请求都不一样:通过可以初步判断这个值应该是JS生成的。全局搜索关键字sign:,可以分别在request.js和request.ts两个文件中看到疑似sign赋值的地方。接下来原理也很简单,给时间戳加上一串固定字符,用MD5加密,然后转为大写。Python实现:importtimeimporthashlibtimestamp=str(int(time.time()*1000))sign=hashlib.md5((timestamp+'bdc739ff2dcf').encode(encoding='utf-8')).hexdigest().upper()print(sign)passwordpassword为明文密码加密后得到的值。如果你尝试直接搜索,你会发现有很多很多的值。很难找到确切的值:你可以看到这个请求是一个XHR请求。这次我们使用XHR断点的方式来定位具体的加密位置。通过这个案例,我们来了解一下如何跟进调用栈,以及如何通过上下文定位到具体的加密位置。切换到Network选项卡,找到登录请求,将鼠标移动到Initiator选项卡下的JS,可以看到它的调用栈。如果站点的加密方式比较简单,没有太多的混乱,可以在调用栈中看到login,send,post,encrypt等关键词,这种情况直接点进去,更简单找到加密的地方,但是大多数站点混淆了函数名和变量名,就像这个案例,调用堆栈显示的函数带有单个或多个不规则字母,无法直接定位。这时候就需要从上一个函数往前慢慢查找。点击进入最后一个函数,也就是Y函数,它位于调用栈的最顶端,意思是在这个函数之后,浏览器会发送一个登录请求,密码加密过程已经处理完毕。在这个函数中下了一个断点,可以在右侧的CallStack中看到调用栈。从下往上展示了点击登录后依次调用的函数的执行过程:要找到具体的加密位置,我们需要一个一个往前查找,对每个函数进行分析。比如往前走到倒数第二个调用栈,也就是o函数。可以看到传入的params参数中包含加密的密码信息,也就是说加密操作必须在这个函数之前:按照这个思路,一步步跟进调用栈,可以看到在执行了一个匿名函数utils.ts,里面调用了一个passwordEncrypt函数,从函数名可以看出基本上就是密码加密函数:这里打个断点调试,传入的是明文密码,passwordEncrypt其实就是encode里面的O函数。调用的ts:跟进O函数,引用了crypto-js加密模块,很明显是AES加密,本地重写即可。本例中的加密比较简单,但是加密函数比较隐蔽,需要耐心跟进调用栈。如果直接搜索,结果太多,不容易定位到加密功能。在这种情况下,有人跟进了。功能之后,可以清楚的看到加密的地方,所以有些网站可能比较糊涂,根本看不出有加密的功能。在这种情况下,我们需要注意参数的变化。如果在这个调用栈中看到的是加密参数,而在之前的调用栈中看到的是明文参数,那么加密操作一定是在两个调用栈之间,埋下断点,仔细分析。完整代码GitHub关注K哥的爬虫,持续分享爬虫相关代码!欢迎加星!https://github.com/kgepachong/下面只是演示了部分关键代码,不能直接运行!完整代码仓库地址:https://github.com/kgepachong...JavaScript加密代码CryptoJS=require("crypto-js")constkey=CryptoJS.enc.Utf8.parse("20171109124536982017110912453698");constiv=CryptoJS.enc.Utf8.parse('2017110912453698');//十六进制数作为密钥偏移量functiongetEncryptedPassword(word){letsrcs=CryptoJS.enc.Utf8.parse(word);让encrypted=CryptoJS.AES.encrypt(srcs,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});returnencrypted.ciphertext.toString().toUpperCase();}//测试示例//console.log(getEncryptedPassword("123457"))Python登录码#!/usr/bin/envpython3#-*-coding:utf-8-*-importtimeimporthashlibimportexecjsimportrequestslogin_url='脱敏处理,完整代码关注github:https://github.com/kgepachong/crawler'session=requests.session()defget_sign():timestamp=str(int(time.time()*1000))sign=hashlib.md5((timestamp+'bdc739ff2dcf').encode(encoding='utf-8')).hexdigest().upper()returnsigndefget_encrypted_pa??rameter(password):withopen('ewt360_encrypt.js','r',encoding='utf-8')asf:ewt360_js=f.read()encrypted_pa??ssword=execjs.compile(ewt360_js).call('getEncryptedPassword',password)returnencrypted_pa??ssworddeflogin(sign,用户名,encrypted_pa??ssword):headers={'sign':sign,'timestamp':str(int(time.time()*1000)),'sec-ch-ua':'"Not;ABrand";v="99","GoogleChrome";v="91","Chromium";v="91"','User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,像Gecko)Chrome/91.0.4472.124Safari/537.36'}data={'autoLogin':True,'password':encrypted_pa??ssword,'platform':1,'userName':username}response=session.post(url=login_url,headers=headers,json=data)print(response.json())defmain():username=input('请输入登录账号:')password=input('请输入登录密码:')sign=get_sign()encrypted_pa??ssword=get_encrypted_pa??rameter(password)login(sign,username,encrypted_pa??ssword)if__name__=='__main__':main()