环境搭建:
首先我们来分析该系统的路由信息,以及如何进行参数的构造。
路由分析:
该系统有两个路由,一是前台功能点路由,二后台功能点路由,但两个路由代码类似只不过后台路由添
加了session校验,我们先来看看前台路由是怎么构造的。
前台路由放在api.php文件中。
在common.php中22行代码处中调用__autoload() 魔术方法来加载 Model 文件夹下的功能代码,方便后续路由的调用。在代码30行去除 get_magic_quotes_gpc() 方法对特殊字符加载的反斜杠,这可能是为了代码的兼容性。
代码5、6两行传入两个参数 ctrl 、 action ,第7行代码其实就是将 action 传过来的参数首字母转换为
大写,因为类名首字母都是大写的,第8行判断该类是否为 Api 或 Comment。
后台路由代码 admin.php 文件与前台路由代码基本类似,只是在上面添加了session校验,检测是否为
登录状态。
漏洞审计
1.任意文件读取/下载
通过上面的路由信息我们知道功能点文件存放在Model文件夹下,我们去翻找Model文件夹发现
file.php 文件也就是File这个类下存在一个 download() 方法。
在这个类中的第85行代码处,我们一目了然的看到了 file_get_contents() 函数,看到这个函数想要
利用,我们会下意思的想到两个点:第一该函数的参数是否可控;第二该函数是没有回显的,如果想要
利用是需要使用 echo 等函数配合。我们只需要查看这里file_get_contents() 中参数是否可控就可以了。
漏洞复现:
由于在上面我们已经分析过路由的构造,所以我们可以不用特意去找功能点就能构造出利用路由。
在路由中 action 传入的是我们要实例化的类名 file , ctrl 则对应我们需要调用的方法 download 。
2.任意文件上传
首先我们去创建一个.php后缀的文件
通过这里我们发现 executeupload() 方法中调用了Upload类下的 upload() 方法,这里的上传主要调
用了upload()方法,我们主要去看下他是如何进行过滤的。
在该方法的最上面定义了 $upext 变量,这里包含了可以上传的后缀名,也就是白名单,大致看了这些后缀没有可利用的。然后下面通过 $_FILES 接收上传文件,
通过 pathinfo() 获取上传文件名。
关键在于代码106行通过 [extension] 获取后缀名,然后到代码107行进行正则匹配如果上传的文件名
不在 $upext 白名单中,则返回下面的提示信息。
这里上传是走不通的,但是在上传的右边有一个创建文件的功能点,我们发现这里竟然没有限制可以上传任意文件
在 create() 方法中,首先接收文件名 name ,然后通过 isdir 来判断创建的是目录还是文件,然后分别做不同的操作进行创建。
然后我们在看看他是如何进行文件写入的,其实下面的功能点就可以直接写入文件内容
其实这里写入内容的代码也在 File 这个类中,在 save() 方法中只是对该文件是否具有写入权限进行判断,就直接将内容写入到文件中。
漏洞复现:
3.mysql日志文件getshell
在Sql类下的 excute() 方法,依旧的简洁明了。通过14行传入 $sqltext 参数也就是我们的SQL语句,
在15行实例化 Dbclass 类调用其中 query() 方法直接执行SQL语句。最后18行将我们SQL语句结果进行
打印输出。
漏洞复现:
MySQL日志文件getshell
Mysql 5.6.34版本以后无法通过into outfile、into dumpfile进行文件写入
我们通过日志文件写shell即可
set global general_log = on;
set global general_log_file = '网站绝对路径';
4.通过修改配置文件getshell
在后台有这么一个 网站设置 功能点,大致一看这里的内容和config.php文件中内容是一致的
首先我们去查看代码该功能点代码,这里调用 upload() 方法,第53行直接判断config.php是否可写,
然后通过POST接收参数,但是这里参数值会被57行代码处的 safeword() 方法进行过滤,跟进该方法看
看是如何对输入内容进行过滤的。
在 safeword() 方法中需要传入两个参数,一个是需要过滤的字符串,另一个参数则决定走哪个case。
上面没有给出第二个参数则直接走默认level,也就是154行下面的代码,在155行判断了数据库类型是否
为 Sqlite 是的话执行Sqlite的过滤代码,如果不是则走158行的else,,调用 Base::_addslashs() 方法,跟进该方法。
将传入的字符通过 addslashes() 函数将特殊字符添加反斜杠,无法绕过限制。
所以我们只能走上面的if条件,这里只要数据库为 Sqlite ,下面的单引号会被替换为两个单引号(当时以
为将单引号替换为双引号了),而这个替换方式是可以被绕过的。
然后我们返回 upload() 方法的第60行,直接将过滤后的内容通过 file_put_contents() 写入到config.php中。
通过分析源码,我们知道输入的单引号会替换为两个双引号,如果我们输入 \' 这样在替换为两个双引号的时候第一个双引号前会有一个反斜杠,那么我们就可以闭合前面的双引号,我们的payload可以构造为:
\');@eval($_REQUEST[1]);/*