事件站点
aHR0cHM6Ly9sZWFybjEuY2NiZnQuY29tL2NmdHUvbS9jYW1wLw==
系统环境
mitmdump --version
Mitmproxy: 9.0.1 binary
Python: 3.11.0
OpenSSL: OpenSSL 3.0.7 1 Nov 2022
Platform: Windows-10-10.0.22624-SP0
原委
有个被称为tktk的防重放机制,通过分析还原了其签名方法。
原借助mitmproxy加载脚本,从而自动计算每个请求的防重放签名,但奇怪的是计算正确的签名无法通过校验,每次请求后只要让mitmproxy重新加载脚本(外部编辑器打开该脚本,ctrl+s,mitmproxy会监听到外部改变自动重新加载)后,再次从burp的repeter中send验签则通过。
测试脚本
import json
import requests
import base64
import urllib.parse
import random
import hashlib
import time
import base64
sha256_hash = hashlib.sha256()
servertime_url = 'aHR0cHM6Ly9sZWFybjEuY2NiZnQuY29tL2NmdHUvYXBpL2F1dGgvc2VydmVydGltZQ=='
def get_server_time():
res = requests.get(base64.b64decode(servertime_url)).json()
return {'secret':res['data']['secret'],'timestamp':res['data']['timestamp']}
# 修改请求
def request(flow):
if(flow.request.headers.get('s-t') is not None):
ser_time = get_server_time()
xts = ser_time['timestamp']
sec = ser_time['secret']
#hex(random.getrandbits(128))[2:34] '11111111111111111111111111111111'
nonce = hex(random.getrandbits(128))[2:34]
cts = str(int(time.time() * 1000))
st = sec[0:5] + xts + sec[5:8] + nonce + sec[8:10] + cts + sec[10:]
sha256_hash.update(st.encode('utf-8'))
flow.request.headers['x-ts'] = xts
flow.request.headers['nonce'] = nonce
flow.request.headers['c-ts'] = cts
flow.request.headers['s-t'] = sha256_hash.hexdigest()
问题
算法正确的情况下,签名计算错误,每次重新加载脚本时正确。
解决
根本原因是hash对象的update方法是串联的,即:
sha256.update(a)
sha256.update(b)
等于sha256(a+b)
因此上述脚本使用同一个sha256对象,update每一次执行都串联上次的待hash串,故导致后续请求的签名错误,而需每次加载脚本则重新创建sha256对象,即重置了hash的待hash串。属于方法的误用。脚本修改如下:
def get_sha256(data:str):
sha256 = hashlib.sha256()
sha256.update(data.encode('utf-8'))
return sha256.hexdigest()
# 省略中间代码,
flow.request.headers['s-t'] = get_sha256(st.encode('utf-8'))