当前位置: 首页 > 技术干货 > 如何审计一个冷门的cms?

如何审计一个冷门的cms?

发表于:2022-01-20 16:49 作者: Lxxx 阅读数(276人)

前言

刚开始代码审计多多少少会有种无从下手的感觉,想要通过自己的代码审计能力直接审计出漏洞未免有些困难。但是如果直接拿到一个cms开始审,刚开始可能富有激情,可是越审到后面就会越怀疑自己,然后放弃代码审计。造成这样的现象的原因便是:不知道自己到底能不能审到漏洞。

那如何审出一个很少人知道,并且肯定存在的洞呢?


本文介绍的办法就是:上cnvd看看以前师傅提交的洞,虽然大多数的洞都是不公开的,但是我们还是可以根据cnvd给的一点点信息来尝试复现。

因此,本文涉及到的漏洞都是比较早被发现,危害性较低,并且已经被厂商修复的漏洞,本文仅对代码审计的过程做一个分析。


确定目标

刚开始审计,可以选择一些比较冷门的cms(内容管理系统),挑选一个自己擅长的cms语言审计,之所以选择冷门的cms是因为这些cms一般存在的安全问题会比热门cms多。

本文选用的cms是:emlog(V5.3.1),这个cms是使用PHP语言开发的个人博客管理系统

 

信息收集

确定完目标之后,就需要上cnvd搜索该cms历史存在的一些漏洞,本文演示的是emlog

如何审计一个冷门的cms?493.png
 

 

可以看到在cnvd上存在这么一些漏洞,虽然在漏洞详情页面并没有直接写漏洞是怎么形成的,但是在这里或多或少会存在一些信息。比如在哪个地方会存在什么类型的洞。

接下里挑选一个简单的漏洞进行演示

如何审计一个冷门的cms?591.png
 

 

 

复现漏洞

由上图可知,在后台页面,一般是admin目录下,有一个ta打头的文件,而我们查看一下emlog目录,唯一一个ta打头的文件就是tags.php

如何审计一个冷门的cms?672.png
 

 

而在tag.php仅有50余行代码,因此想在50行左右的代码地方找到一个SQL注入还是比较简单的

如何审计一个冷门的cms?724.png

 

首先是大概了解一下这50余行代码的作用,对于一个博客内容管理系统来说,tag.php应该是一个处理文章标签管理的文件

因此我们这个cms可能会用到这个文件的页面

如何审计一个冷门的cms?808.png
 

 

在这个页面中存在几种操作,一个是全选,一个是删除,其中点进标签后还存在标签修改的功能

如何审计一个冷门的cms?854.png
 

 

这几个功能对应在tag.php中就是这么几个if语句

如何审计一个冷门的cms?884.png

 

而执行哪一种操作是根据传入action的值来决定的

因此,接来下审计在这些if语句中可能存在的SQL注入的点

首先是第一个if判断

if ($action == '') {
$tags = $Tag_Model->getTag();
include View::getView('header');
require_once View::getView('tag');
include View::getView('footer');
View::output();
}


这一个if判断只有一个action是可控的,其余没有参数可控,因此这个函数不存在SQL注入的点

然后再看一下第二个if判断

if ($action== "mod_tag") {
$tagId = isset($_GET['tid']) ? intval($_GET['tid']) : '';
$tag = $Tag_Model->getOneTag($tagId);
extract($tag);
include View::getView('header');
require_once View::getView('tagedit');
include View::getView('footer');View::output();
}

这一个if判断相比上一个多一个tid为可控参数,不过cms对传入的tid进行了intval的转换

不过我们还是跟进getOneTag方法看一下

function getOneTag($tagId) {
$tag = array();
$row = $this->db->once_fetch_array("SELECT tagname,tid FROM ".DB_PREFIX."tag WHERE tid=$tagId");
$tag['tagname'] = htmlspecialchars(trim($row['tagname']));
$tag['tagid'] = intval($row['tid']);
return $tag;
}

getOneTag方法具有一个参数tagId,这个tagId就是前面我们自定义传入的tid,不过这个地方没法传入字符串进行闭合注入,因为在传入这个方法以前就已经intval转换了,所以这个点不存在sql注入

接下来看第三个if判断

//标签修改
if ($action=='update_tag') {
$tagName = isset($_POST['tagname']) ? addslashes($_POST['tagname']) : '';
$tagId = isset($_POST['tid']) ? intval($_POST['tid']) : '';
    
    if (empty($tagName)) {
        emDirect("tag.php?action=mod_tag&tid=$tagId&error_a=1");
    }
    
$Tag_Model->updateTagName($tagId, $tagName);
$CACHE->updateCache(array('tags', 'logtags'));
emDirect("./tag.php?active_edit=1");
}

这里相比上面多了一个tagname,这个tagname可以传入字符串,也就是有可能会存在SQL注入,不过在传入的时候,cms会对这个tagname进行一个addslashes函数转换,也就是传入的单引号以及一些特殊符号会被转译。

我们这边抓包尝试一下

首先查看一下现在的标签,此时有两个标签tag112以及tags2123

如何审计一个冷门的cms?2485.png
 

 

上面提到的tagname其实就是标签的名字,我们抓包将tag112修改为tag112',尝试能否闭合

如何审计一个冷门的cms?2539.png
 

 

这时候我们返回主页面

如何审计一个冷门的cms?2553.png
 

 

发现此时标签变为了tag112',所以单引号是被转义了,没法直接进行SQL注入。

当然,如果数据库的编码为GBK,那么可以尝试宽字节注入,不过默认使用的数据库为utf8,因此无法使用宽字节注入。

接下来看第4个if判断

//批量删除标签
if ($action== 'dell_all_tag') {
$tags = isset($_POST['tag']) ? $_POST['tag'] : '';

    LoginAuth::checkToken();

if (!$tags) {
emDirect("./tag.php?error_a=1");
}
foreach ($tags as $key=>$value) {
$Tag_Model->deleteTag($key);
}
$CACHE->updateCache(array('tags', 'logtags'));
emDirect("./tag.php?active_del=1");
}

这个地方POST传入了一个tag,是我们的可控参数,并且这里没有对tag进行转义,所以我们再往下看是否存在SQL注入

其中我们传入的tag最终会成为tags数组中的key,仔细看一下foreach循环中,传入的deleteTag方法的参数是数组中的key,而在PHP中,数组的key并不一定需要是数字,也可以是一个字符串,只要keyvalue对应即可。

我们这个时候先不着急抓包,先看看deleteTag方法是如何实现的。

跟进deleteTag方法

function deleteTag($tagId) {
$this->db->query("DELETE FROM ".DB_PREFIX."tag where tid=$tagId");
}

可以发现,SQL语句执行的时候也是没有进行任何过滤的。

这里我们开始抓包

如何审计一个冷门的cms?3365.png
 

 

可以看到post传入了tag[1],这里我们对方括号中的1开始注入,这里也不需要引号闭合,直接注即可。

我使用的是报错注入

tag%5B1%20and%20updatexml(0,concat(0x7e,version()),1)%20%23%5D=1&token=c16eaff79a17d690f5c0caae66276085


 如何审计一个冷门的cms?3534.png

 

看下方的结果,已经把数据库的版本爆出来了。

如何审计一个冷门的cms?3559.png
 

 

总结

本文所复现的是在后台的SQL注入,利用难度较大,危害性较低。并且审计这个cms并不是直接拿一个cms从头开始审,而是根据在cnvd中存在的一些模糊信息进行审计,来提高自己复现这个漏洞的成功率,不至于在一开始就碰壁,而能在相对短的时间里获得较大的成就感,提高学习效率。