您的位置 首页 php

由MetInfo 深入理解PHP变量覆盖漏洞

前言

PHP变量覆盖一般是指一种使用自定义变量替换程序原有变量值的漏洞,这种漏洞本身不会造成太大的漏洞,但是PHP变量覆盖往往会导致程序的执行逻辑发生改变,使程序原有的安全机制发生改变,导致产生其他安全问题。本文首先介绍一些变量覆盖的常见情景,再通过一个代码审计实例深入介绍如何利用。

变量覆盖原理

常见的造成的代码审计的情景是代码中出现以下关键词:

1、register_globals

register_globals是指将传递过来的值注册为全局变量,所以当On的时候,传递过来的值会被直接的注册为全局变量直接使用,而Off的时候,我们需要到特定的数组里去得到它。

当为off时

当为on时(PHP从4.2版本开始将register_globals默认值设置为off,从5.3版本开始废弃,从5.4版本移除,所以当PHP<=5.3时才能运行成功)

通过这种方式就可以向程序注册一个之前没有声明的变量,不过如果之前变量已经存在就无法覆盖掉,如下所示

2、extract

extract() 函数从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。extract函数接收三个参数, 返回成功导入到符号表中的变量数目。

示例代码及运行结果

可见变量a、b、c的值已经被覆盖,即使在这之前已经声明也会被覆盖。这与第二个参数有关,如果选用EXTR_SKIP则不覆盖

3、parse_str

parse_str() 函数把查询字符串解析到变量中。该函数接收两个参数

示例代码及运行结果

如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。

4、 import_request_variables

import_request_variables将 GET/POST/ Cookie 变量导入到全局作用域中,即使关闭了register_globals依然会生效,它接收两个参数,

$types表示变量类型,表示GET/POST/Cookie三者,一般使用首字符(GPC)表示不区分大小写,当$type=gp时POST 变量将使用相同的名字覆盖 GET 变量。$prefix表示变量前缀,示例代码及运行结果

5、$$

使用 foreach 来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。这样就会导致已声明或未声明的值被覆盖,示例代码及运行结果。

代码审计实例

1、基本信息

名称: MetInfo CMS 5.3

简介:MetInfo 是一款可视化操作,SEO 非常友好、功能全面、采用 PHP + Mysql 架构、多语言、响应式展示,适合企业、公司网站建设的 cms 建站系统。这款CMS在5.X版本由于系统架构存在根本缺陷,导致漏洞频发,其中之一就是存在上节第五部分使用foreach赋值的结构,导致大量代码存在变量覆盖的风险。新发布的6.0版本进行了代码重构,修复大部分已知的安全漏洞。

2、变量覆盖

/ include /common.inc.php是核心的公共文件,在第25行至40行有这么一段代码,用以将传入的cookie、get、post参数进行变量赋值

这实际上是一种极易造成变量覆盖的写法,任何在这段代码之前已声明或未声明变量如果在这段代码之后没有被重新赋值就会被覆盖掉。实验如下

这一点的变量覆盖会带来很多其他的安全问题。

1、密码重置

MetInfo有管理员密码重置的功能,此功能由于变量覆盖导致可以获取密码重置链接。

密码找回相关的文件是admin/admin/getpassword.php

前面十几行是从数据库查询配置信息并从赋值给变量,同时可以看到引入了common.inc.php文件。从第47行的switch语句开始是密码找回的逻辑控制语句

第一步选(next1)择密码找回的方式,默认是邮件找回,然后第二步(next2),主要从92行开始执行,前面一大段是拼接数据包的语句,从第143行开始发送邮件

如果jmailsend函数发送失败则进入下面的if语句使用curl_post发送,进入curl_post函数,在文件/include/export.func.php中

其中$post是未能成功发送的邮件内容,然后根据$met_host指定的地址将邮件内容发送过去,$met_host 在程序的值指定为app.metinfo.cn,应该是metinfo官方设置的邮件转发服务器,当站长自身设置的邮件服务器不起作用时先将邮件内容通过http请求发送此服务器,再由此服务器发送密码重置邮件。但是这个$met_host的值由于变量覆盖的原因导致可以被任意设置,可以将密码重置邮件的内容发送到我们指定的服务器。前提是jmailsend发送失败,这里再来看一下jmailsend函数,在文件/include/jmail.php中。

我们的目的是使这个函数返回False,这里同样使用变量覆盖的方式加以利用,在19行可以可以看到根据$met_fd_port指定的端口发送邮件,这里如果将这个变量覆盖掉将导致邮件发送失败。首先在服务器上设置监听端口,curl_post默认80端口

然后构造如下payload

点击发送,即可在自己的服务器上收到邮件内容,其中包含密码重置链接

打开链接,如下所示

4、Getshell- CVE-2017-11347

漏洞文件位于admin/app/physical/physical.php

代码根据$action的值决定后面的执行逻辑,

当$action等于op且后面switch语句的$op的值为3则进入下面的语句

再根据$val控制后面的switch语句,以此决定了$address的值。

然后跟进位于/admin/include/global.func.php的Copyfile函数

它会将$oldcont写入到新文件$newfile中,其内容为

可以看到里面使用require_once ‘$address’;包含了$address的值。那么我们回溯一下$newfile和$address值,其中在256行$newfile从$val数组中获取值,$address在236行~252行的case语句获取值。$val数组在186行生成,由$valphy变量切割成数组,而$valphy可以被变量覆盖,这就导致后面的整个语句都可以被控制。首先$action=op且$op=3,然后根据186行的语句$valphy要构造如下形式$valphy=test|123/shell.php,这样$val就变成了一个长度为2的数组,$address在236行~252行的case语句中也就无法获取值,导致也可以被覆盖。所以我们可以先上传一个带有正常文件后缀的shell,然后将其地址赋值给$address,生成的新文件shell.php就会调用它。

然后构造如下payload

|123/shell.php&address=../upload/201803/1521469553138949.jpg

然后就会在123目录下生成shell.php

声明

文章旨在普及网络安全知识,提高小伙伴的安全意识的同时介绍常见漏洞的特征、挖掘技巧等。若读者因此做出危害网络安全的行为后果自负,与合天智汇及本人无关,特此声明。

注:本文为合天原创奖励投稿,未经允许,严禁转载

文章来源:智云一二三科技

文章标题:由MetInfo 深入理解PHP变量覆盖漏洞

文章地址:https://www.zhihuclub.com/151333.shtml

关于作者: 智云科技

热门文章

网站地图