# 逆转！MitmProxy + WireGuard 中间人代理抓包

## 概述

 WireGuard 可以创建点对点连接，让客户端在外网访问Nas的内网应用，那么我们把思路逆转过来，Nas 的内网当然也可以获取客户端的访问流量。

>[!summary]  摘要
>本文主要介绍如何通过 MitmProxy 配合WireGuard 抓取客户端的访问流量，并以解析某考试App为例，说明如何使用 python 程序解析流量，并修改返回结果。

## MitmProxy

MitmProxy 是一个开源的中间人代理（Man-in-the-Middle Proxy），用于 HTTP 和 HTTPS 流量的捕获、修改和分析。它允许开发者、测试人员或安全专家查看、修改并调试客户端与服务器之间的通信。常见的用途包括：

1. **流量拦截和分析**：可以监控和分析通过代理的 HTTP/HTTPS 请求和响应。
2. **修改流量**：实时修改请求或响应内容，例如修改响应的 JSON 数据，或者拦截请求以模拟不同的条件。
3. **调试和测试**：对 API 请求和 Web 应用进行调试，捕捉和测试不同的网络请求。
4. **SSL/TLS解密**：MitmProxy 能通过生成自己的 SSL 证书来解密 HTTPS 流量，提供清晰的内容查看。

它支持命令行界面（CLI）、Web界面和 Python 脚本接口，可以高度自定义操作和扩展功能。

当然，也有一些功能比较类似的软件，比如 BurpSuite、Fiddler、TcpDump 等。

- BurpSuite 和 Fiddler 桌面端更为常见，很少在 Docker 上面部署
- TcpDump 在Linux 更为常见，可以在 Docker 上面部署，但是不支持 SSL/TLS 解密，所以获取到的数据包都是加密的

MitmProxy 这一类的软件直接部署在公网上面其实并不安全，因为其他人也可以通过 MitmProxy 发送流量来攻击设备，所以将 MitmProxy 和 WireGuard 配合使用，部署在内网中是比较合适的。

MitmProxy 原生就支持 WireGuard 模式，并且已经实现了对 WireGuard 协议流量的解析，部署起来相当方便。

### 部署 Docker 镜像

```dockerfile
docker run -itd --name mitmproxy --restart always \
-v /PATH/mitmproxy/mitmproxy:/home/mitmproxy/.mitmproxy \
-v /PATH/mitmproxy/scripts:/scripts \
-p PORT1:8081 \
-p PORT2:51820/udp \
mitmproxy/mitmproxy \
mitmweb \
--web-host 0.0.0.0 \
--mode wireguard \
-s /scripts/main.py
```

MitmProxy 有三种工具，详见[网页](https://docs.mitmproxy.org/stable/overview-getting-started/)，我这里使用了 `mitmweb`，使用`mitweb`时，需要映射`8081`端口用来访问 Web 界面。

- `mitmproxy`（命令行模式）提供了一个交互式的命令行界面（CLI）。它允许用户实时查看、修改、重放 HTTP/HTTPS 流量。
- `mitmweb`（Web 界面模式）Web 界面版本，提供了一个基于浏览器的图形化界面来查看和修改流量。
- `mitmdump`（批量模式）类似于 TcpDump，它以非交互式方式捕获和处理流量，并将数据输出到命令行或保存到文件中。

MitmProxy 有几种运行模式，详见[网页](https://docs.mitmproxy.org/stable/concepts-modes/)，我这里使用的是 WireGuard 模式，使用该模式时，需要映射`51820/udp`端口用来代理，并且映射的端口要能够公网访问。使用其他模式时，使用的是`8080`端口作为代理。

在此模式下，MitmProxy 运行内部 WireGuard 服务器，设备可以使用标准 WireGuard 客户端应用程序连接到该服务器。

>[!info]  信息
>这里的`main.py`是需要自己编写的，详见下文。

### 连接到服务器

使用浏览器访问`mitmweb`界面，可以在`Capture`页面找到二维码，扫码即可添加连接

![](https://img.papergate.top:5000/i/2025/01/677a5a5744421.webp)

在容器的`/home/mitmproxy/.mitmproxy`文件夹下也能找到`wireguard.conf`记录了客户端的公钥和私钥，已经在构建容器时映射到外部了。

### 安装证书

手机连接 MitmProxy 服务器后访问页面，选择合适的证书下载并安装

```text
http://mitm.it
```

`通用 -> VPN与设备管理 -> mitmproxy`

![](https://img.papergate.top:5000/i/2025/01/677a57f48846f.webp)
`通用 -> 关于本机 -> 证书信任设置 -> 针对根证书启用完全信任`

![](https://img.papergate.top:5000/i/2025/01/677a584fb87e7.webp)

### 编写处理程序

以实现解析某考试 App 为例，讲述如何编写程序，我们的目标是获取到该 App 返回的题目数据，并且把正确的选项内容改成`选我`，错误的选项内容改成`-`

#### 获取数据格式

先获取数据格式，可以看到，返回的数据是 Json 格式

![](https://img.papergate.top:5000/i/2025/01/677a5fb84616f.webp)

分析数据，选项主要存储在`OPTION_CONTENT`和`QUESTION_OPTIONS_CONTENT`

#### 编写程序

随着不断使用，解析程序会越来越多，为了保证可扩展性，使用`addons`来编写程序

##### 编写`exam.py`

```python
from mitmproxy import http
import json

class Exam:
    def response(self,flow) -> None:
        # 检查请求的 URL 是否包含 'getMockQuestionList'
        if 'getMockQuestionList' in flow.request.url and flow.response.headers.get("content-type", "").startswith("application/json"):
            try:
                # 解析 JSON 数据
                data = json.loads(flow.response.content)
                
                # 修改每道题目的选项内容
                if "data" in data and "question" in data["data"]:
                    for question in data["data"]["question"]:
                        right_answer = question.get("RIGHT_ANSWERS", "")
                        option_content = question.get("OPTION_CONTENT", "")
                        options = option_content.split("|")
    
                        # 创建一个新的选项内容列表
                        modified_options = []
                        modified_content = []
                        for label in ['A','B','C','D','E','F','G','H'][0:len(options)]:
                            if label in right_answer:
                                modified_options.append(f'{label}、选我')
                                modified_content.append('选我')
                            else:
                                modified_options.append(f'{label}、-')
                                modified_content.append('-')
                        # 更新 QUESTION_OPTIONS_CONTENT 为修改后的选项
                        question["OPTION_CONTENT"] = "|".join(modified_options)
                        question["QUESTION_OPTIONS_CONTENT"] = "|".join(modified_content)
                
                # 将修改后的数据重新赋值给响应内容
                flow.response.content = json.dumps(data, ensure_ascii=False).encode("utf-8")
            except Exception as e:
                print(f"Error processing response: {e}")


addons = [
    Exam()
]

```

##### 扩充`main.py`

```python
from exam import Exam

addons = [
    Exam()
]
```

#### 运行结果

![](https://img.papergate.top:5000/i/2025/01/677a62f0148a5.webp)

![](https://img.papergate.top:5000/i/2025/01/677a63f87dec6.webp)

正确答案一目了然，轻松获得了 100 分。

---

> 作者: Aphros  
> URL: https://blog.papergate.top/posts/%E9%80%86%E8%BD%ACmitmproxy--wireguard-%E4%B8%AD%E9%97%B4%E4%BA%BA%E4%BB%A3%E7%90%86%E6%8A%93%E5%8C%85/  

