前端加密

前端加密

注:本文档基于 YAKit 自带靶场。

JavaScript 代码混淆

混淆的作用

代码混淆用于防止盗版、抵御爬虫以及保护加密/解密逻辑,增加逆向工程的难度。

混淆工具与技术

Webpacks

什么是 JS 打包器:就是将前端开发写的多个 JS 文件,打包成一个 JS 文件,便于通过 HTTP 传输。

概念:Webpack 是现代 JavaScript 应用程序的静态模块打包工具。它将前端项目中的 JavaScript、CSS、图片、字体等资源视为模块,通过依赖分析将它们打包成一个或多个优化后的静态文件(如 bundle.js),便于浏览器高效加载运行。Webpack 默认支持代码混淆功能。

Terser

概念:Terser 是 Webpack 默认的代码混淆工具,专注于优化 JavaScript 代码体积和性能。Terser 因其高效性和对现代 JavaScript 的支持,已成为前端开发中的主流压缩工具。

Terser 打包后代码

const fs=require("fs"),path=require("path"),sourceDir=path.join(__dirname,"public"),targetDir=path.join(__dirname,"dist");function copyFolderRecursive(e,r){const i=fs.readdirSync(e);for(const o of i){const i=path.join(e,o),s=path.join(r,o);fs.statSync(i).isDirectory()?(fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),copyFolderRecursive(i,s)):(fs.copyFileSync(i,s),console.log(`Copied: ${i} -> ${s}`))}}fs.existsSync(targetDir)||fs.mkdirSync(targetDir,{recursive:!0}),copyFolderRecursive(sourceDir,targetDir),copyFolderRecursive("fake-api-server",targetDir),console.log("All files copied from public to dist!");

解 terser 混淆

Source Map 概念:Source Map 是一种映射技术,可将压缩/混淆后的代码还原为原始源代码,方便调试。若生产环境中泄露 Source Map,攻击者可利用其完全还原前端 JavaScript 代码。

  • 无 Source Map 解决方案:

    • 使用 de4js 工具,一键格式化并解包常见的压缩/加壳技术(常见的压缩、编码均可)。
    • 借助大模型分析代码(但是对长代码不友好)。
  • 有 Source Map

    • 如何发现 Source Map:检查是否存在后缀为 .js.map 的文件,若有,则该文件就是 Source Map。
    • 还原方法:使用工具 shujishuji js.map文件 -o 输出文件夹
    • 注意:还原后的 JavaScript 代码可能包含敏感信息,需仔细分析。

    Source Map 示例

js-ob 混淆(Obfuscator)

Obfuscator 是另一款常用的 JavaScript 混淆工具。 image-20250813201353654

解混淆方法

  • 工具:Babel(JavaScript 编译器工具链)和 AST(抽象语法树)。

    • Babel:Babel 是一个广泛使用的 JavaScript 编译器工具链,主要用于将现代 JavaScript 代码(如 ES6/ES2015+)转换为向后兼容的旧版本 JavaScript(如 ES5),以确保代码能在不支持新特性的旧浏览器或环境中运行。
    • AST:AST 是源代码的树状结构化表示,它以树形结构精确描述代码的语法逻辑(如变量声明、函数调用、循环等),但不包含代码的格式细节(如空格、分号)。它是编译器、代码分析工具(如 Babel、ESLint)和代码转换工具的核心数据结构。
  • 在线工具https://dev-coco.github.io/Online-Tools/JavaScript-Deobfuscator.html

  • 本地工具:decodeObfuscator 使用方法:将混淆后的 JavaScript 文件放入 input 目录,运行命令 node main.js ,解混淆后的代码将输出到 output 目录。

前端加密

签名

概念:签名是一种通过特定算法对数据进行加密以验证其完整性和真实性的技术。前端签名加密常用于防止数据篡改。

靶场:前端验证签名(验签)表单:HMAC-SHA256

定位 JS 签名加密的函数

