分析 function k(e) 可知,是要对 r 参数进行签名的,r 又跟e、t、a、n 这几个参数相关,因此要搞清楚这几个参数的值是怎么来的,所有就在函数开始的第一行就下断点来跟进。
1 2 3 4 5 6 7 8 9 10 11 12
functionk(e) { const t = g() , n = m(); let a; a = e ? b(e) : {}; const r = e ? `${c.a.stringify(a.hasParams)}&${t}&time=${n}` : `&${t}&time=${n}`; return e = Object.assign({}, a.params, { sign: h()(decodeURIComponent(r)), time: n }), e }
首先是 t,跟进 g 函数,执行到 return 就可以发现 t = 年 + “5616” + 月 + 日
然后就是 n,这个 e 的值呢,根据跟进可知是 cookie 里面的,不过这个 e 在本案例的作用不大,最后通过 return 就可以知道返回的值就是:当前时间戳- e
接下来就是 a,根据返回的结果可知 a 的值和 e 相同,通过分析数据包可知,e的内容即请求参数(GET)或请求体(POST)
然后 r 的值就出来了,也就是签名的明文
综上所述 r = 请求内容 + & + t + & + n(这里的请求内容需要注意的是,POST Data是Json格式的要转换为:key1=value1&key1=value1…) 其中 t = 年 + “5616” + 月 + 日 n = 当前时间戳(本案例可不减e也可成功)
0x02 分析加密算法
将断点打在签名代码行,跟进代码的执行,发现加密的类名是 Md5,所有可以计算 r 的 md5 值,与代码执行的 sign 结果进行比较。
defrequest(self, flow): if flow.request.method == "POST": ctx.log.info(f'\n原POST请求体:{flow.request.text}') data = json.loads(flow.request.get_text()) if data != '{}'and'sign'in data: del data['sign'] del data['time'] for k in data: self.plaintext += f"{k}={str(data[k])}&" timestamp = int(time.time() * 1000) self.plaintext += f"{self.today}&time={timestamp}" self.md5()
data["sign"] = self.new_sign data["time"] = timestamp flow.request.set_text(json.dumps(data).replace(' ', '')) ctx.log.info(f'\n新POST请求体:{flow.request.text}') self.plaintext = '' else: ctx.log.info('无参数,无需改签') elif flow.request.method == "GET": ctx.log.info(f'\n原GET请求体:{flow.request.query}') query = flow.request.query if query != '{}'and'sign'in query: del query['sign'] del query['time'] if query: for k in query: self.plaintext += f"{k}={str(query[k])}&" else: self.plaintext = "&" timestamp = int(time.time()) * 1000 self.plaintext += f"{self.today}&time={timestamp}" self.md5()