姿势一
题目背景:ctfshow命令执行 web40
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
仔细看一下题目会发现过滤的是中文的括号!!!下面来一个个拆开说一下payload,首先看看需要用到的函数
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
pos():返回数组中的当前元素的值。
array_reverse():数组逆序
scandir():获取目录下的文件
next(): 函数将内部指针指向数组中的下一个元素,并输出。
第一步,先用 pos(localeconv())获得点号(.)
第二步scandir(pos(localeconv())),即scandir(.),意思是获取当前目录下的文件。
第三步,明确flag.php所在的地方为倒数第二个数组,利用array_reverse(),将数组逆序,即
array_reverse(scandir(pos(localeconv())))
第四步用next()函数将指针指向数组的下一个元素,并输出,即指向flag.php
最后用 highlight_file高亮该文件就ok了。
下面的东西也是参照yu师傅博客得到的一些做题的思想,利用session,开始前先来了解游一些函数吧。
session_start() 启动新会话或者重用现有会话
session_id() 获取/设置当前会话 ID
在cookies处将PHPSESSID的值修改为ls
然后输出?c=session_start();system(session_id());获取当前目录下的文件名
然后再把PHPSESSID的值修改为flag.php,传入我们的payload:
?c=session_start();highlight_file(session_id());
其它题可以这样子,但是这题是行不通的,受php版本影响 5.5 -7.1.9均可以执行。
session_id规定为0-9,a-z,A-Z,-中的字符。在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的。
姿势二
题目背景:长安战疫杯的RCE_No_Para
代码解析:
preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])
preg_replace是执行正则搜索,如果搜索到则替换为空。
\W是匹配非数字、字母、下划线, [^\W]则是非\W的东西,即数字、字母、下划线,相当于小写的\w。
然后难点是(?R)?,意思为递归整个匹配模式。
举个例子:
a() #递归零次
a(b()) #递归一次
a(b(c)()) #递归两次
上面这些都可以被匹配到
所以该正则的含义是将匹配到的数字、字母、下划线替换为空,内部可以无限嵌套相同的模式,最后是否剩下;
做着题的时候也只是想到利用
?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));这种方式来
(详解在另一篇博客,ctfshow 命令执行的web40)
但是scandir被禁用了,想着想着换成getcwd试试,不过这个函数只返回路径
然后找着php文档搜函数,期间搜到过get_defined_vars(),发现有个返回全局get变量的地方,但是不会用.......
最后还是看了师傅们的wp得了正确的解法:
利用get_defined_vars()函数返回可控的值。
该函数会返回全局变量的值,get、post、cookie...的数据
有一个要注意的点,因为a=>phpinfo()是在GET这个数组里面,所以用pos取两次数组得得到值。
第一次,取出GET数组:
第二次,在GET数组中取出a的值:
到这里差不多就ok了,既然这个phpinfo()可控,pos(pos(get_defined_vars()));又可以过正则,那我们直接把phpinfo()换成system执行命令就好了。
payload:
?a=system('cat flag.php');&code=eval(pos(pos(get_defined_vars())));