Introduction

mitmproxy を使って透過プロキシを実現した。

今度は通信内容を覗き見て、リクエストとレスポンスを書き換えてみる。

How to do?

その前に軽く mitmproxy のショートカットキーについて。
基本は vi みたいに使う。

コマンド キー
一つ上 (下) の Flow に移動 または
FLow の詳細画面へ移動 enter
左 (右) のタブに移動 または
今見ている View から戻る q
表示する FLow をフィルタ f を入力後、フィルタリングする文字列を入力
FlowリストまたはEventLog を全部クリア z
EventLog を見る :console.view.eventlog or E

なお EventLog のログレベルは mitmproxy を起動する際に、--set console_eventlog_verbosity=debug のように引数を指定することで変更できる。

通信内容の傍受

mitmproxy を起動すると、設定が正しくできていればクライアントからの通信を mitmproxy が中間者 (Man-in-the-middle) として通信を中継している。
なので、 Flow Details を見ると復号化された状態のリクエストやレスポンスを確認することができる。

通信内容の改ざん

mitmproxy は python スクリプトを使うことで指定の条件を満たすリクエストやレスポンスを改ざんすることができる。

サンプルソースは下記。

下記は 02_ReplacePath のサンプル。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import logging

from mitmproxy import ctx

class Replacer:
def load(self, loader):
loader.add_option(
name="domain",
typespec=str,
default="",
help="target domain",
)
loader.add_option(
name="old",
typespec=str,
default="",
help="old path to be replaced",
)
loader.add_option(
name="new",
typespec=str,
default="",
help="new path",
)

def request(self, flow):
domain = ctx.options.domain
old_path = ctx.options.old
new_path = ctx.options.new

# Note: flow.request.url is ip address. why?
index = flow.client_conn.sni.find(domain)
if index == -1:
return
if not flow.request.path == old_path:
return

flow.request.path = new_path

addons = [Replacer()]

mitmproxy

  • loadmitmproxy からスクリプトが呼ばれたときに呼び出される関数
  • request はリクエストを受け取った際に mitmproxy から呼び出される関数
  • response はレスポンスを受け取った際に mitmproxy から呼び出される関数

となる。
関数内で flow の内容を書き換えたりすることで最終的な結果を書き換えることができる。

上記のサンプルは指定したドメインのとあるパスに対するリクエストを指定したパスに置換することで強制的に呼び出しを捻じ曲げる。

まず、mitmproxy に対して、呼び出すスクリプ及びスクリプトに渡す引数を --set を渡して起動。

1
$ mitmproxy -s script.py --set domain=httpbin.org --set old=/get --set new=/json

次に、クライアントから mitmproxy を起動しているサーバーをプロキシに指定して通信を実行する。
クライアントには事前に mitmproxy が発行した CA 証明をインストールしておくこと。
また、ファイヤーウォールやパケットフィルタは適宜設定しておくこと。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ curl -i -X GET https://httpbin.org/get -x http://<proxyserver>:8080
HTTP/1.1 200 Connection established

HTTP/1.1 200 OK
Date: Sun, 23 Feb 2025 15:31:53 GMT
Content-Type: application/json
Content-Length: 429
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}

https://httpbin.org/get の通信を https://httpbin.org/json に改ざんされていることがわかる。
/get 以外のリクエストや httpbin.org 以外への通信には影響がない。

Source Code