最近的一個滲透項目中,網站的流量用到了AES+SM4 兩層加密算法加密傳輸。遇到傳輸加密的網站,通常是沒辦法上漏掃的。奈何手工測試效率太低,故研究了加密傳輸場景下滲透測試通用解決思路。

如何加解密傳輸流量?

這里通常是有兩種方式來實現。

用Python重新實現加解密算法

如果網站的加密算法是標準的加密算法如AES等,可以直接用Python的pycrypto模塊原生實現。遇到相對簡單的加密算法也可以把js中的加解密代碼摳出來,用Python的execJs執行得到結果。

例如用Python手動實現網站的加解密代碼如下:

這種方式只能實現簡單的加密算法,而且需要寫代碼處理相對麻煩。

JSRPC 轉發加解密函數

如果遇到復雜的加解密算法,比如某些在標準算法上魔改的算法,根本沒法從JS中扣出關鍵代碼的場景,就非常適合jsrpc的方式直接調用瀏覽器中的加解密函數。


https://github.com/jxhczhl/JsRpc


既然網站的傳輸內容是加密的,所以就需要在流量進入被動掃描器之前把流量先進行解密。

Mitmproxy 對流量進行加解密

mitmproxy 就是用于 MITM 的 proxy,MITM 即中間人攻擊(Man-in-the-middle attack)。用于中間人攻擊的代理首先會向正常的代理一樣轉發請求,還能適時的查、記錄其截獲的數據,或篡改數據,引發服務端或客戶端特定的行為。不同于 fiddler 或 wireshark 等抓包工具,mitmproxy 不僅可以截獲請求幫助開發者查看、分析,更可以通過自定義腳本進行二次開發。

上面提到可以用Python重新實現加解密算法,但是在流量進入到被動掃描器之前,需要有一個中間人來做加解密操作,而mitmproxy就充當了這個角色。可以把Python實現加解密的代碼放在mitmproxy中運行,在系統中安裝信任mitmproxy的證書,讓mitmproxy解密https的流量后再進行二次解密。

mitmproxy命令行

第一個mitmproxy的命令行需要設置上游代理為xray監聽的ip和端口。


mitmdump -s code.py -p 8010 --set block_global=false --mode upstream:http://xray-ip:port --ssl-insecure

第二個mitmproxy的命令行是直接轉發給目標網站的。


mitmdump -s code.py -p 8020 --set block_global=false --ssl-insecure

mitmproxy 代碼模版

由于流程中涉及到4次mitmproxy的加解密,所以要開啟兩個mitmproxy監聽端口來轉發流量。


第一次流量加解密代碼模板


from mitmproxy import http
# 加解密函數可以用Python重新實現,也可以用jsrpc直接轉發加解密函數接口
# 解密函數def myEncrypt(data):    pass
# 解密函數def myDecrypt(data):    pass
# 對 瀏覽器 請求內容進行解密,轉發給 掃描器def request(flow: http.HTTPFlow) -> None:    if flow.request.pretty_host != "target.com":        return          # 如果不是目標域名,直接轉發不做任何操作    try:        param = flow.request.content.decode()        decryptData = myDecrypt(param)
        print("第一次請求解密:\n"+decryptData)
        # 將解密后的報文替換加密報文        flow.request.content = decryptData
    except Exception as e:        print(f"報文解密失敗: {e}")
# 對 掃描器 響應內容進行加密,轉發給 瀏覽器def response(flow: http.HTTPFlow) -> None:    if flow.request.pretty_host != "target.com":        return          # 如果不是目標域名,直接轉發不做任何操作    try:        param = flow.response.content.decode()          encryptData = myEncrypt(param)
        print("第一次響應加密:\n" + encryptData)
        # 將明文的報文加密傳輸        flow.response.content = encryptData
    except Exception as e:        print(f"報文加密失敗: {e}")


第二次流量加解密代碼模版


from mitmproxy import http
# 加解密函數可以用Python重新實現,也可以用jsrpc直接轉發加解密函數接口
# 解密函數def myEncrypt(data):    pass
# 解密函數def myDecrypt(data):    pass
# 對 掃描器 請求內容進行加密,轉發給 網站def request(flow: http.HTTPFlow) -> None:    if flow.request.pretty_host != "target.com":        return          # 如果不是目標域名,直接轉發不做任何操作    try:        param = flow.request.content.decode()          encryptData = myEncrypt(param)
        print("第二次請求加密:\n" + encryptData)
        # 將明文報文替換為加密報文        flow.request.content = encryptData
    except Exception as e:        print(f"報文加密失敗: {e}")# 對 網站 響應內容進行解密,轉發給 掃描器def response(flow: http.HTTPFlow) -> None:    if flow.response.pretty_host != "target.com":        return          # 如果不是目標域名,直接轉發不做任何操作    try:        param = flow.response.content.decode()        decryptData = myDecrypt(param)
        print("第二次請求解密:\n"+decryptData)
        # 將加密報文替換為明文報文        flow.response.content = decryptData
    except Exception as e:        print(f"報文解密失敗: {e}")

xray的配置修改

因為在xray的前后都有mitmproxy對流量進行加解密,所以xray只需要配置上游代理即可。但是需要修改兩處配置,http和upstream_proxy都要改為mitmproxy的ip端口。

實戰測試

雖然加密傳輸場景下的滲透測試流程上比較復雜,但是流量本身自帶加密,waf也攔截不了,也省了些麻煩事。

實戰中使用上面我提供的mitmproxy模版并替換加解密函數,就可以對加密傳輸的網站直接上漏掃了,收工!