Drupal フレームワークにおいて、最も古典的で私たちに最も近いのは、2018 年の CVE-2018-7600 脆弱性です。しかし、この脆弱性分析記事を読んで研究する過程で、それらはすべてこの脆弱性点について詳細に分析されていることがわかりました。このフレームワークの実行プロセスにあまり慣れていない人は、読んでも理解するのが難しいかもしれません。
以下は主に 2 つの部分に分かれています:
最初の部分は、drupal フレームワーク プロセス (ここでは主に 8.x シリーズ用) の紹介であり、 symfony オープン ソース フレームワークの基本 このページの drupal フレームワークは、リスナー モードを使用して複雑な処理プロセス全体をサポートし、フレームワークがリクエストを処理する方法についての基本的な理解を提供します。
2 番目の部分では、フレームワークと脆弱性 CVE-2018-7600 の実行プロセスの詳細な解釈を組み合わせます。脆弱性のトリガーの開始点では、まず drupal フレームワークの処理プロセスを動的に理解します。通常のデータ パケットのデバッグ: これは、通常のパッケージ内の制御可能な変数を使用して POC パッケージを構築します。始まりと終わりを理解できるだけでなく、途中のプロセスも透明化できます。平行線を引くことができるようになる。
Drupal は、PHP 言語で書かれたオープンソースのコンテンツ管理フレームワーク (CMF) であり、コンテンツ管理システム (CMS) と PHP 開発フレームワーク (フレームワーク)。世界最高のCMS賞を何年も連続で受賞しており、PHP言語をベースにした最も有名なWEBアプリケーションです。
Drupal アーキテクチャは、コア、モジュール、テーマの 3 つの部分で構成されます。 3 つはフック機構を通じて密接に接続されています。そのうちコア部分は、世界中の多くの著名なWEB開発専門家からなるチームによって開発・保守されています。
Drupal は、強力で自由に構成可能な機能を統合し、個人のブログ (PersonalWeblog) から大規模なコミュニティ主導の Web サイト (Community-Driven) に至るまで、さまざまなアプリケーションを使用して Web サイト プロジェクトをサポートできます。 Drupal は元々、DriesBuytaert によって開発されたコミュニティ ディスカッション ソフトウェアのセットでした。その後、その柔軟なアーキテクチャ、便利な拡張機能、その他の機能により、世界中の何千人ものプログラマーが Drupal の開発とアプリケーションに参加しました。現在、これは強力なシステムに発展しており、The Onion、Ain't ItCool News、SpreadFirefox、Ourmedia、KernelTrap、NewsBusters など、多くの大規模組織が Drupal ベースのフレームワークを使用して Web サイトを構築しています。これはコミュニティ主導の Web サイトで特によく見られます。
まず、公式 Web サイトのダウンロード ページ https://www.drupal.org から最新バージョンを直接ダウンロードできます。 /download または https://www.drupal.org/project/drupal/releases/xxx xxx は、対応するバージョンのソース コード ファイルをダウンロードするためにダウンロードするバージョン番号を表します。 PHP パッケージ管理ツールの Composer を使用してダウンロードすることもできます。
2.2 drupal インストール
インストール環境:WIN7 32-bit
統合環境:PHPSTUDY
デバッグ環境:PHPSTORM
実行可能インストールの問題と解決策:
1. PHP バージョンの問題: できれば PHP7.0 以降
# 2. 日付時刻の問題 解決策: php.ini に設定します 3. インストールに関する警告/core drupal のコア フォルダー。詳細については、次の説明を参照してください。 /modules カスタマイズまたはダウンロードされたモジュールが格納されます。 /profiles ダウンロードおよびインストールされたモジュールが格納されます。カスタム構成ファイル
drupal 7 以前のバージョンの
/sites フォルダーには、主にサイトで使用されるテーマとモジュール、およびその他のサイト ファイルが保存されます。
/themses はカスタマイズまたはダウンロードされたテーマを格納します
/vendor はコード依存ライブラリを格納します
次に、コア フォルダー core
# の下のディレクトリ構造を見てみましょう。
##/core/assets - jquery、ckeditor、backbone、normalizeCSS など、drupal で使用されるさまざまな拡張ライブラリ。 /core/config - drupal コア設定ファイル /core/includes – モジュラー システム自体など、モジュールの基礎となる機能関数 /core/lib – drupal によって提供されるオリジナルのコア クラス /core/ misc – JS、CSS、画像など、コアに必要なフロントエンドのその他のファイル。 /core/modules – コア モジュール、約 80 項目 /core/profiles – 組み込みのインストール構成ファイル /core/scripts – 開発者が使用するさまざまなコンポーネントスクリプトをヒットします/tests – テスト関連ファイル/core/主題 – カーネルテーマ3.2 ロジックを実行するフレームワーク Drupal は、以下に基づいて構築されています。 symfony オープン ソース フレームワーク, symfony 公式 Web サイトから、sysmfony が再利用可能な PHP コンポーネント セットであることがわかります。独自のアプリケーションで任意のコンポーネントを独立して使用できます。symfony 公式 Web サイトでは、各コンポーネントには独自の独立したドキュメントがあり、これらのコンポーネントは Drupal によって直接使用されますが、一部は Drupal 自体の特性に従って変更されます。 まず symfony の実行プロセスを見てみましょう## エントリ ファイルは非常に簡潔で、コードはわずか 6 行ですが、 Drupal 全体については、Drupal の基幹システムが大きすぎるため、すべてを解析することは不可能ですが、エントリファイルを 1 行ずつ見て実行プロセスを解析していきます。
まず、 $autoloader =require_once 'autoload.php'; 表面的には、autoload.php ファイルのみが含まれていますが、実際には、drupal は PHP オートローディング メカニズムを使用してオートローダーを作成し、自動的にロードされたファイルを取得します。物体。
コードの観点からプロセスを簡単に見てみましょう。基本的なプロセスは、vendor/autoload.php の getLoader 関数を呼び出すことです。
次に、関数を入力して、その動作を確認します。
ClassLoader オブジェクトは、内部で定義された基本的な対応関係を使用して、関数とクラス定義ファイルを検索します。
関数は最終的にインスタンス化ローダーを返します。これで最初のステップが完了しました。将来、Drupal は多くのファイルを手動でインクルードする必要がなくなり、多くの作業が節約されます。
その後、 $kernel =new DrupalKernel('prod', $autoloader); drupal は、次のリクエスト オブジェクトの処理に備えて新しい Drupal カーネル オブジェクトを作成します。
このコード行の後には、エントリ ファイルの $request= Request::createFromGlobals() があります。オブジェクト指向システムの場合、$_POST、$_GET、$_COOKIE などのグローバル変数を直接使用しないでください。 Drupal はそれらをすべて $request オブジェクトにカプセル化します。これはシンプルで便利なだけでなく、要求されたオブジェクトを使用して追加の関数やカスタム属性を直接追加することもできます。
最後に、対応するグローバル変数がリクエスト オブジェクトに追加され、カプセル化されたリクエスト オブジェクトが返されます。
上記の操作が単なる準備段階である場合、コードの次の行 $response = $kernel->handle($request); が本題に入り、drupal カーネル オブジェクト カーネルが開始されます。リクエストリクエストを処理します。
#Drupal の処理の中核は、設計パターンでのリスナー パターンの使用です。これには、さまざまなイベントとイベント レベルを含むイベント ソースが含まれます。もう 1 つの部分は、イベントを実行する必要があるプログラムまたは関数であり、これをリスナーと呼びます。リクエスト処理プロセスでは、ノードに到達するたびに対応するイベントが送出され、リスナーは取得したイベント オブジェクトとレベルに基づいて対応する操作を実行します。
システムのコア イベントは、8 つのコアを含む kernelevents.php にある symfony フレームワークのイベントを引き続き使用します。
Const REQUEST = 'kernel.request' は任意のイベントを実行します。フレームワーク コード内 コードはリクエストのディスパッチが開始される前にトリガーされます。
Const EXCEPTION = ‘kernel.Exception’ キャッチされなかった例外が発生したときにトリガーされるイベント。
Const VIEW = ‘kernel.view’ コントローラーの戻り値が応答インスタンスではない場合にトリガーされます。この時点で、コントローラーはさらなるレンダリング作業のためにレンダリング配列を返します。
Const CONTOLLER = ‘kernel.controller’ は、リクエストの解析後に対応するコントローラーが見つかったときにトリガーされ、このコントローラーは変更できます。
Const CONTROLLER_ARGUMENTS =‘kernel.controller_arguments’ コントローラーのパラメーターを解析するときにトリガーされ、パラメーターを変更できます。
Const RESPONSE = ‘kernel.response’ 応答応答リクエストの作成時にトリガーされ、応答される応答を変更または置換できます。
Const TERMINATE = ‘kernel.terminate’ は、応答が送信されるとトリガーされます。このイベントにより、応答後の重いタスクを処理できるようになります。
Const FINISH_REQUEST = ‘kernel.finish_request’ は、リクエストリクエストが完了するとトリガーされ、リクエスト中にアプリケーションが変更されると、アプリケーションのグローバルおよび環境状態をリセットできます。
これらのコア イベントに加えて、drupal の各リスナーも独自のイベントをディスパッチします。これらのファイルの場所は、\core\lib\Drupal\Core\ ディレクトリ内の対応するフォルダーにあります。これらはすべて events.php で終わり、対応する静的イベント変数がファイル内で定義されます。
Drupal コアのリクエスト プロセスを見てみましょう:
リクエストを開始します--->>リクエストを解析してコントローラを取得し、修正します----->>コントローラを解析しますパラメータ -- --》コントローラに従ってメソッドを呼び出します-----》コントローラの戻り状況を観察します。応答オブジェクトの応答を返すか、レンダリングを継続します----->>応答を送信します。プロセス全体の途中で例外が発生した場合、例外イベントが直接トリガーされて例外が分散されます。プロセス全体では、リクエストオブジェクトはコアリクエストイベントに応答するだけでなく、実際の状況に応じて他の共通モジュールイベントに応答する分岐にも入りますが、プロセスがどんなにデコボコしていても、最終的には元の状態に戻ります。メインプロセスを呼び出し、応答オブジェクトの応答を返します。
次に、ソース コードから上記の特定の動作を観察します:
引き続き、index.php からフォローアップし、drupalkernel.php ファイルに入り、どのような操作が実行されたかを見てみましょう。
#次のステップは、一連の関数呼び出しチェーンの処理です。ハンドル関数を追跡し続けることができるため、コア関数 handleaw を直接追跡できます##ここでは、これから返される filterResponse 関数のフォローアップを続けます。
ここでの応答オブジェクトはレイヤーごとに返されます (すべての応答結果がこのプロセスを通過するわけではないことに注意してください)。応答オブジェクトにカプセル化され、index.php ファイルの $response 変数に返されます。次に $response->send() を呼び出して、カプセル化された応答オブジェクトを送信します。
送信するリクエスト操作の内容が複雑すぎる場合があるため、上記の呼び出しが終了すると、drupal カーネルはシャットダウンする前に最終処理を実行します。プロセスは Index.php ファイルの最後の行に入り、$kernel->terminate($request,$response) を呼び出します。呼び出しチェーンに従って stackedhttpkernel.php ファイルに従います
# この時点で、サイクル全体が終了しました。 上記のプロセス全体で最も一般的な操作はイベントのディスパッチであることがわかりました。実際、すべてのディスパッチのプロセスは同じです。特定のディスパッチ プロセスは ContainerAwareEventDispatcher.php ファイルにあります。 kernel.request イベント。例を示します。 システムには合計 19 個のリスナーがあり、各リスナーにはそれに関連するサービス名があります。受信イベント名に基づいて一致します。対応するリスナー次に、サービス名に対応する関数を 1 つずつたどって呼び出します。ここにあるのは kernel.request イベントであり、呼び出しメソッドはコールバック呼び出しです。 4. CVE-2018-7600 をもう一度見てください 3 番目の部分の単純なフレームワーク分析を通じて、漠然とした概念しか理解できないかもしれません。次に、脆弱性の例を組み合わせて、より古典的な Drupal フレームワークの脆弱性 cve-2018-7600 に焦点を当て、フレームワーク内でのこの脆弱性の詳細な動作プロセスを注意深く観察します。ここで使用する脆弱性トリガー環境のバージョンは 8.5.0 です。このバージョンの脆弱性トリガーはより直感的であるため、特に明記されていない限り、以降の分析で使用されるコード バージョンはこのバージョンになります。 4.1 パッチの比較 この脆弱性はバージョン 8.5.1 で修正されており、5.0 と 5.1 の間にはサブバージョンが 1 つしかないため、ソース コードの違いをより明確に比較できます。公式がこの脆弱性をどのように修正しているかを見てみましょう: バージョン 8.5.1 のソース コードでは、リクエスト部分をフィルタリングする新しい RequestSanitizer.php ファイルが追加されています。stripDangerousValues メソッドでは、# で始まるものをフィルタリングします。内のすべてのキー名の値。 プリハンドルメソッドでは、上記ファイルに追加した新規メソッドを呼び出してフィルタリングを行っています。下図の右側の赤い部分が、上記ファイルで追加したフィルタリングコードです。 8.5.1.ここでのフィルタリング コードの呼び出し位置は、drupal カーネルがリクエストを処理する前です。これにより、脆弱性が完全に修正されます。 その後、drupal 公式 Web サイトにアクセスして公式ドキュメントを表示したところ、drupal レンダー API には # の先頭に特別な処理があることがわかりました。重要なドキュメントのリンクは以下です
まず、drupal はオプション リクエストを処理しようとしますが、残念ながら、私たちのリクエストは POST リクエストであるため、処理できません。それを処理して直接手放します。
次に、URL パス上のスラッシュの問題に対処し、複数のスラッシュで始まるパスを 1 つのスラッシュに変換します。
その後、リクエストに応じて本人確認が行われますが、ここではログインしていません、私たちは観光客なので特別扱いはありません。
次に、$_GET['destination'] と $_REQUEST ['destination'] を含むターゲット パラメータがクリーンアップされ、リダイレクト攻撃を防ぎます。
POST リクエストの _drupal_ajax パラメータに基づいてリクエストが AJAX リクエストであるかどうかが判断され、関連する属性が設定されます。
次のステップは、リクエストの URL 部分に従って、対応するルートを照合することです。ここで、drupal はまず、ルート キャッシュ内で対応する一致を検索します。 、すべてのルーティング テーブル検索操作を続行します。 (コード量が多いため、ここではすべてのコードをインターセプトするのではなく、コードの一部のみをインターセプトします。) 処理関数は onKernelRequest にあり、同時に user.routing からも関連情報を見つけることができます。 yml ファイル。
#ルートが見つかりました。次のステップは、ルートが利用可能かどうかを確認することです。
次のステップは、サイトがメンテナンス モードかどうかを確認します。メンテナンス モードの場合は、アカウントからログアウトし、サイトがオフラインかどうかを確認し、動的ページ キャッシュを確認し、非ルーティング設定を前処理して、レプリカを無効にするかどうかを確認します。パラメータに従ってサーバーを起動します。これらの操作に関連する機能を以下のスクリーンショットに示します。
# この時点で、KernelEvents::REQUEST のすべてのリスナーは、動作分析が完了すると、上記の操作で主にいくつかの追加措置が講じられていることがわかります。それらは無視しても問題ありませんが、そこから貴重な情報も抽出し、リクエスト オブジェクトを通じて関連するルーティング情報を照合しました。 4.2.2KernelEvents::CONTROLLER および KernelEvents::CONTROLLER_ARGUMENTS イベント 次に、drupal は handleraw 関数で、一致したルーティング情報を通じて実際のリクエスト コントローラーと対応するパラメーターを見つけます。 まず、KernelEvents::CONTROLLER のリスナーがどのような操作を行うかを見てみましょう。 まず、将来の競合を避けるために、キー KEY を対応するマネージャーに設定します。後続のデータ処理の性質の整合性を保つため、ここではクロージャーを使用してコールバック処理コントローラーの関数を $event オブジェクトに格納します KernelEvents::CONTROLLER_ARGUMENTS には独自のリスナーなので、ここではディストリビューションが直接リリースされます。 4.2.3 コントローラーの呼び出しhandleRaw でリクエスト関連のイベント ディスパッチを処理し、リクエストから対応するコントローラーを見つけた後、コントローラーに基づいて対応する処理関数を見つけます。以下の call_user_function のコントローラーは、上の図のクロージャー コールバック関数に置き換えられています。ここでコントローラーを呼び出すことは、上の図のクロージャー関数を直接入力することと同じです。
drupal では、コントローラーがレンダリング コンテキストに追加され、各コントローラーの処理中にレンダリングする必要がある場所がある場合に、レンダリング操作が実行されるようになります。直接実行されます。
コントローラーが実際の呼び出しメソッド (getContenResult) を入力すると、フォームの構築が正式に開始されます。
4.2.4 フォームの構築
buildForm 関数を入力した後、まず POST 情報を取得し、それを form_state に保存します。
## buildForm 関数のretrieveForm 関数では、フォーム フォームの最初の組み立てが開始されます。レンダリングする必要がある要素がある場合、Drupal のほとんどは \Drupal を直接使用します。 ::service('renderer ')->renderPlain(); このレンダリング サービスは要素に対してレンダリング操作を実行し、最終的なレンダリング関数の主な操作は doRender 関数内にあります。 rquest に従って組み立てられたフォームが組み立てられた後、フォーム要求はすぐに処理されます。ここでは processForm 関数がこの操作を実行します。この関数では、再帰的メソッドを使用します。動作を処理するための操作です。ここでは画像のアップロード操作を行っており、この動作も処理されます。処理が完了すると、画像が移動されます。次に、各要素とトークンをチェックして検証し、最後に結果に基づいてフォーム全体を再構築します。 processForm で画像処理プロセスを追跡したい場合は、以下の関数にブレークポイントを直接設定し、スタック トレースバックに基づいて対象の操作を見つけることができます。processForm 関数を実行した後、再構築後の FORM フォームの一部のスクリーンショットを次に示します。 ここに移動してください。フォーム全体への処理操作が完了しました。 4.2.5 例外のディスパッチ。 前の手順でフォームの操作が完了すると、リクエスト オブジェクトは知らないうちにレスポンス オブジェクトに変換されています。レイヤーごとに返して送信操作を実行しようとしていましたが、次のプロセスで drupal がこれが ajax リクエストであることを発見したため、積極的に操作をインターセプトし、AJAX 例外をスローしてリクエストに対して追加の処理を実行しました。
# 例外をキャッチした後、例外を処理し、例外をディスパッチします。
ここでのディスパッチは、実際には例外をトラバースして照合するプロセスです。例外が発生する状況はさまざまです。正しい例外を照合して、特定の処理を実行します。一致するものがない場合は、そのまま放置してください。ここでは AJAX 例外と一致しましたが、他の例外の処理プロセスの方が気になる場合は、kernel.Exception 配列内で例外を探してください。
さらに追跡調査を行ったところ、onException の buildResponse 関数に AJAX を処理するための特定のメソッドがあることがわかりました。
UploadAjaxCallback 関数では、データ パケットの URL から element_parents パラメーターの値を取得し、これをキーとして使用して、最終的に処理した FORM フォームから結果を取得し、結果をレンダリングして表示します。 HTML ページの方が優れています。
POST パッケージ内の URL のパラメーターに基づいて、FORM フォームの user_picture の下にあるウィジェット配列の最初の項目を取り出します。
doRender でレンダリングされる最終オブジェクトは、取り出したばかりの要素です。
レンダリング後、処理プロセス全体が終了し、応答の構築が開始され、レイヤーごとに返されます。
4.2.6 kernel.response イベント
応答段階に到達したので、応答のトリガーを開始する必要があります。次に、応答に含まれるリスナーを見てみましょう。
応答ディスパッチ関数では、基本的に応答オブジェクトに追加し、対応する拡張操作をいくつか実行します。たとえば、動的ページをキャッシュする必要があるかどうか、キャッシュ コンテキストの追加、プレースホルダーの処理、成功した応答での追加ヘッダーの設定などが必要かどうかを判断します。上記の操作はすべて、リスナーの下の kernel.response 配列に含まれるため、ここでは詳しく説明しません。
##4.2.7 kernel.finish_request リクエストとレスポンスの操作が完了すると、drupal カーネルはすべてが完了したことを通知され、送信されます。 finish_request イベントでは、このイベントのリスナーは 1 つだけです。URL ジェネレーターを正しいコンテキストで実行するには、現在のリクエストを親リクエストとして設定する必要があります。 4.2.8 kernel.terminate イベント 上記の操作が完了すると、リクエスト リクエストがリクエスト スタックからポップされ、Index.php 層に返されます。応答はメインの入り口ページで送信されます。最後に、作業をクリーンアップし、kernel.terminate イベントをトリガーし、関連する変更をファイルに書き込む必要があるかどうかを判断します。最終的に、drupal コアがシャットダウンします。プロセス全体が終了しました。 4.3 プロセス全体の概要 前のセクションでプロセス全体を分解しました。以下に簡単にまとめてみましょう: データ パケットの送信 -->によるとURL に関連するルートを照合 --> ルートに基づいて対応するコントローラを検索 --> コントローラに基づいて処理メソッドを取得 (ここではフォーム関連の操作です) --> フォームを構築してレンダリング --> ; フォーム要求を処理します- -> フォームを処理した後、それが AJAX 操作であるかどうかを判断します --> 積極的に例外をスローし、AJAX コールバックを使用して URL でマークされた FORM フォーム キーを再レンダリングします --> 完了応答オブジェクトの対応する構造 --> 応答の送信 -- > スイープの終了。 5. 脆弱性 POC の構築 上記のフレームワークの分析と理解を組み合わせて、POC の構築を開始します。チェックポイント セキュリティ チームは、この脆弱性の技術的な詳細に関するレポートをリリースしました (上記リンク). この脆弱性のトリガー ポイントは、フォームの構築後に AJAX 例外をトリガーし、レンダリングされるオブジェクトをフォームから抽出することであることがわかります。 FROM フォームを取得してレンダリングするとき、つまり最後の doRender 関数内でトリガーされます。 doRender で次の悪用可能な点が見つかりました: 第 4 部のフレームワークで実行される通常のアップロード パッケージのプロセスの分析に基づいて、次のことを実行したいことがわかりました。構築されたコンテンツは doRender の脆弱性を正常にトリガーします。まず、プロセスを制御し、AJAX コールバック部分に入らせる必要があります。以下の if 判定では、$ajax_form_request、$form_state->isProcessingInput()、$request->request->get('form_id')== $ という 3 つの条件を同時に満たす必要があることがわかります。フォームID。以下の図からわかるように、$ajax_form_request の値は変数 ajax_form によって制御され、form_id はフォームの ID です。次に、URL の element_parents パラメーター値を使用して、フォーム配列の値を取得します。これについては、パート 4 のセクション 4.2.5 で説明されているため、ここでは繰り返しません。最後に、対応する変数を構築し、doRender 関数で call_user_func_array を使用して脆弱性をトリガーします。
上記の説明に基づいて、メール パラメータを使用して次の POC パッケージを構築しました。
上記のメール パラメータが制御可能であることに加えて、 form_build_id パラメータも分析プロセス中に見つかりました。これも制御可能で、別の POC は次のとおりです。
以上がdrupal8 フレームワークの詳細な分析と脆弱性の動的デバッグを実行する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。