emlogcms审计

emlog6.0.0 &pro2.1.9&&pro2.2.0

emlog 6.0.0

一.环境

二.sql注入漏洞

分析

comment.php文件里面没有对变量$action的定义,看到有加载globals.php

是get接受action参数,且addslashes过滤

漏洞在这段代码处,$ip接收的参数没有进行过滤,可控,接着跟进delCommentByIp

单引号闭合,并且只进行sql语句执行,没有输出,所以我们在注入的时候就得采取报错注入或盲注了

尝试,报错权限不足

找到代码发现,报错的原因是还要接受一个后台登陆成功后的token

返回登录界面获得token

它输出了sql语句,说明参数全了能成功去执行sql语句

利用

上面说没有返回执行sql的结果没有输出,就盲注或报错注

但总的来说它得进后台获得token才能注入就很尴尬了

三.任意文件删除漏洞

任意删除1

分析

admin/data.php 中,未过滤bak[]

  • unlink() 函数删除文件。

    若成功,则返回 true,失败则返回 false。

利用

在src目录下创建deled.txt文件

post 提交参数bak[],进行跨目录删除文件

  • 注意参数是数组啊!!

deled.txt成功删除

任意删除2

分析

还是危险函数unlink,参数$icon_1没过滤

但参数$icon_1是从数据库查询字段photo后返回的结果,考虑怎么把任意路径写进去

看到update功能可以操作一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
if ($action == 'update') {
LoginAuth::checkToken();
$User_Model = new User_Model();
// 通过POST参数控制photo字段,可以传入任意文件路径
$photo = isset($_POST['photo']) ? $_POST['photo'] : '';
$nickname = isset($_POST['name']) ? addslashes(trim($_POST['name'])) : '';
$email = isset($_POST['email']) ? addslashes(trim($_POST['email'])) : '';
$description = isset($_POST['description']) ? addslashes(trim($_POST['description'])) : '';

$login = isset($_POST['username']) ? addslashes(trim($_POST['username'])) : '';
$newpass = isset($_POST['newpass']) ? addslashes(trim($_POST['newpass'])) : '';
$repeatpass = isset($_POST['repeatpass']) ? addslashes(trim($_POST['repeatpass'])) : '';

// 移除对photo的addslashes处理,允许传入任意路径
if (strlen($nickname) > 20) {
emDirect("./blogger.php?error_a=1");
} else if ($email != '' && !checkMail($email)) {
emDirect("./blogger.php?error_b=1");
} elseif (strlen($newpass)>0 && strlen($newpass) < 6) {
emDirect("./blogger.php?error_c=1");
} elseif (!empty($newpass) && $newpass != $repeatpass) {
emDirect("./blogger.php?error_d=1");
} elseif($User_Model->isUserExist($login, UID)) {
emDirect("./blogger.php?error_e=1");
} elseif($User_Model->isNicknameExist($nickname, UID)) {
emDirect("./blogger.php?error_f=1");
}

if (!empty($newpass)) {
$PHPASS = new PasswordHash(8, true);
$newpass = $PHPASS->HashPassword($newpass);
$User_Model->updateUser(array('password'=>$newpass), UID);
}

if (!empty($login)) {
$User_Model->updateUser(array('username'=>$login), UID);
}

// 直接使用传入的photo路径,不进行文件上传处理
$usericon = $photo;

// 更新用户信息,将photo字段设置为任意路径
$User_Model->updateUser(array('nickname'=>$nickname, 'email'=>$email, 'photo'=>$usericon, 'description'=>$description), UID);
$CACHE->updateCache('user');
emDirect("./blogger.php?active_edit=1");
}

if ($action == 'delicon') {
LoginAuth::checkToken();
$DB = Database::getInstance();
// 从数据库获取photo字段,此时photo字段已被设置为任意路径
$query = $DB->query("select photo from ".DB_PREFIX."user where uid=" . UID);
$icon = $DB->fetch_array($query);
$icon_1 = $icon['photo'];
// 直接删除指定路径的文件
if (file_exists($icon_1)) {
unlink($icon_1);
}
$DB->query("UPDATE ".DB_PREFIX."user SET photo='' where uid=" . UID);
$CACHE->updateCache('user');
emDirect("./blogger.php?active_del=1");
}

主要看这里!!!更新的photo字段是变量$usericon接受未过滤的photo

1
2
3
4
5
6
 $photo = isset($_POST['photo']) ? $_POST['photo'] : '';  

