跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python大前端算法

前端加密 encrypt-labs 靶场实战:环境搭建与解密

encrypt-labs 靶场涵盖 AES、RSA、DES 等多种前端加密场景,通过 Docker 部署环境并结合 BurpSuite 插件 Galaxy 与 autoDecoder 实现请求加解密自动化。教程详解了固定 Key、服务端获取 Key、混合加密及签名验证等关卡的逆向分析与爆破思路,提供 Python 脚本示例辅助流量处理。

云间运维发布于 2026/4/12更新于 2026/7/2337 浏览
前端加密 encrypt-labs 靶场实战:环境搭建与解密

概述

在 Web 前端安全防护中,为抵御密码爆破攻击,开发人员常会在前端对登录关键参数(如签名、密码等)采用加密技术处理后再提交请求。encrypt-labs 作为专项练习靶场,用于探索这类前端加密场景下的破解思路。我们需要先分析前端加密逻辑,掌握常用的解密/逆向手法,再构造符合服务端验证规则的请求。

1. 环境搭建(Docker 混淆版)

基于 Ubuntu 24.04.3 TLS 环境。

git clone https://github.com/Ta0ing/encrypt-labs-docker.git
cd src
sudo vim database.php
# 修改如下 4 行
$host='encrypt_labs_mysql';
$dbname='encrypt_labs_db';
$username='encrypt_user';
$password='encrypt_password';

Docker 镜像配置:

sudo vim /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 中直接配置明文更直观地进行爆破;也可仅通过脚本实现请求的加解密和爆破。

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)

所有脚本围绕 4 个 hook 方法:hookRequestToBurp,hookRequestToServer,hookResponseToBurp,hookResponseToClient。

作用分别如下:

  1. HTTP 请求从客户端到达 Burp 时被触发。在此处完成请求解密的代码,就可以在 Burp 中看到明文的请求报文。
  2. HTTP 请求从 Burp 将要发送到 Server 时被触发。在此处完成请求加密的代码,就可以将加密后的请求报文发送到 Server。
  3. HTTP 响应从 Server 到达 Burp 时被触发。在此处完成响应解密的代码,就可以在 Burp 中看到明文的响应报文。
  4. 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 填充信息。

图片

函数: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)
async def hook_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)
async def hook_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)
async def hook_response_to_burp(response: ResponseModel):
    return response

@app.post("/hookResponseToClient", response_model=ResponseModel)
async def hook_response_to_client(response: ResponseModel):
    return response

def decrypt(content: bytes) -> bytes:
    cipher = AES.new(KEY, AES.MODE_CBC, IV)
    return unpad(cipher.decrypt(content), AES.block_size)

def encrypt(content: bytes) -> bytes:
    cipher = AES.new(KEY, AES.MODE_CBC, IV)
    return cipher.encrypt(pad(content, AES.block_size))

def get_data(content: bytes) -> bytes:
    body_json: t.Dict = json.loads(content)
    return base64.b64decode(body_json[JSON_KEY])

def to_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)
async def hook_request_to_burp(request: RequestModel):
    """HTTP 请求从客户端到达 Burp 时被调用。在此处完成请求解密的代码就可以在 Burp 中看到明文的请求报文。"""
    # 获取需要解密的数据
    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)
async def hook_request_to_server(request: RequestModel):
    """HTTP 请求从 Burp 将要发送到 Server 时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到 Server。"""
    # 获取被解密的数据
    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)
async def hook_response_to_burp(response: ResponseModel):
    return response

@app.post("/hookResponseToClient", response_model=ResponseModel)
async def hook_response_to_client(response: ResponseModel):
    return response

def decrypt(content: bytes) -> bytes:
    cipher = AES.new(KEY, AES.MODE_CBC, IV)
    return unpad(cipher.decrypt(content), AES.block_size)

def encrypt(content: bytes) -> bytes:
    cipher = AES.new(KEY, AES.MODE_CBC, IV)
    return cipher.encrypt(pad(content, AES.block_size))

def get_data(content: bytes) -> bytes:
    body_json: t.Dict = json.loads(content)
    return base64.b64decode(body_json[JSON_KEY])

def to_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)
async def hook_request_to_burp(request: RequestModel):
    return request

@app.post("/hookRequestToServer", response_model=RequestModel)
async def hook_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}")
        pass
    return request

def 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)

启动脚本后,通过构造 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 字符串",
  "encryptedKey": "RSA 加密后的 AES key(Base64 格式)",
  "encryptedIv": "RSA 加密后的 AES iv(Base64 格式)"
}

图片

