Flatt Security Blog

株式会社Flatt Securityの公式ブログです。プロダクト開発やプロダクトセキュリティに関する技術的な知見・トレンドを伝える記事を発信しています。

株式会社Flatt Securityの公式ブログです。
プロダクト開発やプロダクトセキュリティに関する技術的な知見・トレンドを伝える記事を発信しています。

無料プロキシツール「mitmproxy」を使ってみよう - セットアップ方法とセキュリティエンジニアおすすめの設定

※本記事は筆者styprが英語で執筆した記事を株式会社Flatt Security社内で日本語に翻訳したものになります。

はじめに

こんにちは、Flatt Securityのstypr(@stereotype32)です。今回はセキュリティ診断などで使われるローカルプロキシツールについて紹介します。

ちなみに、開発者の皆さんが脆弱性の検証を行うにはこれらのツールだけでなくセキュリティ知識が必要ですが、そのためにはFlatt Securityが提供する「KENRO」がおすすめです。Web アプリケーションの代表的な脆弱性10個に関して、脆弱なソースコードを修正するなどのハンズオンを通して学ぶことができます。

ぜひバナーより無料・無期限のトライアルをご利用ください。

さて、セキュリティエンジニアの多くは、WebやモバイルアプリケーションのHTTP/HTTPSトラフィックを確認するするためにBurp Suite Professionalを使っていますが、Burp Suite Professionalは少し重くて高価だったので、私は数年前からmitmproxyを使っています。

また、Burp Suite で利用できる機能は、私にとってはtoo muchでした。

以下は、私がmitmproxyを使い続けている理由の一部です。

  1. 完全に無料で使用できる。職場ではBurp Suite Professionalを使用していますが、自分の研究やCTFにはmitmproxyを使用する方が好きです。
  2. Pythonで追加のプラグインやスクリプトを書くことができる。
  3. Webインターフェース(mitmweb)がデフォルトでサポートされているので、お気に入りのクラウドプロバイダや職場のマシンにmitmproxyをインストールし、Webインターフェースを使って少ない労力でトラフィックを確認することができる。
  4. いつでもどこでもツールを使うことができる。クラウドプロバイダにインストールできるため、PCにこのツールをインストールする必要はありません。この設定は、一度すべてを設定し終えると、とても便利なものになります。

​​

この記事では、私が個人的なユースケースで使用しているmitmproxyのインストールとデプロイ方法を説明します。Python に慣れている人なら、きっとこのツールを気に入ることでしょう。

この記事では、mitmproxy を使ってリクエストとレスポンスを変更する方法についても説明します。また、あなたのペネトレーションテストやアプリケーションのセキュリティ診断で使用するのに非常に役立つであろういくつかの例も紹介します。

セットアップとインストール

サーバーのセットアップ

サーバーのセットアップには、ニーズに応じて様々な方法があります。

私の場合は、OpenVPNを使用して下図のような設定をしています。この記事では、以下の図のような設定を行う方法を説明します。いくつかのステップをスキップして、OpenVPN の設定をせずに mitmproxy を使用することもできます。

Flutterのようなフレームワークは、デフォルトのシステムプロキシ設定を回避する独自のHTTP/HTTPSラッパーを持っているので、OpenVPNベースの設定を使用することをお勧めします。

したがって、この記事のセットアップでは、すべてのHTTPとHTTPSのパケットをネットワークレイヤーからmitmproxyに転送します。そうすることで、デバイスからプロキシ設定を手動で上書きする必要がなくなります。

もちろん、フレームワークの設定を上書きする他の代替回避策もあります。しかし、デバッグの利便性を考えると、この方法にしておくことをお勧めします。

この記事では、上記の設定の回避策を、お気に入りのクラウドプロバイダのインスタンスの1つに設定することを想定して説明します。

本記事では、Ubuntu Server 22.04を使用してインストールを行う予定です。

OpenVPNのインストール

mitmproxyのテストと実行だけを計画している場合は、OpenVPNのインストール設定をスキップして、直接mitmproxyのインストールを開始してください。

まず、システムに OpenVPN をインストールするところから始めます。 angristan/openvpn-install のようなプロジェクトがあり、あなたのためにインストールを支援してくれます。

インストールは簡単です。以下のコマンドを順番に実行していくだけです。

# curl -O https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 40586  100 40586    0     0   136k      0 --:--:-- --:--:-- --:--:--  137k
# chmod +x openvpn-install.sh
# ./openvpn-install.sh

プロンプトが長くなりすぎたので、いくつかのポイントを切り出しましたが、ニーズに応じてセットアップをカスタマイズしてください。今回は、以下のようなセットアップを使用します。

...
IP address: (Your public IP address)

