从 0 到 1 玩转前端加密 encrypt-labs 靶场:环境搭建 + 全关卡解析
文章目录
- 前言
- 1 环境搭建(Docker 混淆版)
- 2 配置插件
- 3 AES固定Key
- 4 AES服务端获取Key
- 5 Rsa加密
- 6 AES+Rsa加密
- 7 DES规律key
- 8 明文加签
- 9 加签key在服务器端
- 10 禁止重放
⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全技术研究与合规性交流,旨在提升读者的安全防护意识与技术能力。任何个人或组织在使用相关内容前,必须获得目标网络 / 系统所有者的明确且书面授权,严禁用于未经授权的网络探测、漏洞利用、数据获取等非法行为。
前言
在 Web 前端安全防护中,为抵御密码爆破攻击,开发人员常会在前端对登录关键参数(如签名、密码等)采用加密技术处理后再提交请求,增加了爆破的技术门槛。
而 “encrypt-labs” 作为专项练习靶场,用于探索这类前端加密场景下的破解思路 —— 我们需要先分析前端加密逻辑,掌握常用的解密 / 逆向手法,再构造符合服务端验证规则的请求,以此完成密码爆破的技术练习,理解前端加密防护的核心原理与破解要点。
1 环境搭建(Docker 混淆版)
基于 Ubuntu 24.04.3 TLS 环境。
git clone https://github.com/Ta0ing/encrypt-labs-docker.git # 修改数据库配置cd src sudovim database.php # 修改如下4行$host='encrypt_labs_mysql';$dbname='encrypt_labs_db';$username='encrypt_user';$password='encrypt_password';# docker 镜像配置sudovim /etc/docker/daemon.json {"registry-mirrors":["https://docker.1ms.run", "https://dockerproxy.cn", "https://hub.rat.dev"]} systemctl restart docker# 老版本 Docker(独立安装 docker-compose)docker-compose up -d--build# 新版本 Docker(内置命令 compose)docker compose up -d--build
访问 你的靶机ip:82 地址,默认账户密码:admin 123456

2 配置插件
配置 BurpSuite 插件的目的:
- 其实加解密可以通过任意语言的代码实现,但是我们解密后在 BurpSuite 中可以直接配置明文,更直观的进行爆破;
- 如果不通过 BurpSuite 进行爆破,完全可以仅通过脚本来实现请求的加解密和爆破。
2.1 Galaxy
安装 BurpSuite 插件:https://github.com/outlaws-bai/Galaxy
插件介绍:https://github.com/outlaws-bai/Galaxy/wiki/%E5%8A%9F%E8%83%BD%E8%AF%A6%E8%A7%A3

下载脚本示例代码:便于独立脚本的开发
git clone https://github.com/outlaws-bai/GalaxyHttpHooker.git 示例说明:
- aes_cbc.py(基础版本)直接处理请求体中的JSON数据
- aes_cbc_form.py(表单版本)处理表单数据(a=1&b=2)
- aes_cbc_query.py(查询参数版本)只对查询参数中的字段进行加解密(?a=1&b=2)
因此我们遇到的 HTTP 参数加密请求,可以依据上述模板进行修改。

所有脚本围绕4个hook方法:hookRequestToBurp,hookRequestToServer, hookResponseToBurp, hookResponseToClient。
作用分别如下:我们对请求的加解密是前2个方法,如果响应内容加密了,你想解密需要实现后2个方法。
①:HTTP请求从客户端到达Burp时被触发。你需要在此处完成请求解密的代码,这样就可以在Burp中看到明文的请求报文。
②:HTTP请求从Burp将要发送到Server时被触发。你需要在此处完成请求加密的代码,这样就可以将加密后的请求报文发送到Server。
③:HTTP响应从Server到达Burp时被触发。你需要在此处完成响应解密的代码,这样就可以在Burp中看到明文的响应报文。
④:HTTP响应从Burp将要返回到Client时被触发。你需要在此处完成响应加密的代码,这样就可以将加密后的响应报文返回给Client。
2.2 autoDecoder
官方仓库:https://github.com/f0ng/autoDecoder
3 AES固定Key
使用火狐浏览器的“栈跟踪”功能,查看调用的所有 js 代码。
可以看到sendDataAes http://192.168.10.11:82/js/app.js:1:1098和发起者都是app.js,因此从app.js查询加密逻辑。

