从 0 到 1 玩转前端加密 encrypt-labs 靶场:环境搭建 + 全关卡解析

从 0 到 1 玩转前端加密 encrypt-labs 靶场:环境搭建 + 全关卡解析

文章目录

⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全技术研究与合规性交流,旨在提升读者的安全防护意识与技术能力。任何个人或组织在使用相关内容前,必须获得目标网络 / 系统所有者的明确且书面授权,严禁用于未经授权的网络探测、漏洞利用、数据获取等非法行为。

前言

在 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方法:hookRequestToBurphookRequestToServerhookResponseToBurphookResponseToClient

作用分别如下:我们对请求的加解密是前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脚本进行修改:这里修改了 KEYIV 的值,将模板中的 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_KEYdata
  • 需要对 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 在 encryptedKeyencryptedIv 中被 RSA 的公钥加密;
  • 不知道 RSA 私钥无法解密 key 和 iv;通过Chrome浏览器固定 AES 的 key 和 iv 的值;
  • 传参时encryptedData使用明文数据(格式前面分析已经知道);encryptedKeyencryptedIv使用请求中的原始密文。
  • 加密时通过 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)
在这里插入图片描述
在这里插入图片描述

Read more

【Java 开发日记】有了解过 SpringBoot 的参数配置吗?

【Java 开发日记】有了解过 SpringBoot 的参数配置吗?

目录 核心概念:application.properties 与 application.yml 配置的加载位置与优先级 外部化配置(非常强大) 如何在代码中获取配置值? 常用配置示例 总结 当然了解,Spring Boot 的参数配置是其核心特性之一,也是它实现“约定大于配置”理念的关键。它极大地简化了传统 Spring 应用中繁琐的 XML 配置。 一、核心概念:application.properties 与 application.yml Spring Boot 默认使用这两种文件进行配置(二者选其一即可,.yml 更常用)。 application.properties (传统键值对格式) server.port=8081 spring.datasource.url=jdbc:mysql://localhost:

By Ne0inhk
Java之Volatile 关键字全方位解析:从底层原理到最佳实践

Java之Volatile 关键字全方位解析:从底层原理到最佳实践

文章目录 * 课程导言 * 适用对象 * 学习目标 * 第一部分:从并发三要素看volatile的定位 * 1.1 并发编程的三座大山 * 1.2 volatile的坐标:轻量级的同步利器 * 1.3 一个先导案例:感受volatile的魔力 * 第二部分:volatile与Java内存模型(JMM) * 2.1 为什么要JMM? * 2.2 JMM的核心结构:主内存 vs 工作内存 * 2.3 可见性问题的根源 * 2.4 volatile如何保证可见性? * 2.5 JMM对volatile的规范 * 第三部分:有序性与指令重排序 * 3.1 什么是指令重排序? * 3.2 重排序的潜在风险 * 3.3 volatile如何禁止重排序? * 3.

By Ne0inhk
【保姆级教程】无成本零门槛安装配置OpenClaw龙虾AI全能助手

【保姆级教程】无成本零门槛安装配置OpenClaw龙虾AI全能助手

哈喽大家好!最近爆火的 OpenClaw(龙虾AI)全能助手大家体验了吗?它不仅能帮你自动整理邮件、查询天气,还能全自动写小红书笔记并发布,简直是打工人和自媒体人的摸鱼神器! 很多小伙伴想玩但又怕配置太复杂、花销太大。今天给大家带来一篇零门槛、保姆级的安装配置教程!教你如何低成本获取云服务器,轻松实现 AI 大模型自由。全程图文指引,小白也能轻松搞定,赶紧跟着操作起来吧! 一、获取云服务器 想要畅玩 OpenClaw,首先我们需要一个服务器。这次教大家如何获取腾讯云轻量服务器来进行配置。 ⏰ 活动时间:2026年1月21日 - 3月31日 腾讯推出了登录 CodeBuddy 送 2C2G4M 轻量服务器的限时活动:登录先送1个月,活跃7天再送2个月。 👉 【官方地址】:https://www.codebuddy.cn/promotion/?ref=ie2rwhd1loq 根据页面提示安装好软件并登录账号后,直接选择一个月的轻量应用服务器即可。 之后只要累计活跃7天就能续费两个月(每天和 AI

By Ne0inhk
还有人不会用AI辅助编程吗?

还有人不会用AI辅助编程吗?

文章目录 * AI辅助编程的全面指南:技巧、策略与最佳实践 * 第一部分:AI辅助编程概述 * 1.1 AI编程助手的发展历程 * 1.2 现代AI编程助手的核心技术 * 1.3 AI编程助手的主要功能 * 第二部分:基础技巧与日常应用 * 2.1 有效使用代码补全 * 2.2 自然语言到代码的转换 * 2.3 代码解释与理解 * 2.4 代码重构与优化 * 第三部分:高级应用技巧 * 3.1 复杂算法实现 * 3.2 系统设计与架构 * 3.3 测试开发 * 3.4 文档生成 * 第四部分:团队协作与项目管理 * 4.1 代码审查辅助 * 4.2

By Ne0inhk