...
   1) Default: 1194
   2) Custom
   3) Random [49152-65535]
Port choice [1-3]: 2
Custom port [1-65535]: 1337

...
   1) UDP
   2) TCP
Protocol [1-2]: 1

...
   1) Current system resolvers (from /etc/resolv.conf)
   2) Self-hosted DNS Resolver (Unbound)
   3) Cloudflare (Anycast: worldwide)
   4) Quad9 (Anycast: worldwide)
   5) Quad9 uncensored (Anycast: worldwide)
   6) FDN (France)
   7) DNS.WATCH (Germany)
   8) OpenDNS (Anycast: worldwide)
   9) Google (Anycast: worldwide)
   10) Yandex Basic (Russia)
   11) AdGuard DNS (Anycast: worldwide)
   12) NextDNS (Anycast: worldwide)
   13) Custom
DNS [1-12]: 3

...
Enable compression? [y/n]: n

...
Customize encryption settings? [y/n]: n

...
Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
read EC key
writing EC key
Enter PEM pass phrase: (Enter your password)

...
Client name: mitm

...
   1) Add a passwordless client
   2) Use a password for the client
Select an option [1-2]: 1

...
Using configuration from /etc/openvpn/easy-rsa/pki/easy-rsa-37476.GAqkcY/tmp.VtfsIr
Enter pass phrase for /etc/openvpn/easy-rsa/pki/private/ca.key: (Enter your password)

...
Client stypr added.

The configuration file has been written to /root/mitm.ovpn.
Download the .ovpn file and import it in your OpenVPN client.

これでインストールは完了です。クライアントファイルは、/root/ に用意されます。このファイルは、後にこのチュートリアルで使用します。

OpenVPN利用のためのDNSのセットアップ

これから、DNSサーバーのインストールとDNS設定の調整を続けます。

この作業は必須ではありませんが、ドメイン名のリダイレクトなど、高度な設定を行う場合に推奨されます。

まず始めに、VPNネットワークのアドレスを知っておく必要があります。この記事では、ホストの IP は 10.8.0.1 に設定されています。このIPは、どのディストリビューションで作業しているかによって変わるかもしれません。ほとんどの場合、OpenVPN はデフォルトで 10.8.0.1/24 に設定されています。

# ip addr | grep tun
3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq state UNKNOWN group default qlen 500
    inet 10.8.0.1/24 scope global tun0

ここでは、DNS関連のパッケージをいくつかインストールします。なお、パッケージの名称はディストリビューションによって異なる場合があります。

# apt -y install dnsutils tcpdump bind9 dnsutils

インストールが完了したら、/etc/resolv.conf を修正して、マシンのDNSネームサーバーを変更します。今回の設定では、マシンはCloudflareのhttp://1.1.1.1/ DNSサーバーを使用することになります。お好みのエディターで設定を修正してください。

nameserver 1.1.1.1
nameserver 1.0.0.1

続いて、DNSサーバーのトラフィックをCloudflareのDNSに転送するように、/etc/bind/named.conf.options を変更します。

...
        forwarders {
                1.1.1.1; // Cloudflare server
                1.0.0.1; // Cloudflare server
         };
...
        listen-on { 10.8.0.1; }; // Enter your OpenVPN's IP.
        // listen-on-v6 { any; };
...

最後に、OpenVPNサーバーの設定 /etc/openvpn/server.conf を変更して、DNSサーバーを変更します。

...
push "dhcp-option DNS 10.8.0.1"
# push "dhcp-option DNS 1.0.0.1"
# push "dhcp-option DNS 1.1.1.1"
push "redirect-gateway def1 bypass-dhcp"
...

最後に、DNSサーバーを再起動して設定を適用します。

# service bind9 restart
# service openvpn restart

mitmproxyのインストールと実行

mitmproxyのインストール

mitmproxyのインストールは非常に簡単です。

python をインストールし、pip を使って mitmproxy をインストールするだけです。他のディストリビューションへのインストールについては、mitmproxy.org - Installation で詳細を確認することもできます。

# apt -y install python3-pyasn1 python3-flask python3-dev python3-urwid python3-pip libxml2-dev libxslt-dev libffi-dev
# pip3 install mitmproxy
# mitmproxy --version
Mitmproxy: 8.1.1
Python:    3.10.4
OpenSSL:   OpenSSL 3.0.5 5 Jul 2022
Platform:  Linux-5.15.0-43-generic-x86_64-with-glibc2.35

mitmproxyの実行(OpenVPN無し)

以下のようなコマンドを使用するだけで、http://localhost:8000/ にアクセスすることもできます。