如果是谷歌浏览器,只能看发起时的 js 文件,当然这个靶场不复杂,无伤大雅。

继续使用火狐浏览器演示,查看 app.js 代码,可以看到代码经过了混淆。

在右边 click 处勾上,表示点击时调试。

点击登录按钮,然后点击“AES固定key”,继续调试。

自动定位到如下sendDataAes函数中,可以看到对用户名、密码的对象使用 AES 加密,包含 iv、CBC的mode、padding填充信息。

我们需要知道是如何将用户名密码进行 AES 加密的,直接将此处代码给 AI 分析下:
函数:sendDataAes()
加密参数:
- 算法:AES-CBC
- 密钥(Key):固定 16 位字符串
1234567890123456(Utf8 编码) - 偏移量(IV):固定 16 位字符串
1234567890123456(Utf8 编码) - 填充方式:PKCS7 (默认填充规则)
- 输出:Base64 编码的加密结果
传输方式:将加密后的数据拼接为 encryptedData=加密值,以 application/x-www-form-urlencoded 格式 POST 提交
抓包查看请求信息,是表单格式的。

点击start按钮启动插件,这里不勾选Hook Response,原因是响应内容是明文,request.host填入的是靶机的IP。

直接复制aes_cbc_form.py脚本进行修改:这里修改了 KEY、IV 的值,将模板中的 username 修改为 encryptedData,因为请求时的参数是encryptedData。
import json import base64 import typing as t from fastapi import FastAPI from urllib.parse import parse_qs, urlencode from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from _base_classes import* KEY =b"1234567890123456" IV =b"1234567890123456" JSON_KEY ="data" app = FastAPI()@app.post("/hookRequestToBurp", response_model=RequestModel)asyncdefhook_request_to_burp(request: RequestModel):"""HTTP请求从数据客户端到达Burp时被调用。在此处完成请求解密的代码就可以在Burp中看到明文的请求报文。"""# 获取需要解密的数据 encrypted_data:bytes= base64.b64decode( parse_qs(request.content.decode())["encryptedData"][0])# 调用函数解密 data:bytes= decrypt(encrypted_data)print(f"[DEBUG] 解密后的数据: {data.decode()}")# 更新query request.content = urlencode({"encryptedData": data.decode()}).encode()return request @app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):"""HTTP请求从Burp将要发送到Server时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到Server。"""# 获取被解密的数据 data:bytes= parse_qs(request.content.decode())["encryptedData"][0].encode()# 调用函数加密回去 encryptedData:bytes= encrypt(data)print(f"[DEBUG] 加密后的数据: {base64.b64encode(encryptedData)}")# 更新query request.content = urlencode({"encryptedData": base64.b64encode(encryptedData)}).encode()return request @app.post("/hookResponseToBurp", response_model=ResponseModel)asyncdefhook_response_to_burp(response: ResponseModel):return response @app.post("/hookResponseToClient", response_model=ResponseModel)asyncdefhook_response_to_client(response: ResponseModel):return response defdecrypt(content:bytes)->bytes: cipher = AES.new(KEY, AES.MODE_CBC, IV)return unpad(cipher.decrypt(content), AES.block_size)defencrypt(content:bytes)->bytes: cipher = AES.new(KEY, AES.MODE_CBC, IV)return cipher.encrypt(pad(content, AES.block_size))defget_data(content:bytes)->bytes: body_json: t.Dict = json.loads(content)return base64.b64decode(body_json[JSON_KEY])defto_data(contnet:bytes)->bytes: body_json ={} body_json[JSON_KEY]= base64.b64encode(contnet).decode()return json.dumps(body_json).encode()if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)然后可以在 proxy 请求中尝试解密,操作如下:

解密后可以看到明文显示,这里是弹出了一个窗口,可以看到 url 编码后的结果;注意这里的X-Galaxy-Http-Hook: HookedRequest是 Galaxy 插件 hook 的标识,有这个标识才会进行加密脚本中hookRequestToServer的执行。

使用 Decoder 模块进行 URL 解码看看结果。

如果觉得手动解码有点麻烦,可以将请求发送到 repeater 模块中解密,右侧的 inspector 功能自动解码,省去一次操作步骤。

脚本也会同步打印调试信息,可以看到解密后的信息。

那么我们修改输入的明文,看看插件的效果,能否自动加密后发送请求。

