web-dev-qa-db-ja.com

PHP eval code sandbox break

私はここで激しく反対投票されたコメントに気づきました: http://php.net/manual/en/function.php-check-syntax.php

_function eval_syntax($code)
{
    $braces = 0;
    $inString = 0;

    // We need to know if braces are correctly balanced.
    // This is not trivial due to variable interpolation
    // which occurs in heredoc, backticked and double quoted strings
    foreach (token_get_all('<?php ' . $code) as $token)
    {
        if (is_array($token))
        {
            switch ($token[0])
            {
            case T_CURLY_OPEN:
            case T_DOLLAR_OPEN_CURLY_BRACES:
            case T_START_HEREDOC: ++$inString; break;
            case T_END_HEREDOC:   --$inString; break;
            }
        }
        else if ($inString & 1)
        {
            switch ($token)
            {
            case '`':
            case '"': --$inString; break;
            }
        }
        else
        {
            switch ($token)
            {
            case '`':
            case '"': ++$inString; break;

            case '{': ++$braces; break;
            case '}':
                if ($inString) --$inString;
                else
                {
                    --$braces;
                    if ($braces < 0) return false;
                }

                break;
            }
        }
    }

    // If $braces is not zero, then we are sure that $code is broken.
    // We run it anyway in order to catch the error message and line number.

    // Else, if $braces are correctly balanced, then we can safely put
    // $code in a dead code sandbox to prevent its execution.
    // Note that without this sandbox, a function or class declaration inside
    // $code could throw a "Cannot redeclare" fatal error.

    echo "Braces: ".$braces."\r\n";
    $braces || $code = "if(0){{$code}\n}";

    if (false === eval($code)) {}
}

eval_syntax("file_put_contents('/home/yourname/Desktop/done.txt', 'OVERWRITTEN');");
_

コードをバイパスしてevalを使用した悪意のあるユーザー入力の実行を引き起こそうとしましたが、できませんでした。なぜ反対票が投じられたのかしら。

中括弧が一致していないかどうかを確認できるように、'if(0){' . $code . '}を追加せず、中括弧が一致しない状態でユーザー入力をそのまま実行し、例外をスローして実際には実行されません。

中括弧が一致する場合はevalを呼び出しますが、その内部は_if {0}_ "sandbox"であるため、何も実行しません。どうすればこれをバイパスできますか?

私はevalが安全でないことを知っていますが、ここでトリックが何であるか知りたいです。上記のコードでif(0)と中括弧のチェックのセキュリティをバイパスするにはどうすればよいですか

// {を追加しようとしたので、中括弧が不均衡になり、if(0)が追加されてevalが呼び出されなくなりますが、token_get_allはそれを台無しにします。

11
JohnDoes

あなたが投稿したコードからのこのコメントは私の考えを要約していると思います:

// We need to know if braces are correctly balanced. // This is not trivial due to variable interpolation // which occurs in heredoc, backticked and double quoted strings

コードチェックを不適切に実行すると、リモートでコードが実行される脆弱性があることに注意してください。重要な項目での作業はもちろんエンジニアにとって標準ですが、非常に困難なタスクで作業している場合and間違いは最も深刻な種類のセキュリティの脆弱性につながります-まあ、明らかに災害のレシピです。実際、あなたが投稿したコメントが著者のこれに対する2回目の試みであったことを指摘する価値があります。彼の最初の試みには、(おそらく)「サンドボックスのエスケープ」に対して脆弱であり、最初からやり直すことを要求した少なくとも1つの間違いが含まれていました。確かに、アプリケーションのセキュリティを、Web上のランダムなページのコメントボックスに書かれた1回限りのスクリプトに信頼するつもりはありません。特に(私が何かを見落としているのでない限り)このコメントは、まったく同じことをする組み込み言語を説明するページに偶然あります。私はインターネット上のランダムな男の前に組み込まれた言語を信頼します。

誰もがエクスプロイトを見つけたかどうかに関係なく、そもそもこれが一般的に悪い考えであるという事実は変わりません。著者が考えていない他のエスケープオプションがある場合はどうなりますか?たとえば、さまざまなエクスプロイトがさまざまなエンコーディングの混乱に依存しています。攻撃者がファイル内のエンコーディングとは異なるエンコーディングを使用してブレースを挿入する可能性がありますが、PHPによってサポートされているため、チェックではブレースとして登録されませんが、PHPによってブレースとして認識され、攻撃者がブレースバランスチェックを突破します。確かに、その特定の攻撃手段は実際にはは可能ではないかもしれませんが、私は主に一般的なポイントを強調しようとしています。このような状況では、捕捉が困難な「問題」が多数発生する可能性があり、このコードでそれらの1つが欠落している結果、リモートコード実行の脆弱性が生じます。

「完璧に仕上げるか、非常に脆弱になるか」に直面したとき、私の最初の好みは「ただやらない」です。その後、特に「インターネット上のランダムなコメントを信用しないでください」という強力なメッセージが続きます。特に、同じことを(ただし)安全に実行できる、簡単にアクセスできる言語が組み込まれている場合は特にそうです。

追加して編集evalについてコメントする価値があります。あなたは「私はevalが安全でないことを知っています」と言いました...これは厳密には真実ではありません。 evalの使用は、いくつかの理由で一般に嫌われています:

  1. 他に何もない場合は、パフォーマンスが低下する傾向があります
  2. 不適切に使用すると、確かに実際のセキュリティ問題が発生する可能性があります
  3. 一般に、プログラミングのベストプラクティスに違反している

ただし、他のツールと同様に、時間と場所を設定できます。つまり、問題を明確にすることは、evalが安全でないということではありません。問題は、ここでevalが安全に(かつ適切に)使用されているかどうかです。上記で概説したすべての理由から、ここでは適切に使用されているとは思いませんが、それは包括的な「評価は安全ではない」という記述とは異なります。

1
Conor Mancone