JS前端逆向

news2025/3/9 22:29:33

前言

js逆向一直没有相关了解,虽然目前渗透遇见的不是很多,大多数遇见的要么不加密,要么无法实现其加密流程,不过最近看到了一个较为简单的站点正好能够逆向出来,就做了简单记录。本文旨在介绍js逆向的一些基础思路,希望能对初学js前端逆向的师傅有所帮助。

JS定位

在我们寻找JS源代码时,如果直接翻看全部的js文件以来寻找自己想要的一部分,无疑是复杂繁琐的,且工作量巨大,有点类似大海捞针,因此这里我们需要借助一些巧妙的办法来快速定位某标签的js语句,具体方法如下。

元素审查定位

当我们不确定某处的js文件位置时,可以使用F12,点击元素审查,然后点击登录处,观察事件监听器

10202fd1ce4033a39838b620bb38bac2.png

此时可以观察到login.js文件出现,接下来就可以去对应文件下继续深入。

74119dd63c55951febc0e3399f86444f.png

发现check函数,寻找check函数

9f6d9ba1351ea58a5823c7d1f1c206b8.png

此时发现加密是secret函数,再继续跟secert函数就可以了解其整体流程。

全局搜索法

像我们常见的登录框,他们要提交的加密参数一般名为password,或者加密为Crypto加密,因此我们可以全局搜索此类关键字,进而寻找我们需要找的关键加密js语句,进而实现js逆向。

具体操作也很简单,这里简单举个例子。

首先打开F12,随便点击一个元素,而后ctrl+shift+f,接下来全局搜索关键词即可

45d0a444f5e12f848905dd3301a95213.png

此时含关键词的语句映入眼帘,像一些css文件中的直接略过即可,而后即可找到真正生成密码的地方

151dd77eec31acb0e008540125f57d10.png

接下来便可以深入secret,了解加密方法。

Onclick定位

像一些登录点是存在着onclick属性的,如若该属性值是js函数,那么就极有可能是我们要寻找的js加密函数,而后进行寻找相关函数即可。

注:图参考自cony1大师傅。

cony1大师傅的图为例进行简单讲解

43137187722edea767a4c448f5942040.png

这里发现ssologin函数,接下来寻找该函数

2603c101be228feea7c8702fb38170fa.png

此时即可发现相关js语句。

实战

某登录站点js逆向

找到一个登录站点,随意输入

392d9da37dae1ede6c9c848bce914845.png

发现用户名和密码均被加密,接下来ctrl+shift+f,全局搜索password字段,寻找加密点

fc0a91102d78f4f48a0475c00403db5b.png

第一个这里明显是输入框的password,且是注释,肯定不是这里,接着寻找,后来到

6e6e09fd2e6d4089babf45b03ceb2110.png

整体代码如下

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函数就是加密函数,所以我们接下来跟进此加密函数

c738e3a6d1cbaea439fd4747364e7b5f.png

这里直接给出了iv和key,所以接下来打断点调试就行了,而后打上断点

941554c39f95053a7d95353a3a6a7bc7.png

接下来开始随便输入密码提交,而后来到调试界面

选中code.substring(16)得到keyf3991777154f4bd0

58af0c7f0c1188bb2f53c26939eae479.png

选中code.substring(0,16)得到偏移量ace43e65106a77f6

b34b18f9b01a10ffa8aae2e585e3d8ee.png

下方也给出了Padding和mode分别是Pkcs7CBC,所以接下来直接解密即可,在网络中我们可以看到提交后加密的账密

d67c7a9ec9e2e3823a8d2758a662e330.png

拿去随便找个AES解密网站

290c95e35b0b0e8de4e62769c9849615.png

与所输入的进行比对

4c3feb9c997b7bca58832bda7c38eaa3.png

成功得到正确结果

接下来编写脚本即可,直接将字典的内容全部进行加密,而后放入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 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)
1e323a571b76bce12d63dc41b4d89623.png

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

某道js逆向

6c8164d5eeb6e58f4ee5da52e54dfcd7.png

接下来进行抓包

c7721dae66c6d0f23afab4bc342a11b3.png

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

e44f404943e3439eef5e5d5128a92530.png

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

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

45f12809760686e8e6654da837d96477.png

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

2fc46537484f7ebd1f442c45f9960288.png

相关代码如下

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的值即可

3ee3e50500e00b38a8ed4e9ceac80d48.png

经观察,这里的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 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)
564ab829c4fb065b119336f50f7caaf5.png

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

参考

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

更多详情,点我查看!

51fd1562f20f78f151401108996ff4af.gif

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1280762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超硬核解析Mybatis动态代理原理!只有接口没实现也能跑?

文章目录 前言Mybatis dao层两种实现方式的对比原始Dao开发原始Dao开发的弊端 基于Mapper动态代理的开发方式 Mybatis动态代理实现方式的原理解析动态代理调用链路解析先给出链路调用结果1、调用方法的开始:session.getMapper2、DeaultSqlSession的getMapper3、Conf…

JavaWeb 带条件的分页查询

最终效果图 注意:没有带条件的时候 默认的是第一页数据 条件是组合的 sql->sql的动态变换 注意第二次查询的时候回显问题 就是填完条件后显示完当页数据ok 但是我点击第二页的时候条件还存在着 此时ListSerevlet不仅要拿到页码 页容量 还要拿到三个条件参数 封装…

Linux网络之连接跟踪 conntrack