可以看到,脚本中的hookRequestToServer将明文加密后发送给服务端。

接下来就是如何进行爆破的问题,其实跟普通的爆破是一样的,将解密后端请求添加到 intruder 模块中,针对爆破参数添加字典进行爆破即可(后续题目不再赘述爆破步骤)。

可以看到响应结果是正常显示。

插件脚本也会自动进行加密后发送请求,这里提供的脚本也同步打印了加密的信息。

4 AES服务端获取Key
查看请求:先从服务端获取 aes 加密的 key 和 iv 的值,多次请求可以看到这 2 个值是固定的。

然后发送加密请求,响应内容存在 Unicode 编码,使用 python print解码得到用户名或密码错误


通过栈跟踪,定位到关键代码后分析如下:
请求格式:POST + application/json + encryptedData 的 JSON 体;
请求内容:明文是账号密码 JSON,加密后仅传加密字符串;
加密方式:AES-CBC + PKCS7 填充 + Base64 输出;
加密参数:Key/IV 从后端动态获取(Base64 解码后使用),长度均为 16 字节(AES-128)。

那么我们复制模板中的 aes_cbc.py 脚本,修改对 key 和 iv 进行 base64 解码、修改JSON_KEY为请求中的参数encryptedData:
import json import base64 import typing as t from fastapi import FastAPI from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from _base_classes import* KEY = base64.b64decode("UMmKw73GSNZcCFqj6\/XN5A==") IV = base64.b64decode("1SofbOFnjQCBcNt2M35PTQ==") JSON_KEY ="encryptedData" app = FastAPI()@app.post("/hookRequestToBurp", response_model=RequestModel)asyncdefhook_request_to_burp(request: RequestModel):"""HTTP请求从客户端到达Burp时被调用。在此处完成请求解密的代码就可以在Burp中看到明文的请求报文。"""# print(f"[+] hookRequestToBurp be called. request: {request.model_dump_json()}")# 获取需要解密的数据 encrypted_data:bytes= get_data(request.content)# 调用函数解密 data:bytes= decrypt(encrypted_data)print(f"[DEBUG] 解密后的数据: {data.decode()}")# 更新body为已解密的数据 request.content = data return request @app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):"""HTTP请求从Burp将要发送到Server时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到Server。"""# print(f"[+] hookRequestToServer be called. request: {request.model_dump_json()}")# 获取被解密的数据 data:bytes= request.content # 调用函数加密回去 encryptedData:bytes= encrypt(data)# 将已加密的数据转换为Server可识别的格式 body:bytes= to_data(encryptedData)print(f"[DEBUG] 加密后的数据: {base64.b64encode(encryptedData)}")# 更新body request.content = body return request @app.post("/hookResponseToBurp", response_model=ResponseModel)asyncdefhook_response_to_burp(response: ResponseModel):return response @app.post("/hookResponseToClient", response_model=ResponseModel)asyncdefhook_response_to_client(response: ResponseModel):return response defdecrypt(content:bytes)->bytes: cipher = AES.new(KEY, AES.MODE_CBC, IV)return unpad(cipher.decrypt(content), AES.block_size)defencrypt(content:bytes)->bytes: cipher = AES.new(KEY, AES.MODE_CBC, IV)return cipher.encrypt(pad(content, AES.block_size))defget_data(content:bytes)->bytes: body_json: t.Dict = json.loads(content)return base64.b64decode(body_json[JSON_KEY])defto_data(contnet:bytes)->bytes: body_json ={} body_json[JSON_KEY]= base64.b64encode(contnet).decode()return json.dumps(body_json).encode()if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)启动后将加密请求发送到 repeater 模块进行解密。

成功解密,脚本也正常打印调试信息。

修改密码请求,发送成功

脚本也成功打印调试信息。

5 Rsa加密
加密请求如下:

通过栈追踪定位到如下代码,分析结果如下:
请求方式:POST + 表单格式(application/x-www-form-urlencoded);
请求参数:明文是账号密码 JSON 串(格式是data={“username”:”xxx”,”password”:”xxx”}),加密后为 Base64 格式的 RSA 密文;
加密方法:RSA 非对称加密,PKCS#1 v1.5 填充;
加密参数:固定硬编码的 RSA 1024 位公钥。