方法

  • 搜索 CryptoJSEncrypt 相关的函数调用。
  • 通过调试 JavaScript 代码定位加密逻辑。

示例

通过调试发现,signature 是由 username=xxx&password=xxx 通过 HmacSHA256 加密生成。

image-20250813155938220

验证签名

步骤

  1. 抓取数据包,修改 username 的值。
  2. 使用相同的加密算法(HmacSHA256)计算新的签名。
  3. 替换请求中的 signature 值并发送请求。

image-20250813161444837

替换后的签名验证成功。

image-20250813161623948

自动化签名替换

手动修改签名在批量操作(如密码爆破)中效率低下,可使用 YAKit 热加载 功能实现自动化替换。

// 功能:自动计算并替换 HTTP 请求中的 signature
encryptData = (packet) => {
    // 获取请求体
    body = poc.GetHTTPPacketBody(packet)
    params = json.loads(body)
    // 获取账号和密码
    name = params.username
    pass = params.password
    key = "31323334313233343132333431323334" // 十六进制密钥

    // HmacSha256加密
    signText = f`username=${name}&password=${pass}`
    sign = codec.EncodeToHex(codec.HmacSha256(f`${codec.DecodeHex(key)~}`, signText))


    // 构造新的请求体
    result = f`{"username":"${name}","password":"${pass}","signature":"${sign}","key":"${key}"}`
    return string(poc.ReplaceBody(packet, result, false))

}

beforeRequest = func(req){
    return encryptData(req)
}

效果: 每次发送请求时,YAKit 会自动计算并替换 signature,无需手动操作。

image-20250813164159571

靶场:前端验证签名(验签)表单:先 HMAC-SHA256 再 RSA

定位 JS 签名加密的函数

通过定位 JS 签名加密的函数,发现,signature 是由 username=xxx&password=xxx 通过 HmacSHA256 加密后在利用 RSA 公钥加密生成。

image-20250814093443334

自动化获取签名

getPubkey = func() {
    rsp, req = poc.HTTP(`GET /crypto/js/rsa/public/key HTTP/1.1
Host: 192.168.2.243:8080

    `, poc.https(true))~
    body = poc.GetHTTPPacketBody(rsp)// 响应体    
    return body// body 里面就是rsa的公钥
}

encryptData = (packet) => {
    body = poc.GetHTTPPacketBody(packet)
    params = json.loads(body)
    name = params.username
    pass = params.password
    key = "31323334313233343132333431323334"
    pemBytes = getPubkey()// 获取公钥    

    signText = f`username=${name}&password=${pass}`
    sha256sign = codec.EncodeToHex(codec.HmacSha256(f`${codec.DecodeHex(key)~}`, signText))// HS256加密    
    rsaSign = codec.EncodeToHex(codec.RSAEncryptWithPKCS1v15(pemBytes, sha256sign)~)// RSA加密    

    body = f`{"username":"${name}","password":"${pass}","signature":"${rsaSign}","key":"${key}"}`
    return string(poc.ReplaceBody(packet, body, false))
}

beforeRequest = func(req) {
    return encryptData(req)
}

使用上述热加载,每次发送请求时,YAKit 会自动计算并替换 signature,无需手动操作。

image-20250814101545615

请求加密

概念:与签名不同(签名只是在原有的参数中,多了一个签名参数),请求加密将整个参数加密,通常使用对称加密算法(如 AES)。前端加密中,AES 的密钥(key)和初始向量(IV)通常是随机生成的,通过请求发送给服务器,服务器使用相同的 key 和 IV 解密数据。

靶场:CryptoJS.AES(CBC)_前端加密登陆表单

定位:在该靶场中使用了请求加密,通过静态分析与动态调试,定位 AES-CBC 加密算法,并发现 key 和 IV 。

image-20250814194229580

热加载实现实时加密

