PHP CMS 代码审计实战,RCE, SQL注入,任意文件删除,任意文件上传。
开始
日常打开fofa, 确认目标后,右键检查页面源代码。没有版权信息无法确认是哪个cms. 确定特征值后再fofa(这样找到源码的记录会大大提升) ,翻来翻去, 找到了疑似官方演示站点。
进行子域名收集, 收集到三个子域名
先翻一翻. 在开源信息页面找到了项目提供的源码地址。
V1重复安装覆写配置文件导致RCE
该cms用了低版本的 mysql_connect
语法. 我这里的PHP用了 5.4.45
, 搭建好后不会自动跳转到安装页面. 只能手动跳转 /install/
。
正常安装一遍后, 我刷新页面. 再次进入了安装选项页面,可以再安装一次. 开始分析源码。
看源码是有安装检测的. 但这个文件我却没找到, 目录中根本没有 source
文件夹。
获取到参数时,只是简单删除前后空字符. 没有验证消毒等操作(往后也没有)。
整个配置文件内容定义在 $config_contents
变量中( <?php return array(); ?>
), 参数也只是普通的字符串拼接起来, 参数可控,如果能逃逸就rce了。
这里选择从数据库名称$dbname
下手, 服务器连接地址, 数据库用户名密码不好用特殊字符, 而数据库会检测是否存在如果不存在就会创建, 但是创建数据库这里也没法使用特殊字符。
除非数据库名称用反引号包裹
create datab ase `datab ase name`;
create datab ase `test'1`;
准备一个远程 mysql
服务器, 创建好特殊名称的数据库. 然后重新安装站点
CREATE DATAb ase `'.e val($_GET[333])//`;
安装成功.
打开页面,_mkdir()
方法未定义
非常幸运, _mkdir()
的调用,在 e val
的后面.
跳转到定义函数, 复制函数的定义的语法就好了. 不用复制函数内容
payload 如下
function _mkdir($dir,$mode=0777){};system('whoami');
V2任意文件删除漏洞+安装覆写配置文件导致RCE
配置文件覆写导致RCE
初打开页面后是个报错. 丢失Config.php
盲猜是没有安装所以没有生成该文件。
手动跳转 /install/
, 这次有安装检测了. 安装的话需要先删除install
目录下的lock
文件
打开/install/index.php
, write_config()
是写入配置的函数. 依旧是没有进行任何过滤消毒. 和V1
不同的是他这次不采用字符串拼接的方式了. 而是使用替换, 配置文件的目录改为/ERP/Config/Config.php
了。
看一下文件模板/install/data/config.php
,虽然改成替换字符. 但没有验证消毒等操作. 问题还是存在的
而且在 /install/index.php
111行中创建数据库的语句加上了 反引号, 输入特殊字符也能执行成功. 不需要提前准备数据库了
删除/install/lock
文件, 尝试安装, 验证是否存在漏洞
使用和V1同样的payload, 成功执行
这时如果有办法删除/install/lock
文件, 就可以无条件RCE了
任意文件删除漏洞
全局搜索unlink
由是这个Upload.class.php
, 可以看到只是拼接了一个 .
而非./
,所以并没有做到对路径的限制。
跟进 _REQUEST()
方法, 够方便的...,如果GET
中没有数据, 那就在POST
中拿数据。
跟进unlink_file()
方法, 还是没有对参数进行验证,简单判断文件是否存在就执行unlink()
删除文件了
dir_replace()
方法只是修理一下目录分割符。
尝试访问index.php/Upload/upload_img_remove/
, 存在未授权访问. 报错是因为没法删除 .
,
组合RCE
访问/index.php/Upload/upload_img_remove/?imgfile=/install/lock
, 成功删除/install/lock
文件
再次访问/install/
, 成功进入安装界面。
再次安装站点,和V1
一样的数据库名称. 但这次不需要自己创建了。
(V1、V2)SQL注入漏洞(同一个数据库抽象类)
整个cms, SQL语句和参数几乎都是用字符串拼接的. 而且是拿到参数后直接拼接. 没有任何验证消毒等操作。
跟进Framk/Datab ase.class.php->Datab ase->findAll()
方法, 使用了PDO
,但同样都没有过滤直接将拼接好的SQL语句去执行,
(V1、V2)未授权访问任意文件上传漏洞(同一个基类)·
全局搜索move_uploaded_file
发现了, Crm/Action/Upload.class.php->Upload->upload_img_save()
. 他分离了文件名和后缀名并没有验证. 而是重新生成文件名后拼接原本的文件后缀. 121行的 $pictype
也没有进行验证。
根据他的路由习惯, 直接访问 /index.php/Upload/upload_img_save/
,直接为我们写好了html
上传个phpinfo()
,试一下. 上传成功后路径有回显
成功解析
- 本文作者: JOHNSON
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/341
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!