Galaxy
改造模板中的rsa.py代码:
- 本题不知道解密的私钥,只能尝试进行请求的加密(前面分析已经知道了请求体的格式);
- 这个题目需要对加密后的结果进行 url 编码;
- 注意公钥书写的格式,
\n手动换行; - 提取请求的参数
JSON_KEY是data; - 需要对 BurpSuite 请求添加请求头
X-Galaxy-Http-Hook: HookedRequest,以让 Galaxy 插件识别是要加密的请求。
import base64 from urllib.parse import parse_qs, quote_plus from Crypto.Cipher import PKCS1_v1_5 from Crypto.PublicKey import RSA from fastapi import FastAPI from _base_classes import* pub_key ="""-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujvi NH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlM DSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3Cb ocDbsNeCwNpRxwjIdQIDAQAB -----END PUBLIC KEY-----""" JSON_KEY ="data" app = FastAPI()@app.post("/hookRequestToBurp", response_model=RequestModel)asyncdefhook_request_to_burp(request: RequestModel):return request @app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):"""HTTP请求从Burp将要发送到Server时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到Server。"""# 获取请求内容 content_str = request.content.decode('utf-8')# 解析请求内容,提取参数的值try: parsed_data = parse_qs(content_str)if'data'in parsed_data:# 获取参数的值 original_data = parsed_data['data'][0]print(f"[DEBUG] 加密前的数据: {original_data}")# 对值进行RSA加密 encrypted_data = encrypt(original_data.encode('utf-8'), pub_key)# Base64编码 encrypted_b64 = base64.b64encode(encrypted_data).decode('utf-8')# URL编码 encrypted_url_encoded = quote_plus(encrypted_b64)# 构造新的请求内容 new_content =f"data={encrypted_url_encoded}"print(f"[DEBUG] 加密后的数据: {new_content}")# 更新请求内容 request.content = new_content.encode('utf-8')else:print("[WARNING] 请求中未找到指定参数")except Exception as e:print(f"[ERROR] 处理请求时出错: {e}")passreturn request defencrypt(content:bytes, secret:bytes)->bytes: rsa_key = RSA.import_key(secret) cipher = PKCS1_v1_5.new(rsa_key)return cipher.encrypt(content)if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)启动脚本后,通过构造 BurpSuite 请求完成加密请求,注意添加请求体X-Galaxy-Http-Hook: HookedRequest,让请求走我们编写的 Galaxy 的脚本。


autoDecoder
配置如下图:题目中的密文还进行了 url 编码,所有要勾选2

注意密钥是公钥:填写格式如下:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujvi NH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlM DSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3Cb ocDbsNeCwNpRxwjIdQIDAQAB 
配置后在 repeater 模块发送请求,可以成功加密。

6 AES+Rsa加密
加密请求如下:

