美特CRM getFile 任意文件读取与反序列化漏洞


漏洞简介

MetaCRM是一款智能平台化CRM软件,通过提升企业管理和协同办公,全面提高企业管理水平和运营效率,帮助企业实现卓越管理。美特CRM getFile 接口存在任意文件读取漏洞和fastjson反序列化漏洞。

影响版本

CRM6.5

fofa语法

body="/common/scripts/basic.js" && body="www.metacrm.com.cn"

漏洞分析

先看 web.xml 里对于 getFile 接口的定义

<!-- 流的方式展示文件 -->
    <servlet>
        <servlet-name>getFile</servlet-name>
        <servlet-class>com.metasoft.framework.controller.getFile</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>getFile</servlet-name>
        <url-pattern>/getFile</url-pattern>
    </servlet-mapping>

进入 com.metasoft.framework.controller.getFile 看下其实现逻辑

以及系统的fastjson版本 fastjson-1.2.4.jar,是存在漏洞的版本

看下AES加解密的部分

public class AesEcbCipher {
    private static final String SECRET_KEY = "metacrmloginpass";
    private byte[] key = "metacrmloginpass".getBytes();

    public AesEcbCipher(String secretKey) {
        this.key = secretKey.getBytes();
    }

    public AesEcbCipher() {
    }

    public String Encrypt(String sSrc) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(this.key, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(1, skeySpec);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes("UTF-8"));
            return (new BASE64Encoder()).encode(encrypted);
        } catch (Exception ex) {
            Debug.error("", ex);
            return null;
        }
    }

    public String Decrypt(String sSrc) {
        if (sSrc != null && sSrc.length() != 0) {
            try {
                SecretKeySpec skeySpec = new SecretKeySpec(this.key, "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(2, skeySpec);
                byte[] encrypted1 = (new BASE64Decoder()).decodeBuffer(sSrc);
                byte[] original = cipher.doFinal(encrypted1);
                return new String(original, "UTF-8");
            } catch (Exception ex) {
                Debug.error("", ex);
                return sSrc;
            }
        } else {
            return sSrc;
        }
    }

    public String encrypt(String sSrc) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(this.key, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(1, skeySpec);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes("UTF-8"));
            return Hex.encodeHexStr(encrypted);
        } catch (Exception ex) {
            Debug.error("", ex);
            return null;
        }
    }

    public String decrypt(String sSrc) {
        if (sSrc != null && sSrc.length() != 0) {
            try {
                SecretKeySpec skeySpec = new SecretKeySpec(this.key, "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(2, skeySpec);
                byte[] encrypted1 = Hex.decodeHex(sSrc.toCharArray());
                byte[] original = cipher.doFinal(encrypted1);
                return new String(original, "UTF-8");
            } catch (Exception ex) {
                Debug.error("", ex);
                return sSrc;
            }
        } else {
            return sSrc;
        }
    }
}

硬编码密钥 metacrmloginpass ,加解密都是基于hex格式进行,即传入的数据为hex格式。

反序列化

再看下涉及反序列化的处理逻辑

import com.alibaba.fastjson.JSONObject;
......
String a = request.getServletPath();
String c = request.getRequestURI();
c = StringUtil.replaceStr(c, a + "/", "");
String[] ps = StringUtil.string2array(c, "/");
String url = ps[0];
AesEcbCipher aec = new AesEcbCipher();
String dec = aec.decrypt(url);
JSONObject json = JSONObject.parseObject(dec);

dec 最终来自AES解密后 url 后的结果,而 url又来自 request.getRequestURI() 替换掉 ServletPath+/ 后的值,如请求 /mrxn/xxxxxxx 得到的最终 url 值就是去掉 mrxn/ 后的 xxxxxxx ;经过上述处理后,直接调用 fastjson 的 parseObject 方法处理,导致fastjson 反序列化漏洞。

文件读取

String fileName = json.getString("fileName");
String folder = json.getString("folder");
String sCorpName = json.getString("sCorpName");
String securityCode = json.getString("securityCode");
UserState us = UserManager.getUserBySessionId(request.getSession().getId());
if (us == null && !FileSecurityCode.checkSecurityCode(securityCode)) {
    request.setAttribute("error", (new ResourceService()).getDispMessage("error.common.upanddown.login"));

    try {
        request.getRequestDispatcher("/common/images/default/limit.png").forward(request, response);
    } catch (ServletException e) {
        Debug.error("", e);
    }

} else {
    FileSecurityCode.visits(securityCode);
    response.setContentType(Path.getContentType(fileName));
    String path;
    if ("attach".equals(folder)) {
        path = Path.getAttachAbsoluteDirectory(sCorpName);
    } else if ("template".equals(folder)) {
        path = Path.getTemplateAbsoluteDirectory(sCorpName);
    } else if ("userlogo".equals(folder)) {
        path = Path.getLogoAbsoluteDirectory(sCorpName);
    } else if ("messageserv".equals(folder)) {
        path = Path.getMessageServerAbsoluteDirectory(sCorpName);
    } else if ("picture".equals(folder)) {
        path = Path.getPictureAbsoluteDirectory(sCorpName);
    } else if ("skinbackground".equals(folder)) {
        path = Path.getBackgroundAbsoluteDirectory(sCorpName);
    } else if ("icon".equals(folder)) {
        path = Path.getIconAbsoluteDirectory(sCorpName);
    } else {
        path = Path.getTempAbsoluteDirectory(sCorpName);
    }

    path = path + fileName;
    this.log.debug("path={}", path);
    FileInputStream fio = new FileInputStream(new File(path));
    IOUtils.copyLarge(fio, response.getOutputStream());
    IOUtils.closeQuietly(fio);

AES解密后的内容经fastjson处理后,取出其中需要的 fileName 、folder、sCorpName、securityCode等,主要是path = path + fileName; 其中 path 与 fileName 都是用户可控,且对路径无任何校验或过滤,拼接后直接调用 new File(path) 进行文件操作,造成任意文件读取漏洞。

整体执行流程如下图所示

漏洞复现

反序列化

GET /getFile/AES加密后的payload HTTP/1.1
Host: metasoft.mrxn.net
Cookie: JSESSIONID=D74D969B9489FFBE41D5F5ACE5CAC014

DNSLOG平台成功收到请求

文件读取

漏洞利用需要合法的cookie或者securityCode

而系统自带 druid ,因此可以访问 /druid/websession.html 拿到系统以及存在的 SESSIONID 后进行批量枚举尝试

GET /getFile/AES加密后的payload HTTP/1.1
Host: metasoft.mrxn.net
Cookie: JSESSIONID=2A6482AE7774FBC13DD7854E15CE90F4


成功读取到 web.xml 文件内容


手机扫码阅读

美特CRM fileUpAndDown 反序列化代码执行漏洞

Salia PLCC 镜像源码获取方式

评 论