mitmproxy:通过重新提交的请求替换响应

mitmproxy: Replace response by resubmitted request

我尝试用再次重新提交相同请求的结果替换 mitmproxy 中的响应。这是我当前的脚本:

from mitmproxy import http
from mitmproxy import ctx

import requests
import re

class ReplaceToken:

    nextToken = ""

    def response(self, flow: http.HTTPFlow):

        m = re.search('localhost.local:8085/test/index.php/(.+?)" method=', str(flow.response.content))
        if m:
            self.nextToken = m.group(1)
            ctx.log.info("Next token: " + self.nextToken)
        
        m = re.search('Wrong token!<br>', str(flow.response.content))
        if m:
        
            flow = flow.copy()
            request = flow.request.copy()
        
            ctx.log.info("Found wrong token! Resend request with correct token...")
            request.path = re.sub("/[^/]*$", "/" + self.nextToken, flow.request.path)
            
            playback = ctx.master.addons.get('clientplayback')
            flow.request = request
            
            playback.start_replay([flow])

addons = [ReplaceToken()]

我想在响应中检测错误情况(例如错误的 CSRF 令牌)。如果存在这种情况,我想使用正确的令牌重新发送请求,等待正确的响应并用正确的响应数据替换原始响应数据。

但是,在上面的脚本中,响应没有被替换。而正确的请求只在mitmprox内部发出。

一种可能的解决方法是使用 $random-http-lib 绕过 mitmproxy 发出 HTTP 请求。喜欢下面的 PoC:

import requests
def response(self, flow: http.HTTPFlow):
    r = requests.get("http://www.example.com")
    ctx.log.info(r.text)
    flow.response.text = r.text

但是,这需要手动将原始请求的每个方面(HTTP 方法、路径、headers、body 等)“复制”到外部 HTTP 库 -我想避免。

有什么方法可以仅使用 mitmproxy 本身来实现这一点吗?

无意中(或多或少)我在 github 上发现了一个 OAuth 插件的基本概念,它完全符合我的要求:oauth-mitmproxy

所以代码看起来像这样:

def response(self, flow: http.HTTPFlow):

    m = re.search('localhost.local:8085/test/index.php/(.+?)" method=', str(flow.response.content))
    if m:
        self.nextToken = m.group(1)
        ctx.log.info("Next token: " + self.nextToken)
        
    if flow.is_replay:
        self.postponed_flow.response = flow.response
        self.postponed_flow.resume()
        return
    
    m = re.search('Wrong token!<br>', str(flow.response.content))
    if m:
    
        ctx.log.info("Found wrong token! Resend request with correct token...")
        
        resend_flow = flow.copy()
        resend_flow.request.path = re.sub("/[^/]*$", "/" + self.nextToken, flow.request.path)
        ctx.master.commands.call("replay.client", [resend_flow])

        flow.intercept()
        self.postponed_flow = flow

这里的“技巧”是拦截并存储错误状态流 (self.postponed_flow)。然后完整的流程被克隆、改编和重播。如果检测到重放流,则将响应复制到原始流并恢复流。