一 Linux网络之连接跟踪 conntrack k8s 有关conntrack的分析 ① 什么是连接跟踪 netfilter连接跟踪 conntrack 详述 思考:连接跟踪模块会对哪些协议进行跟踪?TCP、UDP、ICMP、DCCP、SCTP、GRE ② 为什么需要连接跟踪 没有连接跟踪有很多问题是不好解决的&a…

掌握视频剪辑技巧:批量置入视频封面,提升视频品质

在当今数字化时代,视频已成为生活的重要组成部分。无论是观看电影、电视剧、综艺节目,还是分享个人生活、工作成果,视频都以其独特的魅力吸引着大众的视线。视频封面是视频内容的缩影,是观众对视频的第一印象。一个好的封面能吸引…

分享一个简单的基于C语言嵌入式GUI界面切换代码

目录 前言 一、数据类型 二、页面调度 三、页面显示 四、视频展示 前言 最近在用LVGL写一个简单的UI界面,需要进行几个页面的切换,所以就自己写了一个简单页面切换代码,方便进行页面切换,同时使UI代码结构更加清晰。这个结构…

基于Java SSM框架实现实现四六级英语报名系统项目【项目源码+论文说明】

基于java的SSM框架实现四六级英语报名系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个高校四六级报名管理系统,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作…

什么是跨站脚本攻击

跨站脚本攻击 1. 定义2. 跨站脚本攻击如何工作3. 跨站脚本攻击类型4. 如何防止跨站脚本攻击 1. 定义 跨站脚本攻击(Cross-site Scripting,通常称为XSS),是一种典型的Web程序漏洞利用攻击,在线论坛、博客、留言板等共享…

leetCode 51.皇后 + 回溯算法 + 图解 + 笔记

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包…

Python读取栅格遥感影像并加以辐射校正后导出为Excel的一列数据

本文介绍基于Python语言中的gdal模块,读取一景.tif格式的栅格遥感影像文件,提取其中每一个像元的像素数值,对像素值加以计算(辐射定标)后,再以一列数据的形式将计算后的各像元像素数据保存在一个.csv格式文…

【力扣206】反转链表

【力扣206】反转链表 一.题目描述 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1 : 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2 : 输入:head [1,2] 输出&#x…

机器学习---pySpark代码开发

1、eclipse开发pySpark程序 在eclipse中开发pySpark程序,需要安装pydev插件。 1).eclipse安装python插件,安装完成后重启。 2). 在window--->preferences中找到python interpreter配置安装python的路径: 3).新建python项目: 2、pyCharm开…

Centos图形化界面封装OpenStack Centos镜像

目录 背景 环境 宿主机环境安装 创建与安装Centos7.8虚机 虚机设置 安全相关 安装ACPI服务 安装cloud-init 安装cloud-utils-growpart 停⽌虚拟机 删除个性化信息 模板化与压缩 登录与验证 背景 今天早上在Centos官网下载的CentOS-7-aarch64-GenericCloud-2003.…

3dMax拼图生成工具Puzzle2D使用教程

Puzzle2D for 3dsMax拼图生成工具使用教程 Puzzle2D简介: 2D拼图随机生成器(英文:Puzzle2D) ,是一款由#沐风课堂#用MAXScript脚本语言开发的3dsMax建模小工具,可以随机创建2D可编辑样条线拼图图形。可批量…

解决vscode中html部分无法嵌套注释

不管是React项目还是Vue项目,相信你一定遇到过同样的问题,如果想要注释的结构内部也存在注释,那么编译器会报以下问题 使用 HTML-Comment 这个插件即可解决问题 选中需要注释的区域并根据系统输入快捷键,可以发现就算嵌套了注释…

使用Redis构建任务队列

文章目录 第1关:先进先出任务队列第2关:优先级任务队列第3关:定时任务队列 第1关:先进先出任务队列 编程要求 在Begin-End区域编写 add_task(task_name) 函数,实现将任务加入队列的功能,具体参数与要求如下…

C++基础 -35- string类

string类的格式 string a;如下图,使用string类比常规的字符串处理方便很多 而且需要进行的字符串处理,在类中都能完成 #include "iostream"using namespace std;extern "C" {#include "string.h" }int main() {//c的写…

Java基本数据类型详解

✨个人主页:全栈程序猿的CSDN博客 💨系列专栏:Java从入门到精通 ✌座右铭:编码如诗,Bug似流星,持续追求优雅的代码,解决问题如同星辰般自如 Java是一种强类型语言,数据类型在程序中起…

Prefix-Tuning 论文概述

Prefix-Tuning 论文概述 前缀调优:优化生成的连续提示前言摘要论文十问实验数据集模型实验结论摘要任务泛化性能 前缀调优:优化生成的连续提示 前言 大规模预训练语言模型(PLM)在下游自然语言生成任务中广泛采用fine-tuning的方法进行adaptation。但是f…

Java中各种数据类型之间的转换

低类型向高类型自动进行转换,高类型向低类型的准换会丢失数据,整数到字符类型的转换将获取对应编码的字符。 进行高精度向低精度的强制类型准换时,需要将想要转换成的数据类型加一个括号()。 如何完成自动转换呢? 转换前的数据类…

正是阶段高等数学复习--函数极限的计算

之前在预备阶段中函数极限的解决方式分三步,第一步观察形式并确定用什么方式来解决,第二步化简,化简方式一共有7种,分别是最重要的三种(等价替换、拆分极限存在的项、计算非零因子)以及次重要的4种&#xf…