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. 日付時刻の問題
drupal 7 以前のバージョンの
/sites フォルダーには、主にサイトで使用されるテーマとモジュール、およびその他のサイト ファイルが保存されます。
/themses はカスタマイズまたはダウンロードされたテーマを格納します
/vendor はコード依存ライブラリを格納します
次に、コア フォルダー core
# の下のディレクトリ構造を見てみましょう。
## エントリ ファイルは非常に簡潔で、コードはわずか 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 ファイルに入り、どのような操作が実行されたかを見てみましょう。
##ここでは、これから返される filterResponse 関数のフォローアップを続けます。
ここでの応答オブジェクトはレイヤーごとに返されます (すべての応答結果がこのプロセスを通過するわけではないことに注意してください)。応答オブジェクトにカプセル化され、index.php ファイルの $response 変数に返されます。次に $response->send() を呼び出して、カプセル化された応答オブジェクトを送信します。
送信するリクエスト操作の内容が複雑すぎる場合があるため、上記の呼び出しが終了すると、drupal カーネルはシャットダウンする前に最終処理を実行します。プロセスは Index.php ファイルの最後の行に入り、$kernel->terminate($request,$response) を呼び出します。呼び出しチェーンに従って stackedhttpkernel.php ファイルに従います
まず、drupal はオプション リクエストを処理しようとしますが、残念ながら、私たちのリクエストは POST リクエストであるため、処理できません。それを処理して直接手放します。
次に、URL パス上のスラッシュの問題に対処し、複数のスラッシュで始まるパスを 1 つのスラッシュに変換します。
その後、リクエストに応じて本人確認が行われますが、ここではログインしていません、私たちは観光客なので特別扱いはありません。
次に、$_GET['destination'] と $_REQUEST ['destination'] を含むターゲット パラメータがクリーンアップされ、リダイレクト攻撃を防ぎます。
POST リクエストの _drupal_ajax パラメータに基づいてリクエストが AJAX リクエストであるかどうかが判断され、関連する属性が設定されます。
次のステップは、リクエストの URL 部分に従って、対応するルートを照合することです。ここで、drupal はまず、ルート キャッシュ内で対応する一致を検索します。 、すべてのルーティング テーブル検索操作を続行します。 (コード量が多いため、ここではすべてのコードをインターセプトするのではなく、コードの一部のみをインターセプトします。) 処理関数は onKernelRequest にあり、同時に user.routing からも関連情報を見つけることができます。 yml ファイル。
#ルートが見つかりました。次のステップは、ルートが利用可能かどうかを確認することです。
次のステップは、サイトがメンテナンス モードかどうかを確認します。メンテナンス モードの場合は、アカウントからログアウトし、サイトがオフラインかどうかを確認し、動的ページ キャッシュを確認し、非ルーティング設定を前処理して、レプリカを無効にするかどうかを確認します。パラメータに従ってサーバーを起動します。これらの操作に関連する機能を以下のスクリーンショットに示します。
handleRaw でリクエスト関連のイベント ディスパッチを処理し、リクエストから対応するコントローラーを見つけた後、コントローラーに基づいて対応する処理関数を見つけます。以下の call_user_function のコントローラーは、上の図のクロージャー コールバック関数に置き換えられています。ここでコントローラーを呼び出すことは、上の図のクロージャー関数を直接入力することと同じです。
drupal では、コントローラーがレンダリング コンテキストに追加され、各コントローラーの処理中にレンダリングする必要がある場所がある場合に、レンダリング操作が実行されるようになります。直接実行されます。
コントローラーが実際の呼び出しメソッド (getContenResult) を入力すると、フォームの構築が正式に開始されます。
4.2.4 フォームの構築
buildForm 関数を入力した後、まず POST 情報を取得し、それを form_state に保存します。
# 例外をキャッチした後、例外を処理し、例外をディスパッチします。
ここでのディスパッチは、実際には例外をトラバースして照合するプロセスです。例外が発生する状況はさまざまです。正しい例外を照合して、特定の処理を実行します。一致するものがない場合は、そのまま放置してください。ここでは 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 配列に含まれるため、ここでは詳しく説明しません。
次に、URL の element_parents パラメーター値を使用して、フォーム配列の値を取得します。これについては、パート 4 のセクション 4.2.5 で説明されているため、ここでは繰り返しません。最後に、対応する変数を構築し、doRender 関数で call_user_func_array を使用して脆弱性をトリガーします。
上記の説明に基づいて、メール パラメータを使用して次の POC パッケージを構築しました。
上記のメール パラメータが制御可能であることに加えて、 form_build_id パラメータも分析プロセス中に見つかりました。これも制御可能で、別の POC は次のとおりです。
以上がdrupal8 フレームワークの詳細な分析と脆弱性の動的デバッグを実行する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。