Phoenixを守る:Phoenix Contact HMIの重大な脆弱性を暴く 〜 パート2

Phoenixを守る:Phoenix Contact HMIの重大な脆弱性を暴く 〜 パート2

本連載の第 1 回で紹介したように、Nozomi Networks Labs は、Phoenix Contact Web Panel 6121-WXPS デバイス(ファームウェアバージョン 3.1.7)に 14 件の脆弱性を発見しました。調査中、我々は、このデバイスが、リモートの攻撃者に悪用され、完全に侵害される可能性のある、 いくつかの重大な問題の影響を受けることを確認した。この 2 回目のブログでは、HTTPS サービスを分析し、最も危険な脆弱性(攻撃者が認証なしで基礎となる Linux オペレーティングシステム上で OS コマンドを実行することを可能にする)を発見するためにたどったプロセスの技術的な詳細を掘り下げます。

以下は、私たちの調査から得られた主な結果である: 

  • ベンダーから提供されたデフォルトのSSH認証情報を活用して、基盤となるLinuxベースのオペレーティングシステムにアクセスすることができた。
  • HTTPS ウェブ・サービスは NodeJS ベースのアプリケーションで、パックされ難読化されていることがわかりました。一般に公開されているツールを活用するだけで、オリジナルとほぼ同じ NodeJS ソースコードを作成することができました。
  • ソースコードを入手し、静的に解析した結果、攻撃者が認証なしに root 権限で任意のコマンドを実行できる重大な脆弱性 (CVE-2023-3572) を特定することができました。

我々が発見した問題に対して、フエニックス・コンタクト社は、報告されたすべての脆弱性に対処した新しいファームウェア・リリース(v4.0.10)を作成し、これらの問題は6121-WXPSデバイスだけでなく、WP6000製品ファミリー全体に影響を及ぼすと断言した。

背景情報

最初のブログで説明したように、WP 6121-WXPSはフエニックス・コンタクト社が製造する最新のウェブベースHMI(ヒューマンマシンインターフェース)の1つです。従来、HMIは産業用制御設備内に設置され、オートメーション・ソリューションの監視システムへの主な視覚的接続として機能します。HMIを設定した後、デバイスは内蔵のウェブブラウザを使用して指定された監視システム(つまり、ローカルまたはリモートのウェブサービス)と対話し、ディスプレイを介して出力をレンダリングします。

図 1.フエニックス・コンタクトのHTTPS管理サービスのホームページ。

デバイスの底面にある物理的なイーサネット・インターフェースを介して、オペレーターはデバイス内部のウェブサービスにアクセスすることができます:

  • ネットワーク・コンフィギュレーション:デバイスのIPアドレスの割り当てと管理に使用。
  • ウェブアプリケーション:本機のウェブページの設定に使用します。
  • 運用管理:デバイスの管理に関するすべての機能(VNCやRemminaサーバー、SSL/TLS証明書など)
図 2.バックプレーン内のフエニックス・コンタクトWP 6121-WXPSインターフェース。

情報収集

フエニックス・コンタクトが提供するドキュメントを読んだ後、非特権アカウント(つまりブラウザ)と管理アカウント(つまりルート)の両方のデフォルト認証情報を見つけた。この知識を活用して、デバイスからデフォルトで公開されているSSH接続を通じてローカルのLinuxシェルにアクセスし、デバイスのネットワークサービスの詳細を調査し始めました。

図3.デバイスが公開するネットワークサービス。

デバイスのイーサネット・インターフェイスに来るHTTPSトラフィックを処理するために、デバイスがnginxサーバーを使用していることを確認した。このため、/etc/nginx/nginx.confでnginxの設定を検査した:

図4.nginx設定ファイルの内容の一部。