// 直接使用传入的photo路径,不进行文件上传处理
$usericon = $photo;
// 更新用户信息,将photo字段设置为任意路径
$User_Model->updateUser(array('nickname'=>$nickname, 'email'=>$email, 'photo'=>$usericon, 'description'=>$description), UID);

利用

$action=update $action=delicon

在plug.php的上级目录创建文件x.txt

update

delicon

成功删除x.php

四.文件上传漏洞

分析

$zipfile变量没有经过过滤,直接接受上传的文件信息

对接受的文件名用getFileSuffix函数获取文件后缀名判断是否是zip,之后解压到../content/plugins目录下

跟进emUnZip函数

利用

直接上传,发现

从源码中找到报错的出处,emUnZip返回了-1

让ai分析一下,返回-1是因为压缩文件中,没有插件目录名!!!,先把emlog.php文件放在同名的文件夹下面,再进行压缩就可以了

参考文章:

https://forum.butian.net/share/2285

http://www.52bug.cn/hkjs/5666.html

——————-

emlog_pro 2.1.9

一.sql注入(备份数据库二次注入)

跟踪危险函数找到sql语句执行找注入

根据数据库监控找注入

数据库监控

https://gitcode.com/gh_mirrors/my/MySQL-Monitor/?utm_source=artical_gitcode&index=top&type=href&&isLogin=1

再项目目录下创建一个文件夹存放数据库监控

注入点的寻找:

  • 从漏洞报告中知道漏洞文件目录 /admin/user.php

  • 【从执行的sql语句中入手找对应的代码段分析】根据数据库监控找sql注入点:访问user.php页面加载数据包执行sql语句,在数据库监控中寻找可疑的sql语句(有可控点的)

    1
    SELECT COUNT(*) AS total FROM emlog_user where 1=1  #这个就是已经写死的,不可控

    asdf是登陆的用户名,锁定他在源码中找是否可控,分析它从哪里来

  • 在源码中正则表达式找sql语句出处,分析参数是否过滤,从哪里可以注入

寻找可控出处

代码分析参数

$condition如果可控的话,这条sql就可控,那就在源码中去分析

可以看到$condition是与$email $nickname有关的

而$email $nickname赋为空值,从代码上看这里是控制不了了

添加用户尝试控制

但回看上面数据库监控的sql语句的可控的点是我们后台管理员登陆的用户名,想的是再创建一个是payload的用户名,但是欸,创建用户不能自己创建用户名

用户名是随机的,但是我们也知道了这个操作的参数active_add

跟进去看确实用户名是随机产生的


到这里我们审计代码是没有找到可利用的地方能控制$condition进行sql语句拼接的,就想到二次注入,在网站上寻找与数据库相关的功能点

找到备份数据库,再备份文件中发现刚刚操作的sql语句,现在猜想导入备份会不会执行sql语句

所以从代码上面看主要漏洞是在这里data.php 导入数据库备份时执行sql语句

利用

将payload添加进备份文件中

导入备份文件,成功执行

——————-

emlog_pro 2.2.0

CNVD-2023-74536

任意文件上传漏洞1

报告里写content/templates,可以直接根据这里找模板的操作(一个模板是一个文件夹),所以漏洞大概与模板有关

php文件上传的固定全局变量$_FILES接受上传的文件信息,文件上传处理函数move_uploaded_file

文件上传与数据库监控的关联:
当上传文件路径写入数据库中是用数据库监控找

分析

通过关键字定位代码:
搜安装/上传,找到view/templates.php这个文件,对应处理逻辑的文件是admin/templates.php

可以看到它和之前逻辑差不多,上传成功解压到../content/templates/目录下,但自定义的emUnZip函数肯定之前6.0.0相比检测方式不同了

解压函数会检测里面的header.php文件

所以之前6.0.0的利用肯定不能用,emUnZip函数返回-2,因为没有检测到header.php

利用

这里直接将php文件和header.php放在一个文件夹(文件夹的名字就是模块的名字)下面直接压缩就好了

上传成功

任意文件上传2

和刚刚这个代码逻辑是一样的

对admin/plugin.php文件进行分析,大多都一样,但检测方式发生了变化,刚刚是按照模板,现在是插件

然后注意这里就不是检测header.php文件了,检测和6.0.0的一样要求目录下面存在一个与目录名相同的php文件

上传成功

最简单的方法就是找现有的插件,直接利用它往里面放php文件


emlogcms审计
https://bxhhf.github.io/2025/05/29/Emlog代码审计/
作者
bxhhf
发布于
2025年5月29日
许可协议