起因为实战中遇到的一个站点,请求参数使用 js 前端加密,即使加密过程很简单 但仍希望有一个中间 hook 能直接看到明文,如果能接入 sqlmap 直接测试(免去写 tamper)的过程就更好了,于是有了本文

* 均已脱敏,所以可能看起来比较简单?whatever

*060923(金):增加部分内容

*070423(火):过于铸币,今天才想起来要 push

前置

拾起远古 js 逆向技巧,f12 大法伺候之下理清加密逻辑,从 encrypt.js 中抠出可以单独执行的加密函数 jscode 以便被 execjs 调用

把加密函数替换为空,其余不变另存一份 encrypt2.js

yakit

我们希望能在 burpsuite 中直接看到明文来方便调试,让我们用 yakit 来做到这一点:

  1. 准备 jscode
  2. yakit 脚本编写:劫持 encrypt.js 响应,让其返回的 encrypt 函数为明文(encrypt2.js);劫持 /xyz.action 且带 data 的 POST 请求,将 body 明文参数的 name 和 value 分别用 jscode 做加密处理
  3. 配置 burpsuite 上游代理为 yakit,yakit 下游代理为最外层代理(可选)

yak 脚本示例:

func encrypt(name){
	jscode = `...function encrypt(){}...`
	encoded, _ = js.CallFunctionFromCode(jscode,"encrypt",name)
    return encoded, _
}

hijackHTTPRequest = func(isHttps, url, req, forward /*func(modifiedRequest []byte)*/, drop /*func()*/) {
    urls := ["xyz", "qwe"]
    if str.Contains(string(url), ".action"){
        sUrl := str.Split(str.Split(string(url),".action")[0],"/")[-1]
        for url in urls{
            if str.Contains(sUrl,url){
                freq = fuzz.HTTPRequest(req)~
                tmp := ""
                for param in freq.GetPostQueryKeys(){
                    value,_ := codec.UnescapeQueryUrl(freq.GetPostQueryValue(param))
                    param,_ := codec.UnescapeQueryUrl(param)
                    value0,_ = encrypt(value)
                    param0,_ :=  encrypt(param)
                    trmp := codec.EscapeQueryUrl(string(param0)) +"="+ codec.EscapeQueryUrl(str.ReplaceAll(string(value0),"+"," "))
                    tmp = tmp+"&"+trmp
                }
                modifiedBytes := freq.FuzzPostRaw(str.TrimLeft(tmp,"&")).FirstHTTPRequestBytes()
                forward(modifiedBytes)
            }
        }
    }
}

hijackHTTPResponse = func(isHttps, url, rsp, forward, drop) {
    a1 = b`...encrypt2.js...`
    if str.Contains(string(url), "encrypt.js") {
        modified = poc.FixHTTPResponse(a1)
        forward(modified)
    }
}

经过这样的劫持,请求包会呈现这样的流向:

  • 原本
浏览器 POST /xyz.action 加密参数(由encrypt.js加密)
-> burpsuite 拦截加密参数和请求
-> 发送加密请求
-> burpsuite得到响应 浏览器得到响应
  • 修改后
浏览器 POST /xyz.action 明文参数(由修改后的encrypt2.js返回明文)
-> burpsuite 拦截明文参数和请求
-> 发送给上游代理yakit
-> yakit 拦截明文参数和请求 做加密处理
-> 发送加密请求
-> yakit得到响应 burpsuite得到响应 浏览器得到响应

这样 burp 中可以看到明文请求和正常响应,方便我们爆破和其他测试(比如接入 burp, xray 等等)

mitmproxy

但话说回来,yakit 本身是一个比较庞大的类 burp 软件,编写脚本还必须使用 yaklang,我更倾向于使用 mitmproxy 来做相同的事情,毕竟用 python 写是再轻松不过了~

mitmproxy 为每一种连接方式(http, socks….)提供了 5 个生命周期(修改阶段),体现在代码里就是我们可声明的函数

  • requestheaders:仅读取 headers,此时 body 为空
  • request:读取到 request 全文;注意如果 streaming 传输开启,劫持在 stream 之后发生
  • responseheaders:仅读 headers,body 为空
  • response:读取 response 全文;注意如果 streaming 传输开启,劫持在 stream 之后发生
  • error:http error
  • http_connect
  • http_connect_upstream

简单举例:

import execjs


def encrypt(var):
    with open('encrypt.js', 'r', encoding='utf-8') as f:
        ctx = execjs.compile(f.read())
    encoded_var = ctx.call('encode', var)
    return var


class Modify:
    def request(self, flow):
        if flow.request.url.startswith('...'):
            if flow.request.urlencoded_form:	# 此处的urlencoded_form是[(name, value), (...)]
                data = []
                for i in flow.request.urlencoded_form:
                    data.append((encrypt(i), encrypt(flow.request.urlencoded_form[i])))
                flow.request.urlencoded_form = data


addons = [
    Modify()
]

使用:

pip install mitmporxy
# $env:all_proxy = "socks5://127.0.0.1:6005" # 可选
mitmdump -p 8085 -s addon.py	# 8085给burp做上游代理

然而实际编写符合要求的脚本时就陷入了困难 —— 官方文档简直是 ****,让人不忍卒读…… 这里推荐这份文档:Mitmproxy-Document,对每一个接口、参数如何设置都写的很详细,五星好评

image-20230609174248050

实测 mimt 可以为 xray 做上游代理进行正常测试,但是会比直接挂 xray 速度会慢一些 始终有 pending 的部分(但谁让 mimt 操作简单呢,我单方面宣布与这一缺点和解!

实例

日常测站遇到的实例,参数被写死的 key 进行 AES 加密,返回内容也可用固定的 key 解密,直接抠出来替换(加密过程非常非常简单)

image-20230609175026087

这里遇到了第一个坑点:jsencrypt 库是针对前端环境的,支持一种很奇怪的 AES Public Key 格式(126 位),无法 b64 解码 也不含 -----PUBLIC 这种标配的头,但是可以被正常使用,然而 nodejs-jsencrypt 库却不支持这种格式!!!!!所以需要单独为 jsencrypt 补上运行环境(见最上两行代码)

之后就是把网页加载的 含有加密部分的代码换为空,让其返回明文,再编写 mimt 脚本

image-20230609175636518

第二个小坑点是 mimt 修改 query 参数竟然会自动进行 urlencode…… 我还改了半天,结果删掉 quote() 就好了(是我自作多情了)

然后就可以用 burp 愉快的测站啦 qwq