最近少し怠けていて、何かやることを見つけないと落ち着かないので、抜け穴を見つけて解析するつもりなので、TP. ThinkPHP6 の抜け穴をいくつか見てみるつもりです。 TP の最新バージョンは 0.13 です 8 月にマスターが提出しました TP にはデシリアライゼーションの問題があると指摘されました インターネット上の一部の専門家が分析していますが、ブレークポイントが多く、用途が明確になっていないメソッドもありますそこで私も詳しく分析してみました。以下は POC です
# 分析 #まずは POC の開始点を見てください
開始点が Psr6Cache クラスにあることがわかりました。このクラスに入りましたが、__destruct などの一般的な逆シリアル化開始マジックは見つかりませんでしたまたは __wakeup.メソッドの場合、その親クラス AbstractCache の抽象クラスに存在する必要があると推測されます。図に示すように、AbstractCache クラス
に従って、この逆シリアル化チェーンの開始クラスを正常に見つけます。ここでは、autosave 属性を false に制御して、save メソッドを開始できます。
Psr6Cache クラスに戻ってこのメソッドを表示します。
プール属性とキー属性の両方を制御できることがわかります。したがって、異なるクラスの同じ名前のメソッド (getItem) を呼び出すルートが 2 つ存在する可能性があります。または、__call メソッドを直接トリガーしてみてください。 POC 作成者がどのようにして逆シリアル化の進行を許可しているかを見てみましょう。
作成者はコンストラクター メソッドを使用して exp を渡しました。exp は実際に Channel クラスをインスタンス化しています。 Channel クラスに入って確認します。
Channel クラスには __call メソッドがあるため、作成者はチェーンを継続するために __call をトリガーすることを選択します。この呼び出しメソッドは 2 つのパラメーターを受け入れます。メソッドはハードコーディングされており (getItem)、パラメーターは制御可能です (つまり、以前に制御可能だったキー属性)
ログをフォローします。メソッドを確認します。メソッドは 3 つのパラメータを受け入れます (ただし、実際には後続のチェーンには役に立ちません)。レコード メソッド
## に続いてレコード メソッド ## を渡します。
#戻って作成者の POC を確認すると、そのコントロール遅延属性が false であることがわかります。関数に最後の if 分岐を入力させて、save メソッドを実行させます では、save メソッドはよりクリティカルなメソッドである必要があります。save メソッドに続いて悪用される可能性があるポイントは 3 つあります。作成者はどれを選択しましたか? POC によると、作成者がロガー属性を制御し、コンストラクターを使用してそれに値を割り当て、それをSocket クラスのオブジェクト このクラスには、多数の操作を含む同じ名前の複雑なメソッドが含まれています。 # 作成者がどのように構築するかを見てみましょう。作成者は config 属性を制御し、それに配列値を割り当てます。配列には次の内容があります#キーはこれら 2 つのキー値にあります。作成者は構成を制御し、invoke メソッドを呼び出すブランチまでプログラムを実行できるようにします
同時に、アプリ属性も制御可能です。作成者は、アプリ属性を App クラスのオブジェクトにします。App クラスに入りましょう。
まず、App クラスの存在を見てみましょう。メソッドの場合、このメソッドはその親クラスで見つかりました。
次に、App クラスに対して実行される唯一の操作を示します。これは、instance 属性の値を制御します。ここで値を制御する目的は、Request クラスに入り、URL メソッドを実行することです。
作者は、ここでのリクエストクラスは、url属性の値を制御するものです。 url 属性が存在する場合、最初のブランチに入り、その値がそれ自体と等しいことがわかります。
同時に、以前に渡した完全な値が true であることに気付きました。したがって、最終的に返される結果は $this->domain().$url です。URL は制御できましたが、ドメイン メソッドは何を返すのでしょうか?
OK、これを見る必要はありません。多くの分析を行った結果、$currentUri の最終値が得られました。
http://localhost/
currentUri は、チェーンの長さに応じて配列が呼び出され、呼び出しに達しました。デシリアライズの旅はほぼ終了です。
呼び出しを確認してください。App クラスはこのメソッドを見つけることができません。他の This メソッドではありません。親クラスで見つかりました
ここで確認できます。この関数には 3 つの分岐がありますが、最終的にはどこに行くのでしょうか?前に $config['format_head'] で渡した内容によると、まず、渡したオブジェクトは Closure のインスタンスまたはサブクラスではなく、2 番目のブランチの条件を満たしていません。
それでは、3 番目の分岐に入ります。続いて invokeMethod() メソッドを実行します。ここで渡される $callab は [new \think\view\driver\Php,'display'] で、$vars は ['http://localhost/']
# です。 ##渡した $method は配列なので、最初の分岐に入ることに注意してください。新しい \think\view\driver\Php (つまり、オブジェクト) を $class に割り当て、「display」 (つまり、メソッド名) を新しい $method に割り当てます。 次に判定が行われます。$class がオブジェクトの場合、その値はそれ自体です。オブジェクトを渡しているため、ここでは変更はありません。次に、最も重要なコードを入力します。オブジェクト new \think\view\driver\Php とメソッド display が ReflectionMethod に渡されることがわかります。#最後に、invokeArgs メソッドを呼び出し、新しい \think\view\driver\Php オブジェクトを渡し、$args
# を渡します。
##それでは、引数とは何でしょうか?
追跡してみると、処理関数であることがわかりました。私は怠け者で、この時点で分析はほぼ終わっているので、深く読みませんつまり、渡した $vars の重要な部分、つまり ['http://localhost/'] は保持され、後続のパラメーターに入力されます。
さらに詳しく見ると、この関数 (invokeArgs) は call_user_func() と単純に比較できるため、最終的なキー コードは実際には次の 2 行のみです 、つまり、
$reflect = new ReflectionMethod(new \think\view\driver\Php,’display’); return $reflect->invokeArgs(new \think\view\driver\Php,’ ’)
TP のチェーンは相変わらず興味深い (そして複雑)、特に最後の ReflectionMethod クラスの使用法がわかりません。このクラスと、call_user_func 関数と同様の機能を実現するためのクラス内のメソッドの組み合わせを理解していない場合は、 、これは見逃しがちですが、素晴らしいエクスプロイトです。
[関連チュートリアルの推奨事項: thinkphp フレームワーク]
以上がThinkPHP6.0.13 デシリアライゼーションの脆弱性分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。