emlog博客系统的一次审计

前言

最近想做一下博客迁移,发现了emlog博客系统,发现很久没有更新,去CNVD上搜索发现了几个归档的漏洞,这里对照其中的描述进行分析下,本文中emblog版本为6.0

漏洞分析

后台sql注入(一)

漏洞点位于/admin/tag.php文件中删除标签功能处,代码如下

在进行删除操作时调用了deleteTag函数,跟进该函数

可以看到该处进行标签的删除操作时,最后执行的Delete语句中$tagid变量并没有单引号进行保护,这就存在了注入的可能,我们来尝试打印一下$tagid的值,首先在后台管理处选择一个标签进行删除,抓取数据包

可以看到$tagid的值为tag数组的下标值,由于该系统在外部传入post值时并没有做全局过滤,我们可以尝试直接在下标处构造注入语句

2 and updatexml(0x3a,concat(1,(select user())),1)#

如图

可以看到成功的进行了报错注入

后台sql注入(二)

漏洞点位于/admin/widgets.php中的保存排序功能,主要代码如下

首先我们需要传入两个变量,$wgNum会做整型处理,而$widgets传进来时只是进行了序列化处理,没有其他的限制,接着会调用updateOption方法,跟进该方法

该方法中执行了update语句,我们来打印下该处update语句,首先触发保存排序功能,抓取数据包

可以看到序列化字符串使用了单引号包裹,但是由于$widgets没有其他限制导致我们可以输入单引号进行闭合,进而插入sql语句,我们可以构造如下语句

UPDATE emlog_options SET option_value='a:3:{i:0;s:62:"archive' and updatexml(0x3a,concat(1,(select user())),1) and '";i:1;s:4:"link";i:2;s:6:"search";}' where option_name='widgets1'

实际测试看一下

可以看到成功触发了报错,带出了数据

文件上传(一)

漏洞点位于/admin/plugin.php的插件上传功能,代码如下

//上传zip插件
if ($action == 'upload_zip') {
    LoginAuth::checkToken();
    $zipfile = isset($_FILES['pluzip']) ? $_FILES['pluzip'] : '';

    if ($zipfile['error'] == 4) {
        emDirect("./plugin.php?error_d=1");
    }
    if (!$zipfile || $zipfile['error'] >= 1 || empty($zipfile['tmp_name'])) {
        emMsg('插件上传失败');
    }
    if (getFileSuffix($zipfile['name']) != 'zip') {
        emDirect("./plugin.php?error_f=1");
    }

    $ret = emUnZip($zipfile['tmp_name'], '../content/plugins/', 'plugin');
    switch ($ret) {
        case 0:
            emDirect("./plugin.php?activate_install=1#tpllib");
            break;
        case -1:
            emDirect("./plugin.php?error_e=1");
            break;
        case 1:
        case 2:
            emDirect("./plugin.php?error_b=1");
            break;
        case 3:
            emDirect("./plugin.php?error_c=1");
            break;
    }
}

该段代码调用了emUnZip函数对我们上传的zip包进行解压操作,跟进该函数看一下

/**
 * 解压zip
 * @param type $zipfile 要解压的文件
 * @param type $path 解压到该目录
 * @param type $type
 * @return int
 */
function emUnZip($zipfile, $path, $type = 'tpl') {
    if (!class_exists('ZipArchive', FALSE)) {
        return 3;//zip模块问题
    }
    $zip = new ZipArchive();
    if (@$zip->open($zipfile) !== TRUE) {
        return 2;//文件权限问题
    }
    $r = explode('/', $zip->getNameIndex(0), 2);
    $dir = isset($r[0]) ? $r[0] . '/' : '';
    switch ($type) {
        case 'tpl':
            $re = $zip->getFromName($dir . 'header.php');
            if (false === $re)
                return -2;
            break;
        case 'plugin':
            $plugin_name = substr($dir, 0, -1);
            $re = $zip->getFromName($dir . $plugin_name . '.php');
            if (false === $re)
                return -1;
            break;
        case 'backup':
            $sql_name = substr($dir, 0, -1);
            if (getFileSuffix($sql_name) != 'sql')
                return -3;
            break;
        case 'update':
            break;
    }
    if (true === @$zip->extractTo($path)) {
        $zip->close();
        return 0;
    } else {
        return 1;//文件权限问题
    }
}

该处上传插件主要用到如下代码

$plugin_name是解压后文件或文件夹的名称,$dir是在$plugin_name加上一个/,该处的判断要求就是需要我们在压缩包中存在一个文件夹并且文件夹下存在着与文件夹同名的PHP文件,满足这个条件就可以正常上传插件,我们可以构造如下的zip文件,在shell.php中写入一句话

在后台插件安装处上传zip

成功的在/content/plugins/shell/shell.php写入了一句话

文件上传(二)

该处文件上传与上面所述的原理是相同的,触发点位于/admin/template.php中的模板上传功能,关键代码如下

//上传zip模板
if ($action == 'upload_zip') {
    LoginAuth::checkToken();
    $zipfile = isset($_FILES['tplzip']) ? $_FILES['tplzip'] : '';

    if ($zipfile['error'] == 4) {
        emDirect("./template.php?action=install&error_d=1");
    }
    if (!$zipfile || $zipfile['error'] >= 1 || empty($zipfile['tmp_name'])) {
        emMsg('模板上传失败');
    }
    if (getFileSuffix($zipfile['name']) != 'zip') {
        emDirect("./template.php?action=install&error_a=1");
    }

    $ret = emUnZip($zipfile['tmp_name'], '../content/templates/', 'tpl');
    switch ($ret) {
        case 0:
            emDirect("./template.php?activate_install=1#tpllib");
            break;
        case -2:
            emDirect("./template.php?action=install&error_e=1");
            break;
        case 1:
        case 2:
            emDirect("./template.php?action=install&error_b=1");
            break;
        case 3:
            emDirect("./template.php?action=install&error_c=1");
            break;
    }
}

该处同样是调用了emUnZip函数来解压zip包,关键代码如下

根据上面的分析,可以轻松的看出该处的限制要求,该处需要我们在zip中存在一个文件夹,文件夹下要存在一个header.php文件,我们可以构造如下的文件结构,在header中写入phpinfo语句

在后台模板管理功能处上传zip包,上传后点击切换刚才上传的模板

然后访问首页

成功的执行了phpinfo

后记

该系统的两处上传利用点是比较具有代表性的,很多cms在插件或模板管理处都没有太严格的限制,导致可以利用编辑或者上传功能getshell。这也告诉我们无论开发什么都要在用户可交互的功能点处做好检测,提高系统的安全性

posted @ 2020-06-02 10:26  0DayBug  阅读(382)  评论(0编辑  收藏  举报