encryptData = (packet) => {
    body = poc.GetHTTPPacketBody(packet)

    hexKey = "31323334313233343132333431323334"
    hexIV = "97ba30beaabf8ccfebeca655d487805a"
    key = codec.DecodeHex(hexKey)~
    iv = codec.DecodeHex(hexIV)~

    data = codec.AESCBCEncrypt(key /*type: []byte*/, body, iv /*type: []byte*/)~
    data = codec.EncodeBase64(data)

    body = f`{"data": "${data}","key": "${hexKey}","iv": "${hexIV}"}`
    return string(poc.ReplaceBody(packet, body, false))
}

//发送到服务端修改数据包
beforeRequest = func(req){
    return encryptData(req)
}

效果:请求数据自动加密,服务器亦可解密。

image-20250814194551365

靶场:CryptoJS.AES(ECB) 被前端加密的 SQL 注入

该靶场同前一个利用方式一样,使用了 AES-ECB 加密,使用如下热加载即可实现实时解密。

encryptData = (packet) => {
    body = poc.GetHTTPPacketBody(packet)

    hexKey = "31323334313233343132333431323334"
    hexIV = "97ba30beaabf8ccfebeca655d487805a"
    key = codec.DecodeHex(hexKey)~

    data = codec.AESCBCEncrypt(key /*type: []byte*/, body, nil /*type: []byte*/)~
    data = codec.EncodeBase64(data)

    body = f`{"data": "${data}","key": "${hexKey}"}`
    return string(poc.ReplaceBody(packet, body, false))
}

//发送到服务端修改数据包
beforeRequest = func(req){
    return encryptData(req)
}

image-20250814110540642

SQLMap 联合热加载

在 YAKit 中开启热加载代理,结合 SQLMap 可以进行 SQL 注入测试。

encryptData = (packet) => {
    body = poc.GetHTTPPacketBody(packet)
    hexKey = "31323334313233343132333431323334"
    key = codec.DecodeHex(hexKey)~
    data = codec.AESECBEncrypt(key /*type: []byte*/, body, nil /*type: []byte*/)~
    data = codec.EncodeBase64(data)
    body = f`{"data": "${data}","key": "${hexKey}"}`
    return string(poc.ReplaceBody(packet, body, false))
}
beforeRequest = func(req){
    return encryptData(req)
}

image-20250814114413477

请求包(1.txt)

POST /crypto/js/lib/aes/ecb/handler/sqli HTTP/1.1
Host: 192.168.2.243:8080
Content-Type: application/json

{"username":"admin","password":"admin"}

SQLMap 命令

sqlmap -r 1.txt --proxy=http://127.0.0.1:8083 --batch --tables --force-ssl --flush-session

成功检测到 SQL 注入漏洞并获取数据库信息。

image-20250814114815135

image-20250814115131710

靶场:SQL 注入 (从登陆到 Dump 数据库)

定位:发现前端使用 AES-CBC 加密。

image-20250814134803590

手工验证:抓包分析,验证加密数据。

image-20250814134940164

解密结果:成功解密出原始数据。

image-20250814135033804

热加载实现加解密

encryptAES = (packet) => {
    body = poc.GetHTTPPacketBody(packet)
    
    key =  randstr(16)
    iv = randstr(12)
    
    data = codec.AESCBCEncrypt(key /*type: []byte*/, body, iv /*type: []byte*/)~
    data = codec.EncodeBase64(data)
    
    hexKey = codec.EncodeToHex(key)
    hexIV = codec.EncodeToHex(iv)
    
    body = f`{"key": "${hexKey}","iv": "${hexIV}","message": "${data}"}`

    return poc.ReplaceBody(packet, body, false)
}

decryptAES = (packet) => {
    body = poc.GetHTTPPacketBody(packet)
    body = json.loads(body)
    key = codec.DecodeHex(body.key)~
    iv = codec.DecodeHex(body.iv)~
    data = codec.DecodeBase64(body.message)~
    data = codec.AESCBCDecrypt(key, data, iv)~
    return poc.ReplaceBody(packet, data, false)
}