定位到加密代码,分析如下:
使用算法:
- AES-CBC 模式,加密数据格式
{"username":"xxx","password":"xxx"},AES的 key 和 iv 随机16 字节。 - RSA 公钥加密,
_0x41d8d9[_0x13ef4f(953, 972)](_0x13ef4f(990, 973))这一行,就是设置 RSA 公钥的地方;_0x13ef4f(953, 972)对应setPublicKey,(_0x13ef4f(990, 973)即公钥内容。
{"encryptedData":"AES加密后的用户名+密码JSON字符串",// AES加密数据"encryptedKey":"RSA加密后的AES key(Base64格式)",// RSA加密后的AES key"encryptedIv":"RSA加密后的AES iv(Base64格式)"// RSA加密后的AES iv}
在浏览器的控制台打印console.log(_0x13ef4f(990, 973)) 获取 RSA 公钥:

-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujvi NH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlM DSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3Cb ocDbsNeCwNpRxwjIdQIDAQAB -----END PUBLIC KEY----- 思路:
- 目标是替换
encryptedData的内容,因此需要进行 AES 解密; - AES 解密需要 AES 的 key 和 iv;
- key 和 iv 在
encryptedKey和encryptedIv中被 RSA 的公钥加密; - 不知道 RSA 私钥无法解密 key 和 iv;通过Chrome浏览器固定 AES 的 key 和 iv 的值;
- 传参时
encryptedData使用明文数据(格式前面分析已经知道);encryptedKey和encryptedIv使用请求中的原始密文。 - 加密时通过 AES 算法加密
encryptedData的值。
使用Chrome浏览器的 override content 功能替换app.js:先将原始app.js下载到本地,在浏览器源码处右键Overrides content选择本地的文件,选中后修改代码中固定 key 和 iv 为16字节,修改完按Ctrl+S保存,注意格式。
注意:下次打开浏览器即使该页面启用了Overrides content也会失效,需要在修改的地方打断点,用 debugger 的形式运行。
, _0x4515a4 = CryptoJS.enc.Utf8.parse('1234567890123456') , _0x5e9345 = CryptoJS.enc.Utf8.parse('1234567890123456')
然后抓包,可以看到使用固定 key 和 iv 进行 AES 加密后的encryptedData,重试一次保证encryptedData的值不变,说明代码修改有效。

Galaxy
脚本需要实现的是使用AES加密参数encryptedData的值,固定 key 和 iv 为我们指定的值;其他2个参数的值不变。
import base64 import json from Crypto.Cipher import PKCS1_v1_5, AES from Crypto.PublicKey import RSA from Crypto.Util.Padding import pad from fastapi import FastAPI from _base_classes import* KEY =b"1234567890123456"# 16字节 IV =b"1234567890123456"# 16字节 JSON_KEY1 ="encryptedData" app = FastAPI()@app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):try:# 获取请求内容 data:bytes= request.content # 解析JSON request_json = json.loads(data.decode())# print(f"解析的JSON: {request_json}")# 只修改 encryptedData 参数,保持其他参数不变if JSON_KEY1 in request_json:# 获取原始的 encryptedData 值 original_encrypted_data = request_json[JSON_KEY1]print(f"原始 encryptedData: {original_encrypted_data}")# 处理 encryptedData 值ifisinstance(original_encrypted_data,str):try:# 尝试将字符串解析为JSON对象 inner_json = json.loads(original_encrypted_data)print(f"解析出的JSON: {inner_json}")# 重新序列化为字符串用于加密 encrypted_data_bytes = original_encrypted_data.encode()except json.JSONDecodeError:# 如果不是有效的JSON字符串,直接使用原字符串print("encryptedData 不是有效的JSON格式,直接使用原值") encrypted_data_bytes = original_encrypted_data.encode()else:# 如果不是字符串,转换为字符串 encrypted_data_bytes =str(original_encrypted_data).encode()# 对 encryptedData 进行AES加密 encryptedData1:bytes= aes_encrypt(encrypted_data_bytes)# 更新 encryptedData 参数的值 request_json[JSON_KEY1]= base64.b64encode(encryptedData1).decode()# 构造新的请求体 body:bytes= json.dumps(request_json, ensure_ascii=False).encode() encrypted_json_str = body.decode()print(f"[DEBUG] 修改后的数据: {encrypted_json_str}") request.content = body else:print(f"未找到 {JSON_KEY1} 参数")except Exception as e:print(f"处理错误: {e}")return request defaes_encrypt(content:bytes)->bytes: cipher = AES.new(KEY, AES.MODE_CBC, IV)return cipher.encrypt(pad(content, AES.block_size))defrsa_encrypt(content:bytes, secret:bytes)->bytes: rsa_key = RSA.import_key(secret) cipher = PKCS1_v1_5.new(rsa_key)return cipher.encrypt(content)if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)启动编写的脚本,将加密请求发送到 repeater 模块,添加 hook 的请求头,修改加密数据为{\"username\":\"admin\",\"password\":\"123456\"},发送请求后,服务端成功响应。

脚本也同步打印调试信息:

autoDecoder
使用 autoDecoder 比较方便,配置如下(记得保存配置):正则是"encryptedData":"(.*)","encryptedKey"。

记得重新加载下新配置,以免不生效。

直接在请求中明文输入,内容是{"username":"admin","password":"123456"},实战中可以对这里进行爆破。

7 DES规律key
请求如下:

核心代码使用 DES-CBC 模式加密密码,核心规则:
- 密钥(Key):从用户名截取前 8 位(0开始),不足补字符
6到 8 位; - 向量(IV):固定前缀
9999+ 用户名前 4 位(0开始),不足补字符9到 8 位; - 填充方式:PKCS7(CryptoJS 默认);
- 输出格式:加密后的二进制密文转 16 进制字符串(而非 Base64);
- 加密对象:页面输入的密码(
password输入框的值)。
一句话总结:通过用户名获取 key 和 iv 的值,对密码进行 DES-CBC 加密,结果转 16 进制字符串。

Galaxy
脚本代码如下:实现的就是对 password 值的加解密,需要获取username来生成 key 和 iv 的值。
import json from Crypto.Cipher import DES from Crypto.Util.Padding import pad, unpad from fastapi import FastAPI from _base_classes import* KEY =b"" IV =b"" app = FastAPI()@app.post("/hookRequestToBurp", response_model=RequestModel)asyncdefhook_request_to_burp(request: RequestModel):"""HTTP请求从客户端到达Burp时被调用。在此处完成请求解密的代码就可以在Burp中看到明文的请求报文。"""# print(f"[+] hookRequestToBurp be called. request: {request.model_dump_json()}")try:# 解析请求体JSON request_body = json.loads(request.content.decode()) username = request_body.get("username","") password_encrypted = request_body.get("password","")ifnot username ornot password_encrypted:print("缺少username或password字段")return request # 生成KEY和IVglobal KEY, IV KEY, IV = generate_key_iv(username)print(f"生成的KEY: {KEY}, IV: {IV}")# 将十六进制字符串转换为字节用于解密 encrypted_bytes =bytes.fromhex(password_encrypted)# 使用decrypt方法解密 decrypted_bytes = decrypt(encrypted_bytes) password_decrypted = decrypted_bytes.decode('utf-8')print(f"解密后的password: {password_decrypted}")# 更新请求体 request_body["password"]= password_decrypted request.content = json.dumps(request_body, ensure_ascii=False).encode()except Exception as e:print(f"解密过程出错: {e}")return request @app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):"""HTTP请求从Burp将要发送到Server时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到Server。"""# print(f"[+] hookRequestToServer be called. request: {request.model_dump_json()}")try:# 解析请求体JSON(此时password应该是明文) request_body = json.loads(request.content.decode()) username = request_body.get("username","") password_decrypted = request_body.get("password","")ifnot username ornot password_decrypted:print("缺少username或password字段")return request # 生成KEY和IVglobal KEY, IV KEY, IV = generate_key_iv(username)print(f"[+] 生成的KEY: {KEY}, IV: {IV}")# 使用现有的encrypt方法加密password password_bytes = password_decrypted.encode('utf-8') encrypted_bytes = encrypt(password_bytes)# 转换为十六进制字符串 password_encrypted = encrypted_bytes.hex()print(f"加密后的password: {password_encrypted}")# 只更新password字段 request_body["password"]= password_encrypted # 构造新的请求体 request.content = json.dumps(request_body, ensure_ascii=False).encode()except Exception as e:print(f"加密过程出错: {e}")return request defgenerate_key_iv(username:str)->tuple[bytes,bytes]:""" 根据用户名生成KEY和IV KEY: 用户名截取前8位(0开始),不足补字符'6'到8位 IV: 固定前缀'9999' + 用户名前4位(0开始),不足补字符'9'到8位 """# 生成KEY username_prefix = username[:8] key = username_prefix.ljust(8,'6').encode()# 生成IV username_prefix_4 = username[:4] iv_str ="9999"+ username_prefix_4.ljust(4,'9') iv = iv_str.encode()return key, iv defdecrypt(content:bytes)->bytes: cipher = DES.new(KEY, DES.MODE_CBC, IV)return unpad(cipher.decrypt(content), DES.block_size)defencrypt(content:bytes)->bytes: cipher = DES.new(KEY, DES.MODE_CBC, IV)return cipher.encrypt(pad(content, DES.block_size))if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)在 repeater 中解密请求:

解密后密码是明文:

发送也正常:

脚本调试信息也正常:

autoDecoder
使用这个工具的话只能单个进行验证,需要自己算 key 和 iv,密码记得选 16 进制(hex);因此这关使用 autoDecoder 工具只能爆破固定用户名的密码。

8 明文加签
请求如下:

核心代码分析如下:
随机数
- 签名算法:HmacSHA256(基于哈希的消息认证码,不可逆);
- nonce 是随机小数转换成36进制再去掉
0.; - timestamp 是获取当前毫秒时间戳,除以1000并向下取整;
- 签名密钥:固定值
be56e057f20f883e; - 签名原文:
用户名 + 密码 + 随机数(nonce) + 时间戳(timestamp); - 签名输出:HmacSHA256 结果转 16 进制字符串。

选中使用 Galaxy 进行请求加签,脚本如下:
import hashlib import hmac import json import random import string import time from fastapi import FastAPI from _base_classes import* app = FastAPI()@app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):"""HTTP请求从Burp将要发送到Server时被调用。在此处完成请求签名计算的代码。"""try:# 获取原始请求内容 content_str = request.content.decode('utf-8')# 解析请求体JSON request_body = json.loads(content_str)# 获取用户名和密码 username = request_body.get("username","") password = request_body.get("password","")ifnot username ornot password:print("缺少username或password字段")return request # 生成随机数 nonce =''.join(random.choices(string.ascii_lowercase + string.digits, k=10))# 获取当前毫秒时间戳 timestamp =int(time.time())# 签名密钥 secret_key ="be56e057f20f883e"# 构造签名原文: 用户名 + 密码 + 随机数 + 时间戳 sign_text =f"{username}{password}{nonce}{timestamp}"# 计算HmacSHA256签名 signature = hmac.new( secret_key.encode('utf-8'), sign_text.encode('utf-8'), hashlib.sha256 ).hexdigest()# 更新请求体中的字段 request_body["nonce"]= nonce request_body["timestamp"]= timestamp request_body["signature"]= signature # 构造新的请求体 new_content = json.dumps(request_body, ensure_ascii=False).encode() request.content = new_content print(f"新的请求内容: {new_content.decode()}")except json.JSONDecodeError as e:print(f"JSON解析错误: {e}")return request except Exception as e:print(f"签名计算过程出错: {e}")return request return request if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)发送请求时记得添加请求头:X-Galaxy-Http-Hook: HookedRequest


9 加签key在服务器端
请求有2个,先从服务端获取签名,再发送请求:


核心代码逻辑:账号密码 + 生成时间戳 → 找服务器要签名 → 带签名提交登录信息。
asyncfunctionsendDataWithNonceServer(_0xb4476b){function_0x5b14e6(_0x9f039f, _0x3746ee){return_0x4f79d5(_0x9f039f -0xde, _0x3746ee);}const _0x41822d = document['getElementById']('username')['value'], _0x3c65d8 = document['getElementById']('password')['value'], _0x173dbd = Math[_0x5b14e6(0x281,0x282)](Date[_0x5b14e6(0x282,0x26f)]()/0x3e8);try{const _0x55d2c7 =awaitfetch(_0xb4476b +_0x5b14e6(0x283,0x28d),{'method':_0x5b14e6(0x27a,0x26b),'headers':{'Content-Type':_0x5b14e6(0x280,0x26f)},'body':JSON['stringify']({'username': _0x41822d,'password': _0x3c65d8,'timestamp': _0x173dbd })});closeModal();if(!_0x55d2c7['ok']){ console['error']('获取签名失败:', _0x55d2c7['statusText']),alert('获取签名失败,请稍后重试。');return;}const{signature: _0x2755ad}=await _0x55d2c7[_0x5b14e6(0x26b,0x279)]();if(!_0x2755ad){alert('签名获取失败,服务器未返回签名。');return;}const _0x58deb5 =awaitfetch(''+ _0xb4476b,{'method':'POST','headers':{'Content-Type':'application/json'},'body':JSON['stringify']({'username': _0x41822d,'password': _0x3c65d8,'timestamp': _0x173dbd,'signature': _0x2755ad })});if(!_0x58deb5['ok']){ console['error'](_0x5b14e6(0x284,0x28b), _0x58deb5['statusText']),alert('提交数据失败,请稍后重试。');return;}const _0x22648d =await _0x58deb5['json'](); _0x22648d[_0x5b14e6(0x26c,0x26a)]?(alert('登录成功'), window['location']['href']='success.html'):alert(_0x22648d[_0x5b14e6(0x274,0x285)]||'用户名或密码错误');}catch(_0x30ffca){ console['error'](_0x5b14e6(0x26f,0x265), _0x30ffca),alert('发生错误,请稍后重试。');}}直接通过纯 python 脚本实现即可,没必要使用插件。
import requests import json import time host ="http://192.168.10.11:82" headers ={"Content-Type":"application/json","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36","Cookie":"PHPSESSID=73fc283a7d583cc5433f44f606f9822a"}defget_timestamp():returnint(time.time())defstep1_get_signature(username, password, timestamp): url =f"{host}/encrypt/get-signature.php" payload ={"username": username,"password": password,"timestamp": timestamp } response = requests.post(url, json=payload, headers=headers) response.raise_for_status() result = response.json()return result.get("signature")defstep2_send_signed_request(username, password, timestamp, signature): url =f"{host}/encrypt/signdataserver.php" payload ={"username": username,"password": password,"timestamp": timestamp,"signature": signature } response = requests.post(url, json=payload, headers=headers) response.raise_for_status()return response.json()defmain(): username ="admin" password ="123456" timestamp = get_timestamp()try: signature = step1_get_signature(username, password, timestamp) result = step2_send_signed_request(username, password, timestamp, signature)print(f"服务器响应: {json.dumps(result, indent=2, ensure_ascii=False)}")except Exception as e:print(f"发生错误: {e}")if __name__ =="__main__": main()
10 禁止重放
请求如下:可以看到核心在于生成的 random 参数的值。

核心代码:random = RSA 加密 (毫秒时间戳 )。
functiongenerateRequestData(){function_0x34b479(_0x38b999, _0x500418){return_0x4f79d5(_0x38b999 -0x1e4, _0x500418);}const _0x1da0ac = document[_0x34b479(0x360,0x357)](_0x34b479(0x366,0x379))['value'], _0x4fdc07 = document[_0x34b479(0x360,0x360)](_0x34b479(0x368,0x37d))[_0x34b479(0x367,0x351)], _0x5a8525 = Date[_0x34b479(0x388,0x37a)](), _0x9f2be4 =_0x34b479(0x37e,0x381);function_0x5b0e97(_0x482893, _0x201f27){const _0x3ef89b =newJSEncrypt(); _0x3ef89b[_0x434cfc(0x300,0x2f2)](_0x201f27);const _0x311c6b = _0x3ef89b[_0x434cfc(0x2ed,0x2fc)](_0x482893['toString']());function_0x434cfc(_0x57eb61, _0x7fc509){return_0x34b479(_0x57eb61 --0x7d, _0x7fc509);}if(!_0x311c6b)thrownewError('RSA\x20encryption\x20failed.');return _0x311c6b;}let _0x110e21;try{ _0x110e21 =_0x5b0e97(_0x5a8525, _0x9f2be4);}catch(_0x16a5f1){return console['error']('Encryption\x20error:', _0x16a5f1),null;}const _0x163cb9 ={'username': _0x1da0ac,'password': _0x4fdc07,'random': _0x110e21 };return _0x163cb9;}经过断点调试,找到公钥:
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujvi NH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlM DSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3Cb ocDbsNeCwNpRxwjIdQIDAQAB -----END PUBLIC KEY----- 

编写 Galaxy 脚本,纯 python 脚本也行,逻辑一样。
import base64 import json import time from Crypto.Cipher import PKCS1_v1_5 from Crypto.PublicKey import RSA from fastapi import FastAPI from _base_classes import* app = FastAPI() KEY ="""-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujvi NH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlM DSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3Cb ocDbsNeCwNpRxwjIdQIDAQAB -----END PUBLIC KEY-----"""@app.post("/hookRequestToServer", response_model=RequestModel)asyncdefhook_request_to_server(request: RequestModel):"""HTTP请求从Burp将要发送到Server时被调用。在此处完成请求签名计算的代码。"""# print(f"[+] hookRequestToServer be called. request: {request.model_dump_json()}")try:# 获取原始请求内容 content_str = request.content.decode('utf-8')# 解析请求体JSON request_body = json.loads(content_str)# 获取用户名和密码 username = request_body.get("username","") password = request_body.get("password","")ifnot username ornot password:print("缺少username或password字段")return request # 获取当前毫秒时间戳 timestamp =str(int(time.time())*1000)# 对时间戳进行RSA加密 encrypted_bytes = encrypt(timestamp.encode('utf-8'), KEY) random = base64.b64encode(encrypted_bytes).decode()# 更新请求体中的字段 request_body["random"]= random # 构造新的请求体 new_content = json.dumps(request_body, ensure_ascii=False).encode() request.content = new_content print(f"新的请求内容: {new_content.decode()}")except Exception as e:print(e)return request return request defencrypt(content, secret)->bytes: rsa_key = RSA.import_key(secret) cipher = PKCS1_v1_5.new(rsa_key)return cipher.encrypt(content)if __name__ =="__main__":import uvicorn uvicorn.run(app, host="0.0.0.0", port=5000)
