ManageEngine OPManager中的预认证RCE漏洞

admin 2021年12月14日10:14:11评论209 views字数 6183阅读20分36秒阅读模式
ManageEngine OPManager中的预认证RCE漏洞点击上方蓝字关注我们


漏洞概述


ManageEngine OpManager是一款基于Java的网络监控解决方案,可监控网络设备,如路由器、网络摄像头、服务器、防火墙等,并允许主动管理网络,执行网络配置和网络流量分析。

研究人员发现了一个反序列化漏洞,该漏洞允许未经身份验证的攻击者以root或管理员权限执行任意系统命令。受影响的不单只是ManageEngine OpManager,还包括其他基于OpManager的产品,如ManageEngine NetFlow Analyzer。


漏洞细节


该漏洞存在于SUMCommunicationServlet中。智能更新管理器 (SUM) 与Servlet之间的通信可以通过 /servlets/com.adventnet.tools.sum.transport.SUMCommunicationServlet端点调用,无需事先进行身份验证检查。以下清单显示了POST请求的servlet入口点。

public class SUMCommunicationServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession httpSession = request.getSession(); response.setStatus(200); DataOutputStream dos = new DataOutputStream(response.getOutputStream()); if (httpSession == null) { dos.writeInt(1000); } else { SUMHttpRequestHandler requestHandler = (SUMHttpRequestHandler)httpSession.getAttribute("requestHandler"); if (requestHandler == null) { dos.writeInt(1000); } else { byte[] responseData = requestHandler.process(request.getInputStream()); [...] } } dos.flush(); }}

如果请求包含会话,应用程序会尝试从HttpSession属性中获取SUMHttpRequestHandler的实例(第10行)。如果成功,执行将到达第14行,其中POST请求的正文由SUMHttpRequestHandler处理。

我们可以先将包含序列化整数1002的POST请求发送到SUMHandshakeServlet,从而将requestHandler添加到会话中:

public class SUMHandShakeServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ObjectInputStream ois = new ObjectInputStream(request.getInputStream()); int requestId = ois.readInt(); HttpSession httpSession; if (requestId == 1002) { httpSession = request.getSession(true); SUMHttpRequestHandler reqHandler = new SUMHttpRequestHandler(request.getRemoteHost()); httpSession.setAttribute("requestHandler", reqHandler); [...] } }}

SUMHttpRequestHandler.process函数会将有效载荷InputStream转换为DataInputStream,从该流中读取有效载荷长度,最后从有效载荷创建一个字节数组。在该转换过程之后,将调用以有效载荷字节数组作为参数的processSumPDU函数。

  public byte[] process(InputStream is) {        BufferedInputStream bis = new BufferedInputStream(is);        ByteArrayOutputStream bos = new ByteArrayOutputStream();        byte[] dataa = new byte[4096];        int lengthx;         while((lengthx = bis.read(dataa, 0, dataa.length)) > 0) {             bos.write(dataa, 0, lengthx);         }
DataInputStream ois = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); int length = ois.readInt(); byte[] data = new byte[length]; ois.readFully(data, 0, length); byte[] processedData = this.processSumPDU(data); [...]

在该函数中,有效载荷被进一步传递到第8行中的SUMPDU.deserializePDU方法。

