前言
js逆向一直没有相关了解,虽然目前渗透遇见的不是很多,大多数遇见的要么不加密,要么无法实现其加密流程,不过最近看到了一个较为简单的站点正好能够逆向出来,就做了简单记录。本文旨在介绍js逆向的一些基础思路,希望能对初学js前端逆向的师傅有所帮助。
JS定位
在我们寻找JS源代码时,如果直接翻看全部的js文件以来寻找自己想要的一部分,无疑是复杂繁琐的,且工作量巨大,有点类似大海捞针,因此这里我们需要借助一些巧妙的办法来快速定位某标签的js语句,具体方法如下。
元素审查定位
当我们不确定某处的js文件位置时,可以使用F12,点击元素审查,然后点击登录处,观察事件监听器
此时可以观察到login.js
文件出现,接下来就可以去对应文件下继续深入。
发现check函数,寻找check函数
此时发现加密是secret
函数,再继续跟secert函数就可以了解其整体流程。
全局搜索法
像我们常见的登录框,他们要提交的加密参数一般名为password
,或者加密为Crypto
加密,因此我们可以全局搜索此类关键字,进而寻找我们需要找的关键加密js语句,进而实现js逆向。
具体操作也很简单,这里简单举个例子。
首先打开F12,随便点击一个元素,而后ctrl+shift+f
,接下来全局搜索关键词即可
此时含关键词的语句映入眼帘,像一些css文件中的直接略过即可,而后即可找到真正生成密码的地方
接下来便可以深入secret
,了解加密方法。
Onclick定位
像一些登录点是存在着onclick
属性的,如若该属性值是js函数,那么就极有可能是我们要寻找的js加密函数,而后进行寻找相关函数即可。
注:图参考自cony1
大师傅。
以cony1
大师傅的图为例进行简单讲解
这里发现ssologin
函数,接下来寻找该函数
此时即可发现相关js语句。
实战
某登录站点js逆向
找到一个登录站点,随意输入
发现用户名和密码均被加密,接下来ctrl+shift+f,全局搜索password字段,寻找加密点
第一个这里明显是输入框的password,且是注释,肯定不是这里,接着寻找,后来到
整体代码如下
function check() {//这里将用户名,密码加密var code = 'letu@levle';var yname = $("#yname").val();if (yname == '') {alert("用户名不能为空");return false;} else {var newName = secret(yname, code, false);$("#xname").val(newName);}var ypassword = $("#ypassword").val();if (ypassword == '') {alert("密码不能为空");return false;} else {var newPassword = secret(ypassword, code, false);$("#xpassword").val(newPassword);}}
可以看出js代码逻辑并不难,首先提取出ypassword
标签下的内容,而后验证其是否为空,若不为空,则对其进行secret函数处理,很明显,这个secret函数就是加密函数,所以我们接下来跟进此加密函数
这里直接给出了iv和key,所以接下来打断点调试就行了,而后打上断点
接下来开始随便输入密码提交,而后来到调试界面
选中code.substring(16)
得到keyf3991777154f4bd0
选中code.substring(0,16)
得到偏移量ace43e65106a77f6
下方也给出了Padding和mode分别是Pkcs7
和CBC
,所以接下来直接解密即可,在网络中我们可以看到提交后加密的账密
拿去随便找个AES解密网站
与所输入的进行比对
成功得到正确结果
接下来编写脚本即可,直接将字典的内容全部进行加密,而后放入burp进行爆破
import base64
from Crypto.Cipher import AES
from Crypto.Hash import MD5
from Crypto.Util.Padding import pad#填入AES的key和iv
key = 'f3991777154f4bd0'
iv = 'ace43e65106a77f6'def AES_Encrypt(data):global keyglobal ivcipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))paddingdata = pad(data.encode('utf-8'),AES.block_size)encrypted = cipher.encrypt(paddingdata)#print(base64.b64encode(encrypted).decode())return base64.b64encode(encrypted).decode()password = []
with open('password.txt','r',encoding='utf-8') as f:for i in f:password.append(i.strip())
with open('password_aes.txt','w',encoding='utf-8') as w:for i in password:data = AES_Encrypt(i)+'\n'w.write(data)
数据长度明显与错误时不一致,不过这里也未成功进入后台,有二次验证,Google验证码无从下手,故点到为止。
某道js逆向
接下来进行抓包
这里我们首先注意一下每次不同点在哪,以此为入口点来进行下去,因此我们多次刷新界面抓包,同样的参数观察包的参数哪个值是不同的
从上图可以看出sign
和mysticTime
是变化的,因此接下来针对这两个变量进行深入,如果我们能够控制这两个变量,那么我们就可以实现直接脚本请求得到翻译对应的语句。
所以接下来首先从sign
开始,我们首先进行F12,而后输入ctrl+shift+f
全局搜索关键词
这里可以发现出现了js中含有sign
关键字的,但像这个inpage.js
他明显不是我们要找的js语句,因此继续往下寻找(输入sign:
更容易找到对应函数)。这里我们找到如下语句
相关代码如下
const u = "fanyideskweb", d = "webfanyi", m = "client,mysticTime,product", p = "1.0.0", g = "web", b = "fanyi.web", A = 1, h = 1, f = 1, v = "wifi", O = 0;function y(e) {return c.a.createHash("md5").update(e).digest()}function j(e) {return c.a.createHash("md5").update(e.toString()).digest("hex")}function k(e, t) {return j(`client=${u}&mysticTime=${e}&product=${d}&key=${t}`)}function E(e, t) {const o = (new Date).getTime();return {sign: k(o, e),client: u,product: d,appVersion: p,vendor: g,pointParam: m,mysticTime: o,keyfrom: b,mid: A,screen: h,model: f,network: v,abtest: O,yduuid: t || "abcdefg"}}
这里可以看到sign
是由函数k
构成的,同时注意到这里也给出了k的参数,k是由client=fanyideskweb&mysticTime=${e}&product=webfanyi&key=${t}
所组成的,此时再看函数E,o是时间戳,e这里未知,这时候该怎么办呢,先看看他是不是固定值,当自己不确定在哪下断点调试时,就在附近的几个可疑点都打下断点,观察e的值即可
经观察,这里的e值是固定的,即fsdsogkndfokasodnaso
,此时k(o,e)
中的参数我们都了解了,但我们注意到k函数中是有j
在外包裹的,因此我们需要对j函数进行相关了解
function j(e) {return c.a.createHash("md5").update(e.toString()).digest("hex")}
明显的md5加密,因此到这里也就都清楚了。
当我们进行请求时,首先获取当前的时间戳,此作为参数之一,同时与client等参数值组合,进行md5加密,就组成了sign的值。对于mysticTime
这个参数,我们从k
函数也了解到它其实就是时间戳,因此两个变化的参数到目前就都了解其生成过程了。
接下来尝试写python脚本
import hashlib
import time
import requestsrequests.packages.urllib3.disable_warnings()
headers = {"Content-Length": "312",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"Sec-Ch-Ua": "\"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"",
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/x-www-form-urlencoded",
"Sec-Ch-Ua-Mobile":"?0",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Sec-Ch-Ua-Platform": "\"Windows\"",
"Origin": "https://fanyi.youdao.com",
"Sec-Fetch-Site": "same-site",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Referer": "https://fanyi.youdao.com/",
"Accept-Encoding": "gzip, deflate",
}
Cookie = {
"OUTFOX_SEARCH_USER_ID":"239978291@10.130.108.41",
"OUTFOX_SEARCH_USER_ID_NCOO":"520521807.43848985"
}
url = ""
word = input("请输入翻译内容:")
localtime = str(int(time.time() * 1000))
canshu = "client=fanyideskweb&mysticTime={}&product=webfanyi&key=fsdsogkndfokasodnaso".format(localtime)
sign = hashlib.md5(canshu.encode(encoding='utf8')).hexdigest()
data = {"i": f"{word}","from": "auto","to": "","dictResult": "true","keyid": "webfanyi","sign": sign,"client": "fanyideskweb","product": "webfanyi","appVersion": "1.0.0","vendor": "web","pointParam": "client,mysticTime,product","mysticTime": localtime,"keyfrom": "fanyi.web"
}
res = requests.post(url=url,headers=headers,cookies=Cookie,data=data,verify=False)
print(res.text)
此时便得到了加密数据,解密同理,不再阐述。
参考
https://www.freebuf.com/articles/web/381666.html
https://gv7.me/articles/2018/fast-locate-the-front-end-encryption-method/
https://www.cnblogs.com/wxd501/p/17070184.html
原创稿件征集
征集原创技术文章中,欢迎投递
投稿邮箱:edu@antvsion.com
文章类型:黑客极客技术、信息安全热点安全研究分析等安全相关
通过审核并发布能收获200-800元不等的稿酬。
更多详情,点我查看!
参与靶场实战,戳"阅读原文"