在浏览器的控制台打印 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)
async def hook_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 值
            if isinstance(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

def aes_encrypt(content: bytes) -> bytes:
    cipher = AES.new(KEY, AES.MODE_CBC, IV)
    return cipher.encrypt(pad(content, AES.block_size))

def rsa_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)
async def hook_request_to_burp(request: RequestModel):
    """HTTP 请求从客户端到达 Burp 时被调用。在此处完成请求解密的代码就可以在 Burp 中看到明文的请求报文。"""
    try:
        # 解析请求体 JSON
        request_body = json.loads(request.content.decode())
        username = request_body.get("username", "")
        password_encrypted = request_body.get("password", "")
        if not username or not password_encrypted:
            print("缺少 username 或 password 字段")
            return request
        # 生成 KEY 和 IV
        global 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)
async def hook_request_to_server(request: RequestModel):
    """HTTP 请求从 Burp 将要发送到 Server 时被调用。在此处完成请求加密的代码就可以将加密后的请求报文发送到 Server。"""
    try:
        # 解析请求体 JSON(此时 password 应该是明文)
        request_body = json.loads(request.content.decode())
        username = request_body.get("username", "")
        password_decrypted = request_body.get("password", "")
        if not username or not password_decrypted:
            print("缺少 username 或 password 字段")
            return request
        # 生成 KEY 和 IV
        global 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

def generate_key_iv(username: str) -> tuple:
    """
    根据用户名生成 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

def decrypt(content: bytes) -> bytes:
    cipher = DES.new(KEY, DES.MODE_CBC, IV)
    return unpad(cipher.decrypt(content), DES.block_size)

def encrypt(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)
async def hook_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", "")
        if not username or not 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 个,先从服务端获取签名,再发送请求:

图片

图片

核心代码逻辑:账号密码 + 生成时间戳 → 找服务器要签名 → 带签名提交登录信息。

async function sendDataWithNonceServer(_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 = await fetch(_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 = await fetch(''+ _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"
}

def get_timestamp():
    return int(time.time())

def step1_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")

def step2_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()

def main():
    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 加密 (毫秒时间戳 )。

function generateRequestData(){
    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 = new JSEncrypt();
        _0x3ef89b[_0x434cfc(0x300,0x2f2)](_0x201f27);
        const _0x311c6b = _0x3ef89b[_0x434cfc(0x2ed,0x2fc)](_0x482893['toString']());
        function _0x434cfc(_0x57eb61, _0x7fc509){return _0x34b479(_0x57eb61 --0x7d, _0x7fc509);}
        if(!_0x311c6b) throw new Error('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)
async def hook_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", "")
        if not username or not 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

def encrypt(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)

图片

图片

目录

  1. 概述
  2. 1. 环境搭建(Docker 混淆版)
  3. 修改如下 4 行
  4. 老版本 Docker(独立安装 docker-compose)
  5. 新版本 Docker(内置命令 compose)
  6. 2. 配置插件
  7. 2.1 Galaxy
  8. 2.2 autoDecoder
  9. 3. AES 固定 Key
  10. 4. AES 服务端获取 Key
  11. 5. RSA 加密
  12. Galaxy
  13. autoDecoder
  14. 6. AES+RSA 加密
  15. Galaxy
  16. autoDecoder
  17. 7. DES 规律 key
  18. Galaxy
  19. autoDecoder
  20. 8. 明文加签
  21. 9. 加签 key 在服务器端
  22. 10. 禁止重放
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • MATLAB 实现基于天牛须搜索算法(BAS)的无人机三维路径规划
  • 2024 年中国行业大模型市场报告:规模增长、落地路径与发展趋势
  • Copilot 与 Codeium 等 AI 代码助手核心技术解析
  • Redis List 在现代 C++ 中的 redis-plus-plus 实践
  • 海康机器人 3D 激光轮廓仪快速调试指南
  • Java 中基于属性的访问控制(ABAC):动态上下文感知权限管理
  • Windows WSL 环境下 AMD 显卡部署 Stable Diffusion WebUI 与 ComfyUI
  • AI 学习路径:从 Python 到 OpenClaw 本地智能体部署
  • 本地部署 Llama3 8B/70B 大模型完整指南
  • VS Code 远程连接服务器时 GitHub Copilot 失效的排查与修复
  • C++ 基于红黑树实现 set 和 map 容器
  • 基于 LangChain 实现的知识库问答工具
  • 阿里开源纯前端浏览器自动化框架 PageAgent 深度解析
  • SenseVoice-Small ONNX 量化版部署与 Gradio 使用记录
  • Vue 踩坑:el-checkbox-group 编辑页状态同步失效修复
  • OpenRouter:全球 AI 模型聚合平台及免费额度规则
  • Milvus 索引实战:类型选型与 Python 代码示例
  • Langchain-Chatchat 结合 Qwen 实现本地知识库前端开发
  • Whisper 语音识别模型下载指南:版本选择与格式说明
  • GitHub 全界面中文化实战:Tampermonkey 插件配置详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online