As we can see from the location / { ... } block of the configuration file, as soon as an HTTP request comes to the nginx, it then forwards the request to another server running on the same machine at port TCP/8080 (i.e., http://127.0.0.1:8080). We discovered that this application is the /opt/cockpit/cockpit binary.

図5.TCP/8080で公開されるネットワークサービスの詳細。

リバースエンジニアリング - 開梱

コックピット・アプリケーションは、ストリップされたELFバイナリです:

ファイル opt/cockpit/cockpit

コックピットELF 64-bit LSB 実行形式, ARM aarch64, version 1 (GNU/Linux), ダイナミックリンク, インタプリタ /lib/ld-linux-aarch64.so.1, BuildID[sha1]=6ac5477cb8643828b93162bdd8c6d60fc3dde1af, GNU/Linux 3.7.0 用, ストリップ済み

バイナリ内の文字列を活用し、JavaScriptコードの多くのスニペットが含まれていることを発見した。注目すべきは、バイナリの最後に以下の文字列があることだ:

図6.コックピット・バイナリ・ファイルの末尾にある文字列。

このような文字列のパターンは、vercel/pkgユーティリティのような一般的なNodeJSバンドルを使ってパックされた実行ファイル内で見つかるかもしれません。vercel/pkg "のドキュメントを読むと、デフォルトのパッケージ設定ファイル(つまりpackage.json)を使用した場合、このユーティリティは以下のような動作をすることがわかりました(こちらを参照):

  • アプリケーションのJSコードをV8コンパイラでコンパイルし、JSバイトコード(つまりスナップショット)を生成する。
  • JSバイトコード、オリジナルのJSコード、その他の静的アセットを仮想ファイルシステム内に保存する。
  • 最終的に、このファイルシステムをバンドルされたNode.jsエンジンとともに、最終的な実行ファイルにパックする。

NodeJSアプリケーションが実行されると、基本的にこのバンドルされたNode.jsランタイムが起動し、仮想ファイルシステムに格納されたJSバイトコードが実行される。

私たちは、"vercel/pkg "ユーティリティをよりよく理解するために、いくつかの実験を行うことにした。そのため、以下のデフォルトの設定ファイルでシンプルなNodeJSアプリケーションをセットアップした:

mkdir test

cd test && npm init -y

npm install --save-dev pkg

# パッケージがMacOS ARM64用にビルドされるように "build "プロパティを追加:

cat package.json

{

 "name":「test"、

 "バージョン":"1.0.0",

 "説明":"",

 "main":"app.js"、

 "bin":"app.js"、

 "scripts": {

   "test":"echo \"Error: no test specified\" && exit 1"、

   "ビルド":"pkg .--ターゲット node14-macos-arm64 --debug"

 },

 "キーワード":[],

 "著者":"",

 "ライセンス":「ISC」、

 "devDependencies": {

   "pkg":"^5.8.1"

 }

}

npm run buildコマンドを実行したところ、以下のような挙動を示した:

図7.テスト用NodeJSアプリケーションの構築

これらの結果からわかるように、「vercel/pkg」ユーティリティは、デフォルトでV8バイトコードファイルとオリジナルのJavaScriptソースコードの両方を含んでいたようだ:

図8.pkg "ユーティリティが生成するデバッグメッセージ。

この動作を確認するため、"vercel/pkg "ユーティリティによって生成された最終的なアプリケーションに対してpkg-unpackerツールを使用したところ、オリジナルのJSソースコードを取り出すことに成功した:

図9.オリジナルのJSコードを抽出する。

調べてみると、こんな説明があった:"デフォルトでは、pkgは各パッケージのライセンスをチェックし、一般向けでないものはバイトコードとしてのみ含まれるようにする。"

この部分の調査で、licenseプロパティがデフォルトの「ISC」値に設定されているか、「private」プロパティが「true」に設定されていない場合、「vercel/pkg」ユーティリティにはデフォルトでV8バイトコードとオリジナルのJSコードの両方が含まれることが判明した。package.jsonファイルからlicenseプロパティを削除すると、以下のようなデバッグ・メッセージが表示されます:

図10.package.jsonファイルから "license "プロパティを削除した後に "pkg "が生成するデバッグメッセージ。

予想通り、"license "プロパティなしでビルドされたアプリケーションに対してpkg-unpackerツールを実行すると、V8のバイトコードのみを抽出することができ、オリジナルのJSコードも抽出することはできなかった:

図11. NodeJSアプリケーションに埋め込まれた抽出ファイルは、V8バイトコード・ファイルです。

pkg-unpackerツールをコックピット NodeJS アプリケーション(つまり、Phoenix Contact デバイスが公開する HTTPS サーバーを実装するアプリケーション)上で実行し、埋め込まれた JavaScript ソースコードを抽出することができました。

図12.Phoenix Contact NodeJSアプリケーション(コックピット)の開梱。
図13.(部分的な)NodeJSアプリケーションのJavaScriptコード。

package.jsonファイルからわかるように、コックピット・アプリケーションはデフォルトの "license "プロパティを "ISC "に設定してコンパイルされているため、vercel/pkgはJSバイトコードと生のJSコードの両方をNodeJSアプリケーションの仮想ファイルシステム内に書き込んでいます。以前に確認されたように、この動作は、pkg-unpackerのような自動ツールが埋め込まれたJSコードを抽出することを可能にします。

図14.コックピット・パッケージ設定ファイル(package.json)。

リバースエンジニアリング - 難読化解除

コックピット・アプリケーション内に埋め込まれたJavaScriptのソースコード・ファイルをすべて抽出した結果、部外者が読み込んで機密情報を取得できないように難読化されていることがわかりました。

図15.router.jsファイルは難読化され、最小化されている。

この保護メカニズムを破壊するために、プロジェクトに関するメタデータを格納するために使用されるpackage.jsonファイルの評価を開始した。以下のスクリーンショットを見ればわかるように、最終的な実行ファイルを "vercel/pkg "でビルドしてパックする前に、JSコードが人気のあるjavascript-obfuscatorツールで難読化されていることは明らかだ。

図16.コックピット・パッケージ設定ファイル(package.json)。

JS De-obfuscatorユーティリティのおかげで、すべてのJSコードを難読化解除することができた:

図17.難読化されたJSコード。

脆弱性調査CVE-2023-3572

NodeJSアプリケーションに埋め込まれたすべてのJavaScriptコードを難読化解除した後、静的に解析し、NodeJSアプリケーションによって公開された「/api/tmd」APIのすべてのHTTP POSTリクエストを処理するために、JSの「posttime」関数が呼び出されていることを突き止めた。

以下の証拠に示すように、JavaScript 関数は HTTP ボディに受信したすべてのパラメー タを連結し、「timedatectl」の引数として使用する。このコマンドは、標準の NodeJSchild_process.execSync()関数を通して、基礎となる Linux OS 上で実行され、最終的に WP 6121-WXPS デバイス上で日付が設定されます。

図18.controllers/regionalcontroller.jsにある脆弱なJSコード

NodeJSのchild_process.execSync()の ドキュメントを読むと、この関数の動作は、OSシェルをスポーンし、そのシェルを通して指定されたコマンドを実行することがわかりました。

execSync関数に渡された文字列はシェルによって直接処理されるため、特殊文字(シェルによって異なる)を適宜処理する必要がある。最終的なコマンドは実行時に計算され、実行されるために基礎となるLinuxシェルに渡されるため、この条件により攻撃者は簡単にOSコマンド・インジェクションを引き起こすことができます。

コックピット・プロセスはroot権限で実行されるため、このHTTP POSTリクエストを受け取るとすぐに、悪意のあるペイロードがターゲットのLinux OS上でroot権限で実行されます。

以下のスクリーンショットに示すように、このソフトウェアの不具合を悪用することで、ターゲット・デバイス上の管理者シェルを取得することに成功しました。このHTTP APIは認証されていないため、ウェブ・アプリケーションをネットワークで可視化できる攻撃者は、認証なしで容易に管理者権限を獲得できることに注意することが重要です。

図19.ターゲットLinux OS上での任意のコマンドの実行。

結論

Phoenix Contact WP 6121-WXPSに関するこの2回目のブログでは、管理者権限を得るための方法論と手順について説明しました。この目標を達成するためには、NodeJS サーバをリバース・エンジニアリングする必要がありました(NodeJS サーバを解凍し、難読化解除する)。最後に、"/api/tm"APIにソフトウェアの欠陥を見つけた後、"OSコマンド・インジェクション "の脆弱性を悪用し、root権限でLinux OS上で任意のコードを実行することができました。

パート3では、SNMPプロトコルに影響するすべての脆弱性を分析し、悪用するために使用したプロセスについて掘り下げ、特に、攻撃者が認証なしで管理者シェルを取得するために、これらの問題をどのように連鎖させることができるかについて説明します。ご期待ください!