mitmweb \
  --web-port 8000 \
  --web-host 0.0.0.0 \
  --listen-host 0.0.0.0 \
  --listen-port 8080 \
  --mode transparent \
  --ssl-insecure \
  --no-web-open-browser

mitmproxyの実行(OpenVPN使用)

いくつかのことを調整するために、bashスクリプト(/srv/start.sh)を作成しましょう。

今回のスクリプトでは、以下のようなことを行っています。

  1. 80443 をすべて mitmproxy のリッスンポート 9999 にリダイレクトしています。iptables でさらにポートを追加することができます。
  2. 10.8.0.1 は OpenVPN からの私の IP です (ip addr | grep tun)。これを変更する必要があります。
#!/bin/sh
iptables -t nat -F
iptables -t nat -A PREROUTING -i tun0 -p tcp --dport 80 -j REDIRECT --to-port 9999
iptables -t nat -A PREROUTING -i tun0 -p tcp --dport 443 -j REDIRECT --to-port 9999
mitmweb \
  --web-port 8000 \
  --web-host localhost \
  --listen-host 10.8.0.1 \
  --listen-port 9999 \
  --mode transparent \
  --ssl-insecure \
  --no-web-open-browser

では、スクリプトファイルを実行可能にして、アプリケーションを実行してみましょう。

# chmod +x /srv/start.sh
# /srv/start.sh
Web server listening at http://localhost:8000/
Proxy server listening at 10.8.0.1:9999

すべてが正しく設定されていれば、問題なく動作するはずです。

nginxのインストール

プライベートIPアドレスでmitmproxyを使うだけならnginxをインストールする必要はありませんが、パブリックIPで使う予定があるなら、nginxをインストールしてWeb UI用のパスワード認証を追加しておいた方が良いでしょう。

最初のステップとして、パスワードファイルを設定するためnginxとパッケージをインストールします。

# apt -y install nginx apache2-utils

インストール後、ログイン機能用のユーザーを新規に作成することができるようになります。

# htpasswd -c /etc/nginx/.htpasswd username

すべて完了したら、次に /etc/nginx/sites-enabled/default にある nginx の設定を変更します。

以下の設定をコピー&ペーストすればOKです。

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
    server localhost:8000;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /dev/null;
    index index.html;
    server_name _;

    location / {
        auth_basic "mitm server";
        auth_basic_user_file /etc/nginx/.htpasswd;

        proxy_pass http://localhost:8000/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_redirect off;
        proxy_set_header Host "localhost:8000"; # This is required..
        proxy_set_header Origin "http://localhost:8000"; # This too :(
        proxy_read_timeout 86400;
        proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
        proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
        proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
        proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
        proxy_set_header Sec-WebSocket-Accept $http_sec_websocket_accept;
    }

    location /downloads/ {
        root /var/www/html;
        autoindex on;
    }
}

設定変更が完了したら、nginxを再起動します。

$ service nginx restart

クライアントの設定を保存する

ここで、OpenVPNクライアントファイル(/root/mitm.ovpn)ファイルを/var/www/html/downloads に移動し、手軽にダウンロードできるようにします。

# mkdir -p /var/www/html/downloads
# mv /root/mitm.ovpn /var/www/html/downloads/

すべてが完了したら、今度は携帯電話やクライアントデバイスから http://(your_IP)/downloads/(your_name).ovpn にアクセスして OpenVPN クライアント設定をダウンロードすることができます。

mitmproxyを利用する

OpenVPNをインストールしていない場合は、このステップはスキップして、直接プロキシ設定をすればOKです。

サーバーの設定が終わったら、AppStoreかPlayストアからOpenVPN Connectをインストールするだけです。

ダウンロードしたら、ブラウザから http://(your public IP address)/downloads/(your_name).ovpn にアクセスして VPN 設定をダウンロードし、OpenVPN Connect で設定を追加します。

CA証明書のインストール方法はBurp Suiteとほぼ同じで、http://mitm.it/ から証明書をインストールできます。この記事では、CA証明書のチェックを迂回する方法は省略します。

インストールが成功したら、Web コンソールから HTTP/HTTPS リクエストが正しくログに記録されているかどうかを確認します。

mitmproxyの活用

Webコンソール(mitmweb)の基本的な使い方

Web GUIはかなりシンプルでわかりやすいです。

リクエストを再送する場合は、「Requests」タブでヘッダやボディを追加し、replayボタンをクリックすればよいです。

また、フィルタを活用して、必要なものを絞り込むこともできます。公式ドキュメントから構文を確認することができます。

スクリプトを書く

mitmproxy は、スクリプトを追加する -s フラグをサポートしています。これは、基本的に mitmproxy の上に python のコードを追加して書くことができることを意味します。