private byte[] processSumPDU(byte[] pduData) throws Exception {        if (pduData == null) {            return null;        } else if (pduData == SUMPDU.CLOSE_SESSION) {            this.cleanUp();            return null;        } else {            SUMPDU sumpdu = SUMPDU.deSerializePDU(pduData);

最后,使用Java内置类ObjectInputStream对有效载荷进行反序列化。这意味着我们可以反序列化任意对象,当相应的小工具链可用时,这可能导致严重漏洞。

 public static SUMPDU deSerializePDU(byte[] b) throws Exception {        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(b));        return (SUMPDU)ois.readObject();    }

工具链


为了进一步实现RCE,我们需要一个允许执行Java代码或系统命令的小工具链。我们可以在OPManager的代码库和包含的库中查找自定义链,或查找公开可用的小工具。在本例中,OpManager使用commons-beanutils-1.9.3.jar作为依赖项。对于这个库,ysoserial中存在一个众所周知的RCE链———CommonsBeanutils1。该链需要commons-beanutils:1.9.3commons-collections:3.1commons-logging:1.2,但OpManager的类路径中不存在commons collections库,所以将失败。

我们可以稍微修改CommonsBeanutils1链,使其不再需要commons-collections。通过这个链,我们就可以执行任意的Java字节码。该链基于以下三个小工具。

PriorityQueue工具

java.util.PriorityQueue是一个内置的Java类,它实现了可以由自定义比较器排序的优先级队列。它实现了Serializable接口的自定义反序列化功能。readObject自定义函数将在反序列化期间被调用,并且是我们的入口点。

  1. 攻击者控制的ObjectInputStream中的对象数组被写入属性队列。

  2. 在第8行调用heapify函数,以通过自定义比较器对数组进行排序。

  3. 然后,在第13行调用shiftDown

  4. 我们将提供一个自定义比较器siftDownUsingComparator,并在第19行调用。

  5. 最后,自定义比较器的compare函数在第31行被调用。


private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {        var1.defaultReadObject();        var1.readInt();        this.queue = new Object[this.size];        for(int var2 = 0; var2 < this.size; ++var2) {            this.queue[var2] = var1.readObject();        }        this.heapify();    }
private void heapify() { for(int var1 = (this.size >>> 1) - 1; var1 >= 0; --var1) { this.siftDown(var1, this.queue[var1]); } }
private void siftDown(int var1, E var2) { if (this.comparator != null) { this.siftDownUsingComparator(var1, var2); } else { this.siftDownComparable(var1, var2); } }
private void siftDownUsingComparator(int var1, E var2) { int var4; for(int var3 = this.size >>> 1; var1 < var3; var1 = var4) { var4 = (var1 << 1) + 1; Object var5 = this.queue[var4]; int var6 = var4 + 1; if (var6 < this.size && this.comparator.compare(var5, this.queue[var6]) > 0) { var4 = var6; var5 = this.queue[var6]; } } }

BeanComparator 工具

BeanComparator允许根据提供的属性比较两个对象。这意味着在两个对象上都调用属性的getter函数,并比较结果。特别是,调用对象属性的任意getter函数使这个小工具功能强大。如前所述,公开的RCE链需要库commons-collections:3.1。但是,可以将重载构造函数与本机Java 比较器(如 java.util.Collections.ReverseComparator)一起使用。

import org.apache.commons.collections.comparators.ComparableComparator;
public class BeanComparator implements Comparator, Serializable { private String property; private Comparator comparator;
public BeanComparator(String property, Comparator comparator) { this.setProperty(property); if (comparator != null) { this.comparator = comparator; } else { this.comparator = ComparableComparator.getInstance(); } }
public int compare(Object o1, Object o2) { if (this.property == null) { return this.comparator.compare(o1, o2); } else { Object value1 = PropertyUtils.getProperty(o1, this.property); Object value2 = PropertyUtils.getProperty(o2, this.property); return this.comparator.compare(value1, value2); } } }

TemplatesImpl小工具

通过调用任意对象的任意getter函数的能力,我们可以继续构建链以获取RCE。Xalan TemplatesImpl工具提供了一种罕见的功能,可通过提供的Java字节码定义和初始化类。通过前面的小工具调用公共getter函数getOutputProperties,可以触发恶意类构造函数的执行。在第6行getTransletInstance被调用,最后在第13行调用了恶意类属性的构造函数。

public synchronized Properties getOutputProperties() {            return this.newTransformer().getOutputProperties();    }
public synchronized Transformer newTransformer() throws TransformerConfigurationException { TransformerImpl var1 = new TransformerImpl(this.getTransletInstance(), this._outputProperties, this._indentNumber, this._tfactory); [...] } private Translet getTransletInstance() throws TransformerConfigurationException { if (this._class == null) { this.defineTransletClasses(); } AbstractTranslet var1 = (AbstractTranslet)this._class[this._transletIndex].newInstance(); var1.postInitialization(); var1.setTemplates(this); var1.setServicesMechnism(this._useServicesMechanism); var1.setAllowedProtocols(this._accessExternalStylesheet); if (this._auxClasses != null) { var1.setAuxiliaryClasses(this._auxClasses); }
return var1; }

默认情况下,OpManager以root/管理员权限运行。因此,该漏洞利用Linux上会弹出一个root shell,在Windows上会弹出一个管理员 shell。

ManageEngine OPManager中的预认证RCE漏洞

END



ManageEngine OPManager中的预认证RCE漏洞


好文!必须在看

本文始发于微信公众号(SecTr安全团队):ManageEngine OPManager中的预认证RCE漏洞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月14日10:14:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ManageEngine OPManager中的预认证RCE漏洞https://cn-sec.com/archives/438993.html

发表评论

匿名网友 填写信息