蓝队技巧之如何限制XXE解析

蓝队技巧之如何限制XXE解析

在我的上一篇文章中,我们看到了如何配置 XML 解析器以全局禁用 XXE 声明和扩展。这是一个简单但严格的解决方案,可能难以在您的项目中实施。所以今天,我就来谈谈如何精确限制XXE解析。同样,我们的代码示例将主要使用 Java,并引用其他语言。

限制外部连接到授权协议

Java JAXP API 1.5 版引入了新属性来限制对外部内容的访问。在这篇文章中,我们将只关注限制 XXE 以及ACCESS_EXTERNAL_DTD属性。但是,请记住,限制对其他外部资源(如模式)的访问对于降低其他 XML 漏洞的风险也是必要的。

必须使用要使用的授权协议列表定义此属性:

factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "http");

在此示例中,只允许连接到 http 资源,从而防止使用文件 URI 方案通过 XXE 泄露敏感文件,但仍然不足以避免漏洞,因为可以从通过网络访问的资源中检索敏感内容。我们稍后会看到,此功能通常充当第一个过滤器,并与实体解析器结合使用。

通过将此功能设置为空字符串,可以安全地完全禁用 XXE:

factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");

注意:我们已经看到一些 XML 处理器实现,例如 Apache Xerces,它们仍然不支持这些 JAXP 1.5 属性。

蓝队技巧之如何限制XXE解析

对于 libxml 库,限制外部连接的最接近设置是LIBXML_NONET功能,该功能在检索外部资源时禁止使用网络。这是 PHP 中的一个示例:

$doc = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NONET);

使用自定义解析器解析实体

SAX(XML 的简单 API,最初是仅 Java 的 API)解析器基于事件驱动的 API。EntityResolver 接口实现的resolveEntity回调针对在XML 文档中找到的每个实体引用进行调用。默认情况下,内置 Java 解析器将尝试访问 XML 文档中定义的几乎所有外部内容。

因此,为了保护 SAX 应用程序,应该禁用 XXE 声明或引用扩展,正如我们在本系列的第二篇文章中所见,或者应该根据应用程序的需要使用自定义解析器。 

使用自定义解析器的常见用例是:

  • 使用资源的缓存版本,而不是总是从网络中获取它。
  • 将 URI 资源的方案(如 http://)替换为更合适的方案(https://)。
  • 将相对 URI 资源转换为绝对资源。
  • 当然,只授权安全且经过验证的实体。

自定义解析器由一个EntityResolver接口实现组成,该接口实现应该使用SAX 处理器 的setEntityResolver方法进行注册。

例如,我们在开源项目中看到很多防止 XXE 漏洞的有效解决方案是为任何实体关联一个空字符串作为内容,以完全禁用实体解析。与我们之前讨论的解决方案的不同之处在于,这里允许在 XML 文件中使用 XXE 声明,但它们的解析被禁用,从而提供了一种非阻塞且安全的方式来解析 XML 文件:

builder.setEntityResolver(new EntityResolver() {
   @Override
   public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
     return new InputSource(new StringReader(""));
   }
 });

请注意,将null注册为实体解析器等同于使用默认且不安全的解析器,因此可能没有充分的理由这样做:

builder.setEntityResolver(null);

在自定义解析器中,返回null时,解析器的默认行为是获取外部内容:

builder.setEntityResolver(new EntityResolver() {
   @Override
   public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
     return null;
   }
 });

因此,像上面这样的宽松实体解析器仍然可能导致 XXE 漏洞。这就是我们之前讨论的属性的用武之地。下面,entityResolver对systemId以logo.png结尾的实体使用自定义解析,否则使用默认行为。由于通过将  ACCESS_EXTERNAL_DTD属性设置为空字符串已在第一行修改了默认行为,因此实体解析器是安全的。

builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
builder.setEntityResolver(new EntityResolver() {
  @Override
  public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    if (systemId.endsWith("logo.png")) {           
      InputStream in = classLoader.getResourceAsStream("com/package/logo.png");
      return new InputSource(new StringReader(Base64.getEncoder().encodeToString(IOUtils.toByteArray(in))));
    }
 
    return null;
  }
});

其他语言中的其他库提供了类似的功能以允许自定义解析外部实体,这是 PHP 的libxmllibxml_set_external_entity_loader函数的情况:

libxml_set_external_entity_loader(
   function ($public, $system, $context) {
       if(str_ends_with($system, "logo.png")) {
           return fopen("./logo.png", "r");
       }
 
       return null;
   }
);

与 libxml 的主要区别在于,在自定义解析器中返回null不会解析实体并会触发错误。

使用目录解析实体

将实体映射到其他实体通常也使用XML Catalog完成。
XML 目录是一个 XML 文件,其中包含外部实体标识符到 URI 的映射。

<?xml version="1.0"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
 <system systemId="https://www.externalwebsite.com/logos/logo.png" uri="logo.png"/>
</catalog>

XML 处理器对解析 XML 文件时找到的每个实体在目录中执行查找。如果没有匹配,则抛出异常,提供一种安全的方式来防止 XXE 漏洞。

在 java 中,可以通过调用setEntityResolver方法来使用 Catalog,如下所示:

URL catalogUrl = classLoader.getResource(catalogFile);
CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogUrl.toURI());
 
builder.setEntityResolver(cr);

有关 XML 漏洞的更多信息

尽管 XXE 是解析 XML 文件时需要注意的最危险的漏洞,但可能会出现其他问题,例如拒绝服务攻击或使用xincludeXSLT 文件 I/O元素获取外部内容。我们将很快发布一组规则来进一步加强您的 XML 解析器。敬请关注!

from

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注