0x01
漏洞概述
该漏洞最早出现在emlog v1.7,影响范围 emlog v1.7-emlog v2.1.15
emlog官网最新版本为emlog v2.23
emlog 是 "Every Memory Log" 的简称,意即:点滴记忆。它是一款基于PHP语言和MySQL数据库的开源、免费、功能强大的个人或多人联合撰写的博客系统(blog)。基于PHP和MySQL的功能强大的博客及CMS建站系统。致力于提供快速、稳定,且在使用上又极其简单、舒适的博客服务。安装和使用都非常方便
官网 https://www.emlog.net/
0x02
漏洞分析
本次漏洞主要是emlog缓存机制导致
0x03
emlog的缓存机制
emlog使用的缓存机制,会将网站运行的一些数据以php序列化的形式存储在本地文件中,访问时再将其反序列化,以减少对数据库的请求,加快访问速度。
缓存机制的代码主要在include/lib/cache.php中,以下是存储缓存以及读取缓存的代码(include/lib/cache.php, 78~107行)
`public function cacheWrite($cacheData, $cacheName) {`
`$cacheFile = EMLOG_ROOT . '/content/cache/' . $cacheName . '.php';`
`$cacheData = "
if (!file_put_contents($cacheFile, $cacheData)) {
emMsg('写入缓存失败,缓存目录(content/cache)不可写');
}
$this->{$cacheName . '_cache'} = null;
`}`
`public function readCache($cacheName) {`
`if ($this->{$cacheName . '_cache'} != null) {
return $this->{$cacheName . '_cache'};`
`}`
$cachefile = EMLOG_ROOT . '/content/cache/' . $cacheName . '.php';
if (!is_file($cachefile) || filesize($cachefile) <= 0) {
if (method_exists($this, 'mc_' . $cacheName)) {
$this->{'mc_' . $cacheName}();
}
}
if ($fp = fopen($cachefile, 'r')) {
$data = fread($fp, filesize($cachefile));
fclose($fp);
clearstatcache();
$this->{$cacheName . '_cache'} = unserialize(str_replace("
return $this->{$cacheName . '_cache'};
}
`}`
在存储缓存时,会将序列化后的字符串存到文件中,为了防止被直接访问,作者将文件设置成了php文件,而且在文件头部添加了<?php exit;//,这样在被直接访问到时,也不会泄露序列化后的数据。 当需要读取缓存数据时,直接读取文件的内容,并将<?php exit;//去掉,反序列化即可。
但在读取缓存时,这样的设计就存在问题:攻击者可以将要序列化的变量内容设置为<?php exit;//,并写入缓存文件,在读取缓存时,将会导致反序列化时的字符串逃逸,构成反序列化漏洞
0x04
alias文章别名缓存
在include/lib/cache.php, 404~413行,这里将从数据库中查到的别名alias存到数组中,之后将该数组序列化后缓存到文件,如果数据库中的别名是我们可控的,那么这里将可以使用上面的方法来进行反序列化攻击
`/**`
`*文章别名缓存*/`
`private function mc_logalias() {`
`$sql = "SELECT gid,alias FROM " . DB_PREFIX . "blog where alias!=''";`
`$query = $this->db->query($sql);`
`$log_cache_alias = [];`
`while ($row = $this->db->fetch_array($query)) {`
`$log_cache_alias[$row['gid']] = $row['alias'];`
`}`
`$cacheData = serialize($log_cache_alias);`
`$this->cacheWrite($cacheData, 'logalias');`
`}`
在admin/article_save.php,这里可以通过新建blog或者更新blog的方式,来将alias插入数据库中
` `
`/**`
* `article save and update`
* `@package EMLOG`
* `@link https://www.emlog.net`
`*/`
`/**`
* `@var string $action`
* `@var object $CACHE`
`*/`
`require_once 'globals.php';`
`if (empty($_POST)) {`
`exit;`
`}`
`$Log_Model = new Log_Model();`
`$Tag_Model = new Tag_Model();`
`$title = Input::postStrVar('title');`
`$postDate = isset($_POST['postdate']) ? strtotime(trim($_POST['postdate'])) : time();`
`$sort = Input::postIntVar('sort', -1);`
`$tagstring = isset($_POST['tag']) ? strip_tags(addslashes(trim($_POST['tag']))) : '';`
`$content = Input::postStrVar('logcontent');`
`$excerpt = Input::postStrVar('logexcerpt');`
`$alias = Input::postStrVar('alias');`
`$top = Input::postStrVar('top', 'n');`
`$sortop = Input::postStrVar('sortop', 'n');`
`$allow_remark = Input::postStrVar('allow_remark', 'y');`
`$password = Input::postStrVar('password');`
`$cover = Input::postStrVar('cover');`
`$link = Input::postStrVar('link');`
`$author = isset($_POST['author']) && User::haveEditPermission() ? (int)trim($_POST['author']) : UID;`
`$ishide = Input::postStrVar('ishide', 'y');`
`$blogid = Input::postIntVar('as_logid', -1); //自动保存为草稿的文章id`
`if (isset($_POST['pubPost'])) {`
`$ishide = 'n';`
`}`
`if (!empty($alias)) {`
`$logalias_cache = $CACHE->readCache('logalias');`
`$alias = $Log_Model->checkAlias($alias, $logalias_cache, $blogid);`
`}`
`//管理员发文不审核,注册用户受开关控制`
`$checked = Option::get('ischkarticle') == 'y' && !User::haveEditPermission() ? 'n' : 'y';`
`$logData = [`
`'title' => $title,`
`'alias' => $alias,`
`'content' => $content,`
`'excerpt' => $excerpt,`
`'cover' => $cover,`
`'author' => $author,`
`'sortid' => $sort,`
`'date' => $postDate,`
`'top ' => $top,`
`'sortop ' => $sortop,`
`'allow_remark' => $allow_remark,`
`'hide' => $ishide,`
`'checked' => $checked,`
`'password' => $password,`
`'link' => $link,`
`];`
`if (User::isWiter()) {`
`$count = $Log_Model->getPostCountByUid(UID, time() - 3600 * 24);`
`$post_per_day = Option::get('posts_per_day');`
`if ($count >= $post_per_day) {`
`emDirect("./article.php?error_post_per_day=1");`
`}`
`}`
`if ($blogid > 0) {`
`$Log_Model->updateLog($logData, $blogid);`
`$Tag_Model->updateTag($tagstring, $blogid);`
`} else {`
`$blogid = $Log_Model->addlog($logData);`
`$Tag_Model->addTag($tagstring, $blogid);`
`}`
`$CACHE->updateArticleCache();`
`doAction('save_log', $blogid);`
`// 异步保存`
`if ($action === 'autosave') {`
`exit('autosave_gid:' . $blogid . '_');`
`}`
`// 保存草稿`
`if ($ishide === 'y') {`
`emDirect("./article.php?draft=1&active_savedraft=1");`
`}`
`// 文章(草稿)公开发布`
`if (isset($_POST['pubPost'])) {`
`if (!User::haveEditPermission()) {`
`notice::sendNewPostMail($title);`
`}`
`emDirect("./article.php?active_post=1");`
`}`
`// 编辑文章(保存并返回)`
`$page = $Log_Model->getPageOffset($postDate, Option::get('admin_perpage_num'));`
`emDirect("./article.php?active_savelog=1&page=" . $page);`
找一下关键代码
`$alias = Input::postStrVar('alias');`
`...`
`// 将传入的数据放到数组中`
`$logData = [`
`'alias' => $alias,`
`...`
`];`
`...`
`// 新建文章,将数据插入数据库,或者更新文章,更新数据库`
`$blogid = $Log_Model->addlog($logData);`
`// $Log_Model->updateLog($logData, $blogid);`
`// 更新缓存`
`$CACHE->updateArticleCache();`
在include/lib/cache.php:79行定义了updateArticleCache(),其包含了更新logalias的缓存
`public function updateArticleCache() {`
`$this->updateCache(['sta', 'tags', 'sort', 'newlog', 'record', 'logsort', 'logalias']);`
`}`
到这一步发现可以通过新建文章和更新文章来控制文章别名的缓存
0x05
漏洞复现
先注册一个用户,登录后发布两篇文章(草稿),从返回结果可以获得文章的id,这里文章id分别是4和5
先修改id为5的blog,设置其alias为a";i:4;s:1:"x
同理,修改id为4的,设置alias为<?php exit;//
发送完两个数据包后,alias的缓存文件为content/cache/logalias.php,内容如下
`exit;//a:2:{i:4;s:13:" exit;//";i:5;s:13:"a";i:4;s:1:"x";}`
在读取缓存时,先将<?php exit;//替换为空,再进行反序列化,反序列化字符串为
a:2:{i:3;s:13:"";i:4;s:13:"a";i:4;s:1:"x";}
通过反序列化,结果为
`array(2) {`
`[3]=>`
`string(13) "";i:4;s:13:"a"`
`[4]=>`
`string(1) "x"`
`}`
到这里,其实可以很明显的看到反序列化字符逃逸漏洞的存在。
免责声明
Drt安全战队
由于传播、利用本公众号Drt安全战队所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号Drt安全战队及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉,谢谢!
原文始发于微信公众号(Drt安全战队):【漏洞分析复现】emlogV2.1.15存在反序列化漏洞 CVE-2023-43291
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论