百趣云 百趣云的博客

mitmproxy 插件开发:写一个自动解密响应体的中间人

为什么要写插件而不是手动看包

mitmproxy 手动看包适合探索,但逆向工程经常需要批量处理:自动解密响应、自动提取 token、自动记录特定接口。这些靠手点不现实——mitmproxy 的插件系统(Python addon)就是干这个的。

mitmdump -s myaddon.py --listen-port 8888
# 手机代理指向 8888,所有流量经过你的 Python 代码

插件骨架:事件驱动

mitmproxy 插件是事件驱动的,常用事件钩子:

from mitmproxy import http, ctx

class DecryptAddon:
    def request(self, flow: http.HTTPFlow):
        # 请求经过时触发(发往服务器前)
        if 'api.target.com' in flow.request.pretty_host:
            ctx.log.info(f'[REQ] {flow.request.method} {flow.request.path}')
            ctx.log.info(f'[BODY] {flow.request.get_text()}')

    def response(self, flow: http.HTTPFlow):
        # 响应经过时触发(返回客户端前)
        if 'api.target.com' in flow.request.pretty_host:
            body = flow.response.get_text()
            ctx.log.info(f'[RESP] {body[:500]}')

addons = [DecryptAddon()]

实战场景一:自动解密 AES 响应

目标 App 的响应体是 AES 加密的(密钥已从 App 逆出),插件自动解密后展示:

from mitmproxy import http, ctx
from Crypto.Cipher import AES
import base64, json

KEY = b'1234567890abcdef'   # 从 App 逆出的密钥
IV = b'0102030405060708'

class AutoDecrypt:
    def response(self, flow: http.HTTPFlow):
        if 'api.target.com' not in flow.request.pretty_host:
            return
        try:
            raw = base64.b64decode(flow.response.get_text())
            cipher = AES.new(KEY, AES.MODE_CBC, IV)
            plain = cipher.decrypt(raw)
            plain = plain[:-plain[-1]]  # 去 PKCS7 填充
            data = json.loads(plain)
            # 把解密结果写回响应(App 端无感,mitmproxy 界面直接看明文)
            flow.response.set_text(json.dumps(data, ensure_ascii=False, indent=2))
        except Exception as e:
            ctx.log.warn(f'解密失败: {e}')

addons = [AutoDecrypt()]

注意:把解密结果写回响应会让 App 收到明文——如果 App 有响应完整性校验会崩。稳妥做法是只记录不改写(写日志/存文件),或只在分析阶段改写。

实战场景二:自动提取并维护 token

App 的 token 每小时刷新,手动抓包复制太蠢。插件自动提取:

import json, time

class TokenHarvester:
    def __init__(self):
        self.tokens = []

    def response(self, flow: http.HTTPFlow):
        # 登录接口的响应里提取 token
        if '/login' in flow.request.path:
            data = json.loads(flow.response.get_text())
            token = data.get('data', {}).get('token')
            if token:
                self.tokens.append({'token': token, 'time': time.time()})
                # 落盘供协议程序读取
                with open('/tmp/tokens.json', 'w') as f:
                    json.dump(self.tokens, f)

addons = [TokenHarvester()]

协议程序读 /tmp/tokens.json 就能拿到最新 token——mitmproxy 成了 token 的自动收割机。

实战场景三:请求重放与修改

class RequestModifier:
    def request(self, flow: http.HTTPFlow):
        if '/api/order' in flow.request.path:
            # 修改请求参数(测试服务端校验逻辑)
            body = json.loads(flow.request.get_text())
            body['price'] = '0.01'   # 改价测试
            flow.request.set_text(json.dumps(body))

addons = [RequestModifier()]

精髓:三个工程要点

  1. mitmdump 比 mitmproxy 适合生产。mitmproxy 是交互界面,mitmdump 是无头命令行——跑插件服务用 mitmdump,稳定且资源占用低
  2. flow 对象是完整上下文flow.requestflow.responseflow.client_connflow.error 全在,甚至可以拿到 TLS 信息(flow.server_conn.certificate_list)做证书分析
  3. 插件热重载mitmdump -s addon.py 运行中改了 addon.py 会自动重载——调试插件时不用重启,改完保存即生效

总结

  1. mitmproxy 插件 = Python 事件钩子,request/response 两个钩子覆盖 90% 需求
  2. 三大实战场景:自动解密、token 收割、请求修改
  3. 解密写回要小心完整性校验,稳妥方案是只记录
  4. 生产用 mitmdump,插件热重载加速调试

交流微信:run1255

By 百趣云 阅读量:3 On