关于近期发生的利用weblogic漏洞进行挖矿事件的漏洞简要分析

最早我们看到黑产利用该漏洞(CVE-2017-3506)进行批量挖矿,后续又发现利用该漏洞的绕过去批量扫描攻击。我们先来看一下这次使用漏洞的成因是什么,依据捕获到的攻击数据包可知利用的是一个web service的服务,为了分析方便,我直接使用简单的PoC。

漏洞编号:CVE-2017-3506

调试环境:win7 x64+Idea

jdk版本:java 1.8.0_131

WebLogic版本:10.3.6.0(无补丁)

调试PoC

POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: localhost:7001
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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
Cookie: wp-settings-time-1=1506773666
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: text/xml
Content-Length: 407

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">  
  <soapenv:Header> 
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">  
		<java version="1.8.0_131" class="java.beans.XMLDecoder">
			...
                        ...
                        ...
		</java>
    </work:WorkContext> 
  </soapenv:Header>  
  <soapenv:Body/> 
</soapenv:Envelope>	

 

漏洞分析

直接入口点是wls-wsat.war这个war提供的web service服务,然后通过WLSServletAdapter进行处理,在WorkContextServerTube类中进行处理POST数据包中的XML数据。

大体函数调用图,如下

这里我们分析数据解析的过程WorkContextServerTube类的processRequest函数。

public NextAction processRequest(Packet var1) {
        this.isUseOldFormat = false;
        if (var1.getMessage() != null) {
            HeaderList var2 = var1.getMessage().getHeaders();
            Header var3 = var2.get(WorkAreaConstants.WORK_AREA_HEADER, true);
            if (var3 != null) {
                this.readHeaderOld(var3);
                this.isUseOldFormat = true;
            }

            Header var4 = var2.get(this.JAX_WS_WORK_AREA_HEADER, true);
            if (var4 != null) {
                this.readHeader(var4);
            }
        }

        return super.processRequest(var1);
    }

var3这个对象获取了和</work:WorkContext>相关的内容。然后执行readHeaderOld这个函数。我们来跟进一下。

protected void readHeaderOld(Header var1) {
        try {
            XMLStreamReader var2 = var1.readHeader();
            var2.nextTag();
            var2.nextTag();
            XMLStreamReaderToXMLStreamWriter var3 = new XMLStreamReaderToXMLStreamWriter();
            ByteArrayOutputStream var4 = new ByteArrayOutputStream();
            XMLStreamWriter var5 = XMLStreamWriterFactory.create(var4);
            var3.bridge(var2, var5);
            var5.close();
            WorkContextXmlInputAdapter var6 = new WorkContextXmlInputAdapter(new ByteArrayInputStream(var4.toByteArray()));
            this.receive(var6);
        } catch (XMLStreamException var7) {
            throw new WebServiceException(var7);
        } catch (IOException var8) {
            throw new WebServiceException(var8);
        }
    }

这里var4存储了java对象XML序列化后的数据。之后执行receive函数,参照函数调用图,省略一些地方,我们看到WorkContextInput对象执行readUTF函数。

public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException {
        String var1 = var0.readUTF();
        return (WorkContextEntry)(var1.length() == 0 ? NULL_CONTEXT : new WorkContextEntryImpl(var1, var0));
    }

这里就看到我们在反序列化漏洞中最常见的readObject函数,触发xml的反序列化漏洞。

public String readUTF() throws IOException {
        return (String)this.xmlDecoder.readObject();
    }

 

验证截图

四月份发布的补丁简要说明

public WorkContextXmlInputAdapter(InputStream is)
  {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try
    {
      int next = 0;
      next = is.read();
      while (next != -1)
      {
        baos.write(next);
        next = is.read();
      }
    }
    catch (Exception e)
    {
      throw new IllegalStateException("Failed to get data from input stream", e);
    }
    validate(new ByteArrayInputStream(baos.toByteArray()));
    this.xmlDecoder = new XMLDecoder(new ByteArrayInputStream(baos.toByteArray()));
  }
  
  private void validate(InputStream is)
  {
    WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
    try
    {
      SAXParser parser = factory.newSAXParser();
      parser.parse(is, new DefaultHandler()
      {
        public void startElement(String uri, String localName, String qName, Attributes attributes)
          throws SAXException
        {
          if (qName.equalsIgnoreCase("object")) {
            throw new IllegalStateException("Invalid context type: object");
          }
        }
      });
    }
    catch (ParserConfigurationException e)
    {
      throw new IllegalStateException("Parser Exception", e);
    }
    catch (SAXException e)
    {
      throw new IllegalStateException("Parser Exception", e);
    }
    catch (IOException e)
    {
      throw new IllegalStateException("Parser Exception", e);
    }
  }

只是在反序列化之前增加了一个validate函数,如果qName等于object,就抛出异常终止。可谓简单暴力,然而,这里的黑名单这种修复,很难彻底修复完整。值得深思…

 

最后,为什么一定要打十月份补丁呢!

因为,Oracle官方四月份补丁对该漏洞修复不彻底,可以绕过补丁,依旧执行远程命令。目前绕过的漏洞在官方发布的十月份的补丁中已修复。

 

注意事项

1.可以触发漏洞的url为wls-wsat.war xml中所有url-pattern,并不是单一的一个url入口。

2.关于XmlDecoder安全性及利用请参看http://blog.diniscruz.com/2013/08/using-xmldecoder-to-execute-server-side.html

3.请大家尽快登录Oracle官方网站,下载WebLogic十月份补丁并安装。

(完)