XXE漏洞
XML
XML全称“可扩展标记语言”(extensible markup language),XML是一种用于存储和传输数据的语言。与HTML一样,XML使用标签和数据的树状结构。但不同的是,XML不使用预定义标记,因此可以为标记指定描述数据的名称。由于json的出现,xml的受欢迎程度大大下降。
XML文档结构
包括:XML声明+DTD文档类型定义+文档元素
,例如:
其中
DTD
DTD(document type definition)文档类型定义用于定义XML文档的结构,它作为xml文件的一部分位于XML声明和文档元素之间,比如:
|
|
它就定义了 XML 的根元素必须是message,根元素下面有一些子元素,所以 XML必须像下面这么写:
|
|
DTD需要在!DOCTYPE
注释中定义根元素,而后在中括号的[]
内使用!ELEMENT注释定义各元素特征。
实体
如:
|
|
它规定了xml文件的根元素是foo,但ANY说明接受任何元素。重点是!ENTITY
,这就是我们要提到的实体,实体本质是定义了一个变量,变量名xxe,值为“test”,后面在 XML 中通过 & 符号进行引用,所以根据DTD我们写出下面的xml文件:
|
|
因为ANY的属性,元素我们可以随意命令,但user值通过&xxe,实际值为test。
外部实体
上面的例子就是内部实体。
XML外部实体是一种自定义实体,定义位于声明它们的DTD之外,声明使用SYSTEM
关键字,比如加载实体值的URL:
|
|
这里URL可以使用file://协议,因此可以从文件加载外部实体。例如:
|
|
XXE简介
XML外部实体注入(XXE),当应用程序在解析XML输入时,在没有禁止外埠实体的加载而导致加载了外部文件及代码时,就会造成XXE漏洞。
XXE漏洞可通过file协议或ftp协议来读取文件源码,也可以对内网进行探测或攻击。
危害
根据有无回显可分为有回显XXE和Blind XXE
危害:
- 任意文件读取
- 内网探测:如
<!ENTITY xxe SYSTEM "http://127.0.0.1:8080" >探测端口;
- 攻击内网站点
- 命令执行:
<!ENTITY xxe SYSTEM "expect://id" >执行命令;
- DOS攻击
- Bind XXE攻击课用于试试Oob(数据带外攻击)
…
挖掘思路
关注可能解析xml格式数据的功能处,较容易发现的是请求包参数包含XML格式数据,不容易发现的是文件上传及数据解析功能处,通过改请求方式、请求头Content-Type等方式进行挖掘。
三步:
- 检测XML是否会被成功解析以及是否支持DTD引用外部实体,有回显或者报错;
- 若没有回显则可以使用Blind XXE漏洞来构建一条带外信道提取数据;
- 最后可以尝试XInclude,某些应用程序接收客户端提交的数据,将其嵌入到服务器端的XML文档中,然后解析文档,尝试payload:
|
|
执行系统命令
|
|
探测内网端口
|
|
读取本地文件
|
|
OOB XXE
1.在我们VPS创建一个test.dtd
文件:
|
|
2.开始web服务,以便后续步骤可以访问到这个test.dtd
文件,比如SimpleHTTPServer
3.burp抓包修改数据包, 下面数据包一旦经受害者服务器处理,就会向我们的vps发送请求,查找我们的DTD文件
|
|
结果我们vps收到了flag.txt的文件内容,而SimpleHTTPServer也收到了对test.dtd的GET请求
代码审计
XML的常见接口
解析XML常见的方法有四种,即:DOM、DOM4J、JDOM 和SAX。
Java中常用以下常见接口来解析XML语言:
1.XMLReader
当XMLReader使用默认的解析方法且未对xml进行过滤时,会出现xxe漏洞
2.SAXBuilder
当SAXBuilder使用默认的解析方法且未对xml进行过滤时,会出现xxe漏洞
3.SAxReader
4.AXParserFactory
5.Digester
其触发的xxe漏洞是没有回显的,我们一般需要通过Blind XXE的方法来利用
6.DocumentBuiderFactory
挖掘技巧
挖掘XXE漏洞的关键是找到:
代码是否涉及xml解析——>xml输入是否是外部可控——>是否禁用外部实体(DTD)
若三个条件满足则存在漏洞。
审计关键字
和其他漏洞一样,只不过审计时候搜索的关键字不同,搜索常见关键字以快速对代码进行定位:
|
|
例子
这里以xxe-lab为简单例子说明
搜索关键字发现使用了DocumentBuilderFactory.newInstance()
进入代码查看xml是否可控
可以发现:程序首先通过DocumentBuilderFactory.newInstance()
来获取一个实例,然后通过DocumentBuilder::parse
来解析,通过request.getInputStream()
来传入的body内容,并返回一个Document
实例。
继续跟进,发现直接通过getValueByTagName
函数获取了username节点的内容并在进行对比数据后将其输出到页面上。也就是说可以通过控制username的值来达到回显xxe的目的。
构造payload:
|
|
漏洞防御
- 使用XML解析器时需要设置其属性,禁止使用外部实体
|
|
- 手动黑名单过滤
- 许多第三方组件会爆出xxe漏洞,应避免引入这些组件