beforeRequest = func(req){
    return encryptAES(req)
}
afterRequest = func(rsp){
    return decryptAES(rsp)
}

 

效果:请求和响应数据自动加解密。

image-20250814140035702

SQL 注入测试

  1. 使用万能密码登录,替换 Cookie 后成功进入系统。
  2. 发现用户搜索功能存在 SQL 注入漏洞。
  3. 使用热加载代理结合 SQLMap 测试:

image-20250814144609787

sqlmap -r 1.txt --proxy=http://127.0.0.1:8083 --batch --tables --force-ssl --flush-session --dbms sqlite

结果:成功获取数据库信息。

image-20250814142407948

JsRpc

JsRpc 是一款用于远程调用浏览器内置 JavaScript 函数的工具,可以无需处理复杂或未知的前端加密逻辑,直接利用相关函数。

运行 JsRpc

  1. 下载并运行 JsRpc:

    ./linux_amd64
    

    JsRpc 运行

  2. 在浏览器控制台加载 JsRpc 客户端

  3. 连接 JsRpc 服务器:

    var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&name=hlg");
    

    JsRpc 连接

  4. 定位加密函数(如 Encrypt),注入环境变量并注册动作(如 word):

    window.enc = Object(Encrypt)
    
    demo.regAction("hi", function (resolve, param) {
        res = enc(param['word'])
        resolve(res)
    })
    
  5. 调用加密函数:

    http://127.0.0.1:12080/go?action=hi&group=zzz&param={"word":"username=a&password=b"}
    

    JsRpc 加密结果

JsRpc 与 BurpSuite 的联动

在开启 JsRpc 的基础上。

步骤

  • 在 BurpSuite 安装 autoDecoder 插件,用于处理加解密。
  • 启动 Flask API 服务,实现 JsRpc 自动签名:
from flask import Flask,Response,request
import base64,re
import requests
import json
from urllib.parse import quote,urlencode

app = Flask(__name__) 
url = "http://localhost:12080/go"
@app.route('/encode',methods=["POST"])
def encrypt(): 
    param = request.form.get('dataBody') # 获取  post 参数  
    print(param)
    headers = request.form.get('dataHeaders') # 获取 header 参数  

    json_data = json.loads(param)

# 提取字段并拼接
    result = f'username={json_data["username"]}&password={json_data["password"]}'
    uri = '?action=hi&group=zzz&param={{"word":"{0}"}}'.format(quote(result))
    print(uri)
    res = requests.post(url= url+uri) #这里换get也是可以的
    print(res.text)
    encry_param = json.loads(res.text)['data']

    print(encry_param)
    json_data['signature'] = encry_param
    print(headers)

    return headers.strip() + "\r\n\r\n\r\n\r\n" + json.dumps(json_data)


@app.route('/decode',methods=["POST"]) 
def decrypt():
    param = request.form.get('dataBody') # 获取  post 参数  
    headers = request.form.get('dataHeaders') 
    return headers.strip() + "\r\n\r\n\r\n\r\n" + param


if __name__ == '__main__': 
    app.debug = True # 设置调试模式,生产模式的时候要关掉debug  
    app.run(host="0.0.0.0",port="8888")

:Flask 服务运行在 http://127.0.0.1:8888,提供加密接口 /encode 和解密接口 /decode

image-20250814202344994

  • 在 BurpSuite 的 autoDecoder 插件中配置 接口加解密,即可以测试对数据包的加解密,若可用,则如下例中的 signature 就会发生变化。

image-20250814203139728

  • Option 模块中,启用 RepeaterIntruder,同时勾选 接口加解密对数据头进行处理,并并添加靶场域名,随后注意 保存配置,即可在重放器和爆破处实时进行加解密。

image-20250814203636560

image-20250814203737786

注意:加解密请求日志可在 Logger 模块查看。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