CVE-2015-3269: Apache Flex BlazeDS XXE Vulnerabilty
This was originally posted on blogger here.
In a recent Product Security Review, Code White Researchers discovered a XXE vulnerability in Apache Flex BlazeDS/Adobe (see ASF Advisory). The vulnerable code can be found in the BlazeDS Remoting/AMF protocol implementation.
All versions before 4.7.1 are vulnerable. Software products providing BlazeDS Remoting destinations might be also affected by the vulnerability (e.g. Adobe LiveCycle Data Services, see APSB15-20).
Vulnerability Details
An AMF message has a header and a body. To parse the body, the method readBody()
of AmfMessageDeserializer
is called. In this method, the targetURI
, responseURI
and the length of the body are read. Afterwards, the method readObject()
is called which eventually calls the method readObject()
of an ActionMessageInput
instance (either Amf0Input
or Amf3Input
).
/* */ public void readBody(MessageBody body, int index)
/* */ throws ClassNotFoundException, IOException
/* */ {
/* 158 */ String targetURI = amfIn.readUTF();
/* 159 */ body.setTargetURI(targetURI);
/* 160 */ String responseURI = amfIn.readUTF();
/* 161 */ body.setResponseURI(responseURI);
/* */
/* 163 */ amfIn.readInt();
/* */
/* 165 */ amfIn.reset();
/* */
/* */
/* 168 */ if (isDebug) {
/* 169 */ debugTrace.startMessage(targetURI, responseURI, index);
/* */ }
/* */ Object data;
/* */ try {
/* 173 */ data = readObject();
/* */ }
/* */ catch (RecoverableSerializationException ex)
/* */ {
/* 177 */ ex.setCode("Client.Message.Encoding");
/* 178 */ data = ex;
/* */ }
/* */ catch (MessageException ex)
/* */ {
/* 182 */ ex.setCode("Client.Message.Encoding");
/* 183 */ throw ex;
/* */ }
/* */
/* 186 */ body.setData(data);
/* */
/* 188 */ if (isDebug) {
/* 189 */ debugTrace.endMessage();
/* */ }
/* */ }
/* */
/* */
/* */
/* */
/* */ public Object readObject()
/* */ throws ClassNotFoundException, IOException
/* */ {
/* 199 */ return amfIn.readObject();
/* */ }
/* */ }
In case of an Amf0Input
instance, the type of the object is read from the next byte. If type has the value 15, the following bytes of the body are parsed in method readXml()
as a UTF string.
/* */ public Object readObject()
/* */ throws ClassNotFoundException, IOException
/* */ {
/* 91 */ int type = in.readByte();
/* */
/* 93 */ Object value = readObjectValue(type);
/* 94 */ return value;
/* */ }
/* */
/* */ protected Object readObjectValue(int type) throws ClassNotFoundException, IOException
/* */ {
/* 99 */ Object value = null;
/* 100 */ switch (type)
/* */ {
/* */ case 0:
/* 103 */ value = Double.valueOf(readDouble());
/* 104 */ break;
/* */
...
/* */
/* */ case 15:
/* 147 */ value = readXml();
/* 148 */ break;
/* */
....
/* */ protected Object readXml() throws IOException
/* */ {
/* 511 */ String xml = readLongUTF();
/* */
/* 513 */ if (isDebug) {
/* 514 */ trace.write(xml);
/* */ }
/* 516 */ return stringToDocument(xml);
/* */ }
/* */
The xml string gets passed to method stringToDocument
of class XMLUtil
where the Document
is created using the DocumentBuilder
.
/* */
/* */ public static Document stringToDocument(String xml, boolean nameSpaceAware)
/* */ {
/* 116 */ ClassUtil.validateCreation(Document.class);
/* */
/* 118 */ Document document = null;
/* */ try
/* */ {
/* 121 */ if (xml != null)
/* */ {
/* 123 */ StringReader reader = new StringReader(xml);
/* 124 */ InputSource input = new InputSource(reader);
/* 125 */ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
/* 126 */ factory.setNamespaceAware(nameSpaceAware);
/* 127 */ factory.setValidating(false);
/* 128 */ DocumentBuilder builder = factory.newDocumentBuilder();
/* */
/* 130 */ document = builder.parse(input);
/* */ }
/* */ }
/* */ catch (Exception ex)
/* */ {
/* 135 */ throw new MessageException("Error deserializing XML type " + ex.getMessage());
/* */ }
/* */
/* 138 */ return document;
/* */ }
/* */ }
When a DocumentBuilder
is created through the DocumentBuilderFactory
, external entities are allowed by default. The developer needs to configure the parser to prevent XXE.
Exploitation
Exploitation is easy, just send the XXE vector of your choice.