XXE注入详解
XML介绍
XML全称可扩展标记语言(EXtensible Markup Language),XML跟HTML格式类似,但是作用不同,XML侧重于数据传输,HTML注重于标记语言,也就是说XML其实是一种数据传输的手段
XML格式
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!--xml文件的声明-->
<bookstore> <!--根元素-->
<book category="COOKING"> <!--bookstore的子元素,category为属性-->
<title>Everyday Italian</title> <!--book的子元素-->
<author>Giada De Laurentiis</author> <!--book的子元素-->
<year>2005</year> <!--book的子元素-->
<price>30.00</price> <!--book的子元素-->
</book> <!--book的结束-->
</bookstore> <!--bookstore的结束-->
- 所有 XML 元素都必须有关闭标签。
- XML 标签对大小写敏感。
- XML 必须正确地嵌套。
- XML 文档必须有根元素。
- XML 的属性值须加引号。
使用PHP代码来读取XML文件:
<?php
libxml_disable_entity_loader(false);
$xmlContent = file_get_contents("./xml.xml");
$dom = new DOMDocument();
$dom->loadXML($xmlContent, LIBXML_NONET| LIBXML_DTDLOAD);
$cred = simplexml_import_dom($dom);
var_dump($cred);
?>
# 运行结果
class SimpleXMLElement#2 (2) {
public $comment =>
array(2) {
[0] =>
class SimpleXMLElement#4 (0) {
}
[1] =>
class SimpleXMLElement#3 (0) {
}
}
public $book =>
class SimpleXMLElement#5 (6) {
public $@attributes =>
array(1) {
'category' =>
string(7) "COOKING"
}
public $comment =>
array(5) {
[0] =>
class SimpleXMLElement#10 (0) {
...
}
[1] =>
class SimpleXMLElement#9 (0) {
...
}
[2] =>
class SimpleXMLElement#8 (0) {
...
}
[3] =>
class SimpleXMLElement#7 (0) {
...
}
[4] =>
}
}
public $title =>
string(16) "Everyday Italian"
public $author =>
string(19) "Giada De Laurentiis"
public $year =>
string(4) "2005"
public $price =>
string(5) "30.00"
}
}
XML元素内容中不能有这些特殊字符:< > &
,但可以使用它们的实体编码字符:
< > & ' "
替换< > & ' "
DTD(document type definition)介绍
- DTD是XML的一部分,他用来给XML定义一种格式规范,比如自定义一种标签
- DTD用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。是XML文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。
- DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
内部DTD
这是我们在XML内部自己定义的DTD:
<?xml version="1.0"?>
<!DOCTYPE note [<!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,head,body,end)><!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->
<!ELEMENT end (#PCDATA)><!--定义end元素为”#PCDATA”类型-->
]>
<note>
<to>F12</to>
<from>is</from>
<head>very</head>
<body>Handsome</body>
<end><![CDATA[This is a CDATA section. It can contain any text data, including special characters like <, >, and &.]]></end>
</note>
这是一些DTD的格式说明:
PCDATA
PCDATA的意思是被解析的字符数据,是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开,被解析的字符数据不应当包含任何&,<,或者>字符,需要用它们的实体编码来替换
CDATA
CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开,使用方法是<![CDATA[内容]]>
<?php
libxml_disable_entity_loader(false);
$xmlContent = file_get_contents("./xml.xml");
$dom = new DOMDocument();
$dom->loadXML($xmlContent, LIBXML_NONET|LIBXML_DTDLOAD);
$cred = simplexml_import_dom($dom);
var_dump($cred);
?>
# 运行结果
class SimpleXMLElement#2 (5) {
public $to =>
string(3) "F12"
public $from =>
string(2) "is"
public $head =>
string(4) "very"
public $body =>
string(8) "Handsome"
public $end =>
class SimpleXMLElement#3 (0) {
}
}
可以看到CDATA中的内容没有被解析
外部DTD
引入外部DTD文件
<!DOCTYPE 根元素名称 SYSTEM "dtd路径">
<!DOCTYPE 根元素名称 PUBLIC "DTD名称" "DTD文档的URL"
(网络上的DTD文件)
示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root-element SYSTEM "test.dtd">
<note>
<to>F12</to>
<from>is</from>
<head>very</head>
<body>Handsome</body>
</note>
<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->
DTD属性
属性声明语法:
<!ATTLIST 元素名称 属性名称 属性类型 默认值>
DTD实例:
<!ATTLIST example suxin CDATA "F">
XML实例:
<example suxin="F" />
这个属性跟html标签中的属性是一个意思
属性类型选项:
DTD实体
- 实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
- 实体引用是对实体的引用。
- 实体可在内部或外部进行声明
可以将DTD实体理解为一个变量
一般实体
<!ENTITY 实体名称 "实体内容">
引用方法:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY abc "F12">
]>
<test>&abc;</test>
参数实体
<!ENTITY % 实体名称 "实体内容">
引用方法:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY %abc "F12">
%abc
]>
<test>test</test>
一般实体在代码块中引用通过&名称
进行引用,而参数实体是通过%名称
进行引用的,参数实体直接在DTD实体中就引用了
外部实体
外部实体,用来引入外部资源。有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机。
<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<author>&file;</author>
外部实体的引用是支持协议的,如http,file等,如果是php的话,也支持伪协议等等
内部实体
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY abc "F12"> #这个就是内部实体,其实和上面的一般实体一样
]>
<test>&abc;</test>
XML注入
XML注入的原理跟SQL注入的原理差不多,都是闭合以达到执行语句的效果,一个简单的例子
<?php
libxml_disable_entity_loader (false);
$input=$_POST[0];
$xmlfile = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<bookstore>
<book category='COOKING'>
<title>Everyday Italian</title>
<author>$input</author>
<year>2024</year>
<price>30.00</price>
</book>
</bookstore>";
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
var_dump($creds);
?>
这里我们可以控制$input的内容,post一个0=F12
如果我们这样传,0=F12</author><name>hacker_F12</name><author>
可以看到成功注入name标签
防御建议
对传入的数据做个转义过滤就行了
XXE攻击
外部实体注入
实际上就是利用DTD搭配协议来进行攻击
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
echo $creds;
?>
可以看到读到了F12 is very handsome
内部实体注入
主要是利用外部实体加参数实体,通过报错带出数据,一张图即可理解