代码审计涉及漏洞:
XML Entity Expansion Injection (XML实体扩展注入)
XML External Entity Injection (XML外部实体注入)
为什么是第一讲就是XXE漏洞,而不是SQL注入之类的?
因为我发现XXE相对来讲入门门槛是偏高的,网上的各类XXE教程对于大牛还好,但对于一些零开发基础的小伙伴来讲,着实不太友好,所以我希望通过从最基础的理论着手,为大家建立一个健全易懂的XXE漏洞复现,结合代码审计,更好的帮助大家更好的攻克这个点。
基本概念
在展开讲XXE之前,有些概念我们必须得了解些基础概念,因为本章重点为XXE,关于XML学习未提到的部分大家可以参照参考资料自我拓展。
XML
XML 指可扩展标记语言(eXtensible Markup Language)。
方便大家理解,这里与HTML对比着给大家说一下:HTML和XML 为不同的目的而设计,HTML 被设计用来显示数据,其焦点是数据的外观。XML 被设计用来传输和存储数据,其焦点是数据的内容。HTML 旨在显示信息,而 XML 旨在传输信息。
DTD
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。
DTD的声明:指XML文档中声明该文档的DTD或DTD来源的部分,可以包含在使用它的XML文档内部,也可以以独立的DTD文档(*.dtd)文档存在。
所以DTD一般认为有两种引用或声明方式:
1、内部DTD:即对XML文档中的元素、属性和实体的DTD的声明都在XML文档中。
2、外部DTD:即对XML文档中的元素、属性和实体的DTD的声明都在一个独立的DTD文件(.dtd)中。
(网上有提到的引用公共DTD其实也算外部引用DTD的一种)
XML基本文档结构
]>
&xxe;
XXE的复现
讲到JAVA语言出现的XXE,不得不提今年出现的spring中出现的XXE漏洞【CVE-2018-1259】,今天就拿它来开刀吧。
Spring Data Commons, versions 1.13 prior to 1.13.12 and 2.0 prior to 2.0.7, used in combination with XMLBeam 1.4.14 or earlier versions, contains a property binder vulnerability caused by improper restriction of XML external entity references as underlying library XMLBeam does not restrict external reference expansion. An unauthenticated remote malicious user can supply specially crafted request parameters against Spring Data's projection-based request payload binding to access arbitrary files on the system.
从官方的描述中我们可以看到,此漏洞主要出现在XMLbeam1.4.14之前的版本,同时要求Spring Data Commons 1.13至1.13.11以及2.0至2.0.6的版本,经笔者自行测试,目前idea默认选择的spring boot2.1.1版本漏洞已修复,我们可以看到默认的Spring Data Commons版本为2.1.0以上,猜测这个应该是跟着springboot版本号走的。
显然,这是因为spring boot 2.1.1默认禁用了DTD。
笔者不得不尝试降低spring boot版本,以便漏洞正常复现,然后就看到了如下一幕。哇,,就哭出来了,低版本下不到了。
因为开发已经荒废了好久,完全不知道springboot不同版本的各模块区别。最终,给大家提供了一个良好的解决方法,直接修改pom文件,借助maven自动解决依赖问题。
在这里说点经验之谈,我对不同版本进行了尝试,不知道是不是个人原因,我发现版本并没有按照官方提到的那样精确,比如这里如果spring-data-commons采用2.0.6版本, XMLBeam采用1.4.13版本,你会发现DTD依然是被禁止的。所以本次漏洞复现我选择了spring-data-commons2.0.6+ XMLBeam1.4.14的组合。
然后我们在springboot中简单的写个小功能,操作过程打算使用burpsuite复现,那就不写前端页面啦~核心代码如下,用来接收通过XML提交的两个数据:firstname/lastname
public interface UserPayload {
@XBRead("//firstname")
@JsonPath("$..firstname")
String getFirstname();
@XBRead("//lastname")
@JsonPath("$..lastname")
String getLastname();
}
springboot不愧为新一代的懒人框架,简单的调整下入口文件,小程序完美启动~
我们先来构造一个正常的XML文档~
rabbit 666
然后我们来构造一个引用特殊的payload,增加DTD,为了演示效果,我们在C盘下新建一个txt文件。
]>
&rabbit; 666
试着来访问下系统文件:
]>
&rabbit; 666
XXE的利用
这时候,我们不禁要问了,XXE漏洞究竟能用来做什么?
任意文件读取
这里,我们就不重复了,说到底,这是XXE最基本的使用方式,我们上面也一直体现了这一点。
SSRF
SSRF(Server-Side Request Forgery:服务器端请求伪造),说白了就是借助漏洞实现内网探测,我在80端口的网站下临时放了我们刚刚的测试文件,简单修改下payload:
]>
&rabbit; 666
当然啦,这里的127.0.0.1可以替换成任意你想要的内网地址,我们就可以借此实现对内网的探测。
DOS攻击
看到这里会不会虎躯一震,这丫怎么会?继续看
]>
&lol9;
此测试可以在内存中将小型 XML 文档扩展到超过 3GB 而使服务器崩溃。
亦或者,如果目标是UNIX系统,
]>
&xxe;
如果 XML 解析器尝试使用 /dev/random 文件中的内容来替代实体,则此示例会使服务器(使用 UNIX 系统)崩溃。
远程命令执行
这种情况很少见,并不是传统意义上的任意命令执行,只是因为环境的特殊配置,导致XML与某些命令操作关联,进而造成了命令执行。当PHP环境中的PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上,就会造成我们说的这种情况,在这里,我们不做展开。
修复建议:
对 XML 解析器进行安全配置,使它不允许将外部实体包含在传入的 XML 文档中。不管是上面语言,抑或是使用了市面是哪种主流XML解析方案,最终的解决方案都可以如此借鉴:
为了避免 XXE injections,应为 XML 代理、解析器或读取器设置下面的属性:
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
如果不需要 inline DOCTYPE 声明,可使用以下属性将其完全禁用,这种方式显然更直接,我们搭建环境中一直在吐槽DTD被禁用,就是这个意思,DOCTYPE被禁,也就禁掉了DTD的根本:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
说到这里你可能会突然提到一个问题,emmmm,那XMLbeam的XXE漏洞是如何修复的呢?
我给大家找到了XMLbeam1.4.14和1.4.15中的createDocumentBuilderFactory()函数方便大家做下对比:
#####1.4.14
public DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance();
if (!DefaultXMLFactoriesConfig.NamespacePhilosophy.AGNOSTIC.equals(this.namespacePhilosophy)) {
instance.setNamespaceAware(DefaultXMLFactoriesConfig.NamespacePhilosophy.HEDONISTIC.equals(this.namespacePhilosophy));
}
return instance;
}
#####1.4.15
public DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance();
instance.setXIncludeAware(this.isXIncludeAware);
instance.setExpandEntityReferences(this.isExpandEntityReferences);
if (!DefaultXMLFactoriesConfig.NamespacePhilosophy.AGNOSTIC.equals(this.namespacePhilosophy)) {
instance.setNamespaceAware(DefaultXMLFactoriesConfig.NamespacePhilosophy.HEDONISTIC.equals(this.namespacePhilosophy));
}
return instance;
}
当然,这里提供了部分封装,无妨,我们去找下官方提供的漏洞补丁,以方便理解,
下载地址:https://pan.baidu.com/s/161gWHgR6Dc7A-eSWe39ikg
然后我们在第106行处会看到如下配置,正是我们刚刚提到的解决方案。
private static final String NON_EXISTING_URL = "http://xmlbeam.org/nonexisting_namespace";
private static final String[] FEATURE_DEFAULTS = new String[] { "http://apache.org/xml/features/disallow-doctype-decl#true", //
"http://xml.org/sax/features/external-general-entities#false", //
"http://xml.org/sax/features/external-parameter-entities#false", //
"http://apache.org/xml/features/nonvalidating/load-external-dtd#false" };
写在后面:唉,花了一周的时间复现这个问题,QAQ彻底卸掉了eclipse入坑了idea,从零学起springboot,emmmm,此刻乐的像个200斤的胖子,开心,嘤~
参考资料:
https://github.com/spring-projects/spring-data-examples/tree/master/web
https://www.freebuf.com/column/157466.html
https://www.freebuf.com/column/156863.html
https://xz.aliyun.com/t/2341
https://blog.csdn.net/zl908760230/article/details/53911618
https://blog.spoock.com/2018/05/16/cve-2018-1259/
https://blog.csdn.net/zhangshengsky/article/details/72804940
https://www.freebuf.com/column/156863.html
http://www.runoob.com/xml/xml-tutorial.html
http://www.runoob.com/dtd/dtd-tutorial.html
http://central.maven.org/maven2/org/springframework/data/spring-data-commons/