mitmproxy に関する詳細な情報は、このリンクを読むことを強くお勧めします。

https://docs.mitmproxy.org/stable/addons-scripting/

ありがたいことに、mitmproxy には、テスト用のサンプルスクリプトがたくさんあります

srv/start.sh-s フラグとスクリプトファイル名を追加することで、独自のスクリプトを追加することができます。

mitmweb \
  --web-port 8000 \
  --web-host 0.0.0.0 \
  --listen-host 0.0.0.0 \
  --listen-port 8080 \
  --mode transparent \
  --ssl-insecure \
  --no-web-open-browser \
  -s proxy.py

すべてのHTTPレスポンスに対応するvConsoleを追加する

個人的にはモバイルアプリのWebViewでのデバッグを便利にするために Tencent/vConsole を使っていますが、それらによってmitmproxyは非常に便利になります。

Burp Suite でもそのようなスクリプトを書くことはできますが、mitmproxy の方が、必要に応じて機能を追加するための簡単な python スクリプトを書くのが楽だと感じています。

このスクリプトは、すべての html ページに vconsole.js を挿入するので、デバッグが簡単になります。

# wget https://unpkg.com/vconsole/dist/vconsole.min.js

vconsole.min.js をダウンロードしたら、vconsole.py を作成し、以下のコードを書きます。

#!/usr/bin/python3 -u

"""
vconsole.py

Add vConsole for all HTTP responses
"""

from mitmproxy import http

# Load vConsole - https://unpkg.com/vconsole/dist/vconsole.min.js
CONSOLE_JS = ""
with open("vconsole.min.js", encoding="utf-8") as f:
    CONSOLE_JS = f.read()


def request(flow: http.HTTPFlow) -> None:
    """
    Load cached vconsole.min.js
    """
    if flow.request.path == "/__script/vconsole.min.js":
        flow.response = http.Response.make(
            200,
            CONSOLE_JS,
            {"Content-Type": "text/javascript"}  # (optional) headers
        )

def response(flow: http.HTTPFlow) -> None:
    """
    Append vConsole script for all text/html responses
    """

    _vconsole = """
        <style>
            .vc-switch { z-index: 9999999 !important; }
        </style>
        <script async>
        initVConsole = () => {
            window.vConsole = new window.VConsole({
                defaultPlugins: ['system', 'network', 'element', 'storage'],
                maxLogNumber: 1000,
                // disableLogScrolling: true,
                onReady: () => {
                    console.log('vConsole is ready.');
                },
                onClearLog: () => {
                    console.log('on clearLog');
                }
            });
            console.log("vconsole loaded!");
        };

        loadVConsole = () => {
            script = document.createElement('script');
            script.src = '/__script/vconsole.min.js';
            script.onload = initVConsole;
            document.body.append(script);
        };

        window.addEventListener('DOMContentLoaded', (event) => {
            loadVConsole();
        });
        </script>
    </head>
    """

    # Never cache responses.
    flow.response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    flow.response.headers["Pragma"] = "no-cache"
    flow.response.headers["Expires"] = "0"
    # You can also add CSP headers to disable XSS-related settings...

    # if the response is HTML, inject script.
    is_html = False
    for key, value in flow.response.headers.items():
        if key.lower() == "content-type":
            if value.startswith("text/html"): # text/html; charset=utf-8
                is_html = True

    if is_html:
        flow.response.content = flow.response.content.replace(b"</head>", _vconsole.encode())

-s パラメータを追加することで、vConsoleスクリプトを実行することができます。

# mitmweb \
  --web-port 8000 \
  --web-host localhost \
  --listen-host 10.8.0.1 \
  --listen-port 9999 \
  --mode transparent \
  --ssl-insecure \
  --no-web-open-browser \
  -s vconsole.py

HTTPレスポンスにスクリプトを注入すると、すべてのWebページでモバイルフレンドリーなDevTools機能を利用できるようになります。

次の画像は、Flutterベースのアプリケーション上のvConsoleです。

終わりに

ぜひmitmproxyをデバッグや脆弱性検証にお役立てください。Webセキュリティの知識は「KENRO」のハンズオンで習得するのがおすすめです。

ぜひバナーより無料・無期限のトライアルをご利用ください。

また、Flatt Securityではセキュリティエンジニアが手動で検査を行うセキュリティ診断サービスも提供しています。

診断プランは柔軟に様々な形が組めますが、上記のデータが示すように、診断は幅広いご予算帯に応じて実施が可能です。ご興味のある方向けに下記バナーより料金に関する資料もダウンロード可能です。

また、Flatt Securityはセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式Twitterのフォローをぜひお願いします!

twitter.com

ここまでお読みいただきありがとうございました。