JS前端逆向

前言

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,且是注释,肯定不是这里,接着寻找,后来到

整体代码如下

go 复制代码
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进行爆破

go 复制代码
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 key
    global iv
    cipher = 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)
null

数据长度明显与错误时不一致,不过这里也未成功进入后台,有二次验证,Google验证码无从下手,故点到为止。

某道js逆向

null

接下来进行抓包

这里我们首先注意一下每次不同点在哪,以此为入口点来进行下去,因此我们多次刷新界面抓包,同样的参数观察包的参数哪个值是不同的

从上图可以看出sign和mysticTime是变化的,因此接下来针对这两个变量进行深入,如果我们能够控制这两个变量,那么我们就可以实现直接脚本请求得到翻译对应的语句。

所以接下来首先从sign开始,我们首先进行F12,而后输入ctrl+shift+f全局搜索关键词

这里可以发现出现了js中含有sign关键字的,但像这个inpage.js他明显不是我们要找的js语句,因此继续往下寻找(输入sign:更容易找到对应函数)。这里我们找到如下语句

相关代码如下

go 复制代码
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函数进行相关了解

go 复制代码
function j(e) {
                return c.a.createHash("md5").update(e.toString()).digest("hex")
            }

明显的md5加密,因此到这里也就都清楚了。

当我们进行请求时,首先获取当前的时间戳,此作为参数之一,同时与client等参数值组合,进行md5加密,就组成了sign的值。对于mysticTime这个参数,我们从k函数也了解到它其实就是时间戳,因此两个变化的参数到目前就都了解其生成过程了。

接下来尝试写python脚本

go 复制代码
import hashlib
import time
import requests

requests.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)
null

此时便得到了加密数据,解密同理,不再阐述。

参考

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元不等的稿酬。

更多详情,点我查看!

参与靶场实战,戳"阅读原文"

相关推荐
lovingsoft5 分钟前
Chrome Lighthouse 详解
前端·chrome
Qlly15 分钟前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝19 分钟前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦19 分钟前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
释厄62333 分钟前
错误无误公理——错误正确本同源都迭代定律——逆转有价值
android·开发语言·kotlin
shmily麻瓜小菜鸡40 分钟前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
2601_9628857244 分钟前
如何用 Python计算 CR能量指标(中间意愿指标)?
开发语言·python
打工仔折腾 AI1 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战
汉堡大王95271 小时前
单次输出拉到100万Token:拆完 Gemini 4 Argon 的续写协议,才看懂谷歌这7个月在憋什么
前端·人工智能·后端
看浪的路人1 小时前
第9讲:大模型应用监控与可观测性——OpenTelemetry + Prometheus + Grafana 全链路追踪